/* ==========================================================================
   Socle commun (public + organisateur)

   Palette par variables, redéfinies par le mode nuit et par chaque thème
   arcade plus bas — tout composant construit sur ces variables se
   réhabille seul. Refonte 2026-09 (mockups validés avec l'organisateur) :
   un seul langage visuel pour l'écran public et l'admin — mêmes titres,
   mêmes boutons (principal / secondaire / discret / danger), mêmes
   statuts, icônes SVG plutôt qu'emoji (voir `ICON_PATHS`, public.js).
   ========================================================================== */
:root {
  /* Lisibilité (2026-09) : fond et bordures d'origine (#f6f5f1/#e2e0d9)
     trop proches du blanc des cartes — 1,09:1 carte/fond, 1,3:1
     bordure/carte, cartes et bracket fondus dans le fond. Cibles appliquées
     à chaque palette (défaut, nuit, thèmes arcade) : carte/fond ≥1,2:1,
     bordure/carte ≥1,5:1, `--line` ≥2:1 sur le fond, `--border-strong`
     ≥3:1 sur les cartes (contour d'un champ ou d'un bouton, seuil WCAG
     des composants d'interface). */
  --bg: #ebe9e1;
  --surface: #ffffff;
  --border: #d1cfc8;
  /* Traits de liaison du bracket — plus marqués que `--border` : posés
     directement sur le fond, pas sur une carte. */
  --line: #a3a097;
  /* Contour des champs de saisie et boutons secondaires. */
  --border-strong: #8f8b7f;
  --text: #1c211e;
  --muted: #5b6259;
  --accent: #1a6b4a;
  --accent-weak: #e1efe7;
  --warn: #8f4507;
  /* Vert distinct de --accent (teinte plus "citron vert") pour "en cours" :
     garde une distinction visuelle claire avec le vert de --accent utilisé
     pour les vainqueurs/1ers, tout en étant vert plutôt que jaune. Assez
     sombre pour rester ≥4,5:1 sur --live-weak (badge "En cours") comme sur
     --surface. */
  --live: #3f6212;
  --live-weak: #ebf7d3;
  /* Couleur de texte des boutons/liens qui utilisent `--accent` en fond
     (bouton principal, `.btn-cta`, carte "Vous suivez") — blanc par
     défaut, à surcharger uniquement par les thèmes dont `--accent` est
     trop clair pour porter du texte blanc (voir plus bas, ex. Phosphor,
     Ty Korrigans). Centralise ce qui était avant une règle par thème du
     type `button:not(.secondary):not(...) { color: ... }` : fragile, elle
     s'appliquait par erreur à tout futur bouton avec son propre fond
     (repéré 3 fois de suite avant ce remplacement par variable). */
  --on-accent: #fff;
  /* Texte sur un aplat `--live` (en-tête plein d'un match en cours dans
     le bracket) : blanc sur les palettes claires, la couleur du fond sur
     les palettes sombres, où `--live` est une couleur vive. */
  --on-live: #fff;
  /* Rouge des actions destructrices (supprimer, forfait) — distinct de
     --warn (plus ambré, pour un signal plus discret : flèche de classement
     en baisse, avertissements). */
  --danger: #b42318;
  --radius: 10px;
  --radius-lg: 16px;
  --font-body: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-display: "Bricolage Grotesque", "Instrument Sans", -apple-system, "Segoe UI", sans-serif;
}

/* Teintes dérivées, calculées sur `body` (pas `:root`) : un thème arcade
   redéfinit --surface/--text/--warn sur `body`, et une variable dérivée
   posée sur `:root` resterait figée sur les couleurs par défaut. */
body {
  --surface-2: color-mix(in srgb, var(--text) 8%, var(--surface));
  --warn-weak: color-mix(in srgb, var(--warn) 14%, var(--surface));
}

* { box-sizing: border-box; }

/* `hidden` doit toujours gagner : sans ça, tout élément à qui une règle
   donne un `display` explicite (boutons en `inline-flex`, `.match-row`,
   `.btn-cta`, menus…) restait visible malgré l'attribut — piège rencontré
   plusieurs fois séparément avant cette règle unique. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.45;
}
/* Polices du thème par défaut seulement (chargées par les pages, voir le
   `<link>` Google Fonts de chaque HTML) — un thème arcade impose les
   siennes sur `body[data-arcade]`, jamais concurrencées ici puisque ce
   sélecteur l'exclut. */
body:not([data-arcade]) { font-family: var(--font-body); }
body:not([data-arcade]) :is(header.top h1, .display, body.public-page h2, .page-title, section > h2, .stat-value) {
  font-family: var(--font-display);
}
.num, .display { font-variant-numeric: tabular-nums; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.meta { color: var(--muted); font-size: 0.85rem; }
.muted { color: var(--muted); }

.icon { display: inline-flex; flex-shrink: 0; line-height: 0; }
.icon svg { display: block; }

/* ---------- En-tête ---------- */
header.top {
  position: relative;
  padding: 1rem 1.25rem 0.85rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

/* Petite marque d'appli au-dessus du h1 (nom d'événement) — jamais le
   titre principal lui-même, pour ne pas rivaliser avec ce que
   l'utilisateur est venu voir. Hérite de la police du thème actif :
   reste lisible et discrète quel que soit l'habillage. */
header.top .brand { color: var(--muted); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 0.2rem; }
header.top h1 { margin: 0; font-size: 1.4rem; line-height: 1.15; }
header.top .sub { color: var(--muted); font-size: 0.9rem; margin-top: 0.15rem; }
/* Laisse la place au médaillon (logo du thème, `header.top::after` plus
   bas) : la ligne de navigation passe sous le titre, à la même hauteur que
   lui sur un écran étroit. */
header.top nav { margin-top: 0.7rem; display: flex; align-items: center; gap: 0.5rem; max-width: calc(100% - 132px); }
header.top nav .nav-links { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }

/* Navigation en pastilles : la page courante en plein (`aria-current`),
   les autres en simple texte — même grammaire côté public et organisateur. */
.nav-pill {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: 40px; padding: 0 0.95rem; border-radius: 999px;
  background: transparent; color: var(--text); border: 1px solid transparent;
  font: inherit; font-weight: 600; font-size: 0.95rem; text-decoration: none; cursor: pointer;
}
.nav-pill:hover { background: var(--surface-2); }
.nav-pill[aria-current="page"], .nav-pill.active-page { background: var(--text); color: var(--bg); }
.nav-follow { border-color: var(--border); }
.nav-follow.active { border-color: var(--accent); background: var(--accent-weak); color: var(--accent); }
/* Accès organisateur : même pastille à contour que "Suivre mon équipe",
   poussée à droite du bandeau pour la séparer des liens destinés au public
   (`margin-left: auto` dans `.nav-links`, qui prend toute la ligne). Reste
   dans `.nav-links` : le repli du nav (écran TV) la masque avec le reste. */
header.top nav .nav-links { flex: 1; min-width: 0; }
.nav-orga { margin-left: auto; border-color: var(--border); }

/* Pastille "N matchs en direct" — mène à l'écran des terrains. */
.live-pill {
  display: inline-flex; align-items: center; gap: 0.45rem;
  min-height: 36px; padding: 0 0.85rem; border-radius: 999px;
  background: var(--live-weak); color: var(--live);
  font-weight: 700; font-size: 0.88rem; text-decoration: none;
}
.live-pill[hidden] { display: none; }
.live-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--live); flex-shrink: 0;
  animation: pulse-live 1.8s ease-in-out infinite;
}

/* Seul appel à l'action de tout le nav public (les équipes s'inscrivent
   elles-mêmes) — sorti du lot des liens plutôt que traité à égalité avec
   "Organisateurs", qui n'intéresse pas le même public. */
.btn-cta {
  display: inline-flex; align-items: center; min-height: 40px;
  background: var(--accent); color: var(--on-accent); font-weight: 700;
  font-size: 0.92rem; padding: 0 1rem; border-radius: 999px; text-decoration: none;
}
.btn-cta:hover { opacity: 0.88; }
/* `.btn-cta` pose son propre `display`, à la même spécificité que la
   règle navigateur `[hidden] { display: none; }` — sans ce sélecteur plus
   spécifique pour la contrer, poser `hidden` sur "Inscrire une équipe"
   (`toggleRegisterLink`, public.js, piloté par le réglage
   `registration_open`) n'aurait aucun effet visuel malgré l'attribut bien
   présent. Même piège déjà rencontré sur `.match-row` et `.forfeit-menu`
   (voir plus bas). */
.btn-cta[hidden] { display: none; }

/* Liens du nav repliables — pensé pour un affichage TV pendant
   l'événement : une fois le nécessaire consulté, ces liens ne servent plus
   à rien sur un écran non piloté et ne font qu'occuper de la place.
   `.nav-links` regroupe les vrais liens pour pouvoir les masquer d'un bloc
   sans toucher au bouton lui-même (voir `setupNavToggle`, app.js —
   préférence retenue par navigateur via `localStorage`, comme le thème
   jour/nuit). */
.nav-toggle {
  background: transparent; border: none; color: var(--muted); cursor: pointer;
  padding: 0.3rem; margin-left: auto; border-radius: 999px; line-height: 1; min-height: 0;
  /* En bas de la ligne de navigation : quand les liens passent sur
     plusieurs lignes (téléphone), il reste à côté du dernier bouton
     ("Organisateurs") au lieu de flotter au milieu. */
  align-self: flex-end; margin-bottom: 0.55rem;
}
.nav-toggle:hover { color: var(--accent); }
.nav-toggle-icon {
  display: inline-block; transform: rotate(90deg); transition: transform 0.15s ease;
  font-size: 1rem;
}
header.top nav.nav-collapsed .nav-links { display: none; }
header.top nav.nav-collapsed .nav-toggle-icon { transform: rotate(-90deg); }
header.top:has(nav.nav-collapsed) { padding-bottom: 0.6rem; }
header.top:has(nav.nav-collapsed) nav { margin-top: 0.35rem; }

/* Logo de l'app en médaillon dans l'en-tête, à droite du titre — même
   traitement que le blason de Ty Korrigans (voir plus bas dans ce
   fichier), pour le thème par défaut cette fois : `:not([data-arcade])`
   plutôt qu'un `[data-arcade="..."]` précis, puisque c'est justement
   l'absence de thème arcade (voir `applyTheme()`, public.js, qui ne pose
   jamais `data-arcade` pour le thème par défaut). `max-width` sur le seul
   h1 : laisse la marque inchangée, seul le titre a besoin de laisser la
   place, exactement comme chez Ty Korrigans. Deux variantes jour/nuit
   (`data-theme`) — mêmes images que `CUSTOM_CLUB_CRESTS.default`
   (admin.js), qui les réutilise pour l'affiche QR et les diplômes. */
body.public-page:not([data-arcade]) header.top h1,
body:not([data-arcade]) header.top h1 {
  max-width: calc(100% - 132px);
}
body.public-page:not([data-arcade]) header.top::after,
body:not([data-arcade]) header.top::after {
  content: ""; position: absolute; top: 0.5rem; right: 1rem;
  width: 108px; height: 108px;
  background: url("/img/default-crest-light.png") no-repeat center / contain;
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.35));
}
body.public-page:not([data-arcade])[data-theme="dark"] header.top::after,
body:not([data-arcade])[data-theme="dark"] header.top::after {
  background-image: url("/img/default-crest-dark.png");
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
}

/* Sélecteur d'équipe suivie ("Suivre mon équipe", public.js) — posé dans
   `<body>` (pas dans l'en-tête, que certains thèmes coupent en
   `overflow: hidden`) et positionné sous le bouton par le script. */
.follow-picker {
  position: absolute; z-index: 60; width: min(320px, calc(100vw - 2rem));
  background: var(--surface); color: var(--text); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: 1rem; display: grid; gap: 0.7rem;
}
.follow-picker-title { font-weight: 700; }
.follow-picker-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }

/* ---------- Mise en page ---------- */

/* 860px à l'origine : beaucoup trop étroit sur un écran d'organisateur
   (ordinateur, souvent large) — la plupart des blocs de contenu
   s'adaptent déjà tout seuls à la largeur disponible, ce cap ne servait
   donc qu'à les brider. Élargi plutôt que retiré : les formulaires
   (`form.stacked`, 420px) gardent leur propre largeur resserrée pour
   rester lisibles. */
main { max-width: 1320px; margin: 0 auto; padding: 1.25rem 1rem 2rem; }
main.narrow { max-width: 640px; }

section { margin-bottom: 1.75rem; }
section h2 { font-size: 1.15rem; margin: 0 0 0.6rem; }

/* En-tête de section : titre à gauche, info ou lien à droite. */
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.section-head h2 { margin: 0; }
.section-link { font-size: 0.9rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.section-link:hover { text-decoration: underline; }

/* Carte de contenu générique (panneau) — même rayon/bordure partout. */
.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.1rem 1.25rem;
}

/* Fait sortir une section de la largeur max de `main` (pensée pour du
   texte/tableaux, pas pour un diagramme aussi large que le bracket) —
   technique "breakout" classique. Utilisé par l'admin (bracket cliquable).
   `.bracket` garde son `overflow-x: auto` par sécurité pour les très
   grands brackets qui dépasseraient quand même. */
.bracket-section-wide {
  max-width: none; width: 100vw;
  position: relative; left: 50%; right: 50%;
  margin-left: -50vw; margin-right: -50vw;
  padding-left: 3rem; padding-right: 1.5rem; box-sizing: border-box;
}

details { margin-bottom: 1.75rem; }
details summary {
  font-size: 1.1rem; font-weight: 600; cursor: pointer; color: var(--text);
}
details[open] summary { margin-bottom: 0.6rem; }

/* En-tête de page (activité, terrains, inscription) : icône, fil
   d'Ariane + titre, chips, progression. */
.page-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin: 0.25rem 0 1.25rem; }
.page-head:empty { display: none; }
.page-head-title { flex: 1; min-width: 200px; }
.breadcrumb { font-size: 0.88rem; color: var(--muted); }
.breadcrumb a { color: var(--accent); font-weight: 600; text-decoration: none; }
.page-title, body.public-page h2.page-title {
  margin: 0.1rem 0 0; font-size: 2rem; line-height: 1.1; font-weight: 800;
  color: var(--text); text-transform: none; letter-spacing: 0;
}
.page-head .sport-badge { width: 3.5rem; height: 3.5rem; border-radius: 14px; }
.page-head-progress { width: min(240px, 100%); display: grid; gap: 0.35rem; }
.phase-label { font-weight: 700; font-size: 0.9rem; }
.phase-label.en_cours { color: var(--live); }
.phase-label.terminee { color: var(--accent); }
.chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.chip {
  padding: 0.3rem 0.75rem; border-radius: 999px; background: var(--surface);
  border: 1px solid var(--border); font-size: 0.85rem;
}

/* Pastille carrée teintée autour d'une icône de sport. */
.sport-badge {
  width: 2.5rem; height: 2.5rem; border-radius: 10px; flex-shrink: 0;
  background: var(--accent-weak); color: var(--accent);
  display: inline-flex; align-items: center; justify-content: center;
}

/* Ombre douce partagée par les cartes — un peu de profondeur plutôt
   qu'un simple contour plat. Neutralisée en thème sombre (voir plus bas)
   où une ombre noire sur un fond déjà sombre ne fait que salir le rendu. */
.card, .panel, .match-row, .bracket-match, .live-card, .activity-card, .list-card, .pool-card, .court-col, .match-group, .pilot-court {
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 4px 12px rgba(20, 20, 15, 0.06);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.1rem;
  margin-bottom: 0.6rem;
}

.card a { color: inherit; text-decoration: none; display: block; }
.card h3 { margin: 0 0 0.6rem; font-size: 1rem; }

/* Champ label+contrôle empilé (onglet Réglages) : un `<label>` est en ligne
   par défaut, insuffisant pour l'empiler proprement au-dessus d'un
   `<select>` pleine largeur. Largeur plafonnée : un menu à quelques options
   ("Auto/Jour/Nuit") n'a pas besoin de courir sur toute la largeur d'une
   carte. */
.field { margin-bottom: 0.75rem; }
.field:last-child { margin-bottom: 0; }
.field label { display: block; margin-bottom: 0.3rem; }
.field select { max-width: 280px; }

/* Utilitaire générique (pas seulement dans une .card) : sans ça, un
   élément portant à la fois "card" et "row" ne matchait jamais cette règle
   (sélecteur descendant .card .row, pas .card.row) et le bouton restait
   collé au contenu au lieu d'aller à droite. */
.row { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }

table { width: 100%; border-collapse: collapse; background: var(--surface); border-radius: var(--radius); overflow: hidden; }
th, td { text-align: left; padding: 0.55rem 0.6rem; border-bottom: 1px solid var(--border); font-size: 0.92rem; }
th { color: var(--muted); font-weight: 700; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; }
tr:last-child td { border-bottom: none; }
td.num, th.num { text-align: center; font-variant-numeric: tabular-nums; }

/* ---------- Statuts ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.75rem; font-weight: 700;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--accent-weak);
  color: var(--accent);
  white-space: nowrap;
}
/* Couleurs pilotées par variables plutôt que codées en dur : sans ça, ce
   badge reste un carré gris clair figé même sur fond sombre (mode nuit
   comme thèmes arcade). */
.badge.a_venir { background: var(--surface-2); color: var(--muted); }
.badge.en_cours { background: var(--live-weak); color: var(--live); }
.badge.terminee { background: var(--accent-weak); color: var(--accent); }

/* Onglet Accueil (admin) : mini-cartes de compteurs, barre de progression
   par activité, checklist de prise en main. `align-items: stretch` pour des
   tuiles de même hauteur sur une ligne. */
.stat-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.75rem; }
.stat-tile { margin-bottom: 0; }
.stat-tile .stat-value { font-size: 1.8rem; font-weight: 800; line-height: 1.1; }
.stat-tile .stat-label { color: var(--muted); font-size: 0.85rem; margin-top: 0.2rem; font-weight: 500; }
/* Couleur de valeur pour une tuile-statut (ex. inscriptions ouvertes/
   fermées) plutôt qu'un nombre — même gabarit, juste la valeur teintée. */
.stat-value.is-live { color: var(--live); }
.stat-value.is-muted { color: var(--muted); }
/* Une tuile peut être un vrai <button> (cliquable, voir `statTile` dans
   admin.js) — neutralise les styles de bouton par défaut du navigateur
   (fond plein, texte blanc centré) pour qu'elle reste identique à la
   version <div> non cliquable. */
button.stat-tile { display: block; width: 100%; color: var(--text); text-align: left; background: var(--surface); border: 1px solid var(--border); font-weight: 400; }
button.stat-tile:hover { border-color: var(--accent); }

.progress-bar { background: var(--border); border-radius: 999px; height: 6px; overflow: hidden; margin-top: 0.35rem; }
.progress-bar-fill { background: var(--accent); height: 100%; border-radius: 999px; }

.checklist-item {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.4rem 0; font-size: 0.92rem;
}
.checklist-mark { color: var(--muted); font-size: 1rem; width: 1.1rem; text-align: center; flex-shrink: 0; }
.checklist-item.done .checklist-mark { color: var(--accent); }
.checklist-item.done .checklist-label { color: var(--muted); text-decoration: line-through; }
.checklist-label { flex: 1; }

.badge-btn {
  display: inline-flex; align-items: center; font-size: 0.75rem; padding: 0.25rem 0.65rem; border-radius: 999px;
  border: none; cursor: pointer; font-weight: 700; white-space: nowrap; min-height: 0;
}
.badge-btn.a_venir { background: var(--surface-2); color: var(--muted); }
.badge-btn.en_cours { background: var(--live-weak); color: var(--live); }
.badge-timer {
  margin-left: 0.35rem; font-variant-numeric: tabular-nums; font-weight: 700; opacity: 0.85;
}

.group-label {
  font-size: 0.75rem; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: 0.08em;
  margin: 0.9rem 0 0.4rem;
}
.group-label:first-of-type { margin-top: 0.4rem; }

.matches-list { display: flex; flex-direction: column; gap: 0.4rem; }

/* Ligne de match de l'écran de saisie admin (`scoreEntryRow`). */
.match-row {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.55rem 0.75rem; font-size: 0.95rem; gap: 0.5rem; flex-wrap: wrap;
}
/* `display: flex` ci-dessus a la même spécificité que la règle navigateur
   `[hidden] { display: none; }` — sans ce sélecteur plus spécifique pour la
   contrer, poser/retirer `hidden` (recherche par équipe) n'aurait aucun
   effet visuel malgré l'attribut bien présent (même piège déjà rencontré
   sur `.forfeit-menu`). */
.match-row[hidden] { display: none; }
/* Prochain match réellement jouable sur un terrain libre — ni le terrain
   ni aucune des deux équipes déjà engagés ailleurs (voir
   `nextMatchByLocation`, admin.js). */
.match-row.next-match { border-color: var(--accent); border-width: 2px; }
.match-row .next-match-tag { color: var(--accent); font-weight: 700; }
.match-row .teams { flex: 1; min-width: 140px; }
.match-row .score { font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
.match-row .tag { font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.match-row .score-side { display: flex; flex-direction: column; align-items: flex-end; gap: 0.15rem; }

/* Sépare les deux noms d'équipe d'une ligne de match (`teamsVs`,
   public.js, point d'entrée partagé par tous les écrans) : un petit
   "contre" discret, lisible même quand les deux noms sont longs. */
.vs-pill {
  display: inline-block; color: var(--muted);
  font-size: 0.75rem; font-weight: 600; margin: 0 0.45rem; vertical-align: 1px;
}
/* Équipe suivie ("Suivre mon équipe", public.js) : même couleur que le
   vainqueur, repérable d'un coup d'œil dans n'importe quelle liste. */
.followed { color: var(--accent); font-weight: 700; }
.is-followed { background: var(--accent-weak); }

/* Rafraîchissement silencieux (5s) sur l'écran public : sans indice
   visuel, une mise à jour de score passe inaperçue. */
@keyframes flash-update { 0% { background-color: var(--live-weak); } 100% { background-color: transparent; } }
.match-row.flash, .bracket-match.flash, .result-row.flash, .compact-match.flash { animation: flash-update 2s ease-out; }

/* ---------- Bracket ----------
   `.bracket-tree` a une largeur/hauteur explicite posée en inline-style par
   public.js (calculée à partir du nombre d'équipes), qui contient les
   lignes SVG et les boîtes en `position: absolute` — voir
   `bracketRoundCenter` dans public.js pour la formule de centrage. */
.bracket { display: flex; align-items: flex-start; gap: 2.5rem; overflow-x: auto; padding-bottom: 0.5rem; }
.bracket-tree { position: relative; flex: none; }
.bracket-lines { position: absolute; top: 0; left: 0; overflow: visible; }
.bracket-line { stroke: var(--line); stroke-width: 2; }
.bracket-col-header {
  position: absolute; top: 0; font-size: 0.75rem; font-weight: 700; color: var(--text);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.bracket-third-place { flex: none; padding-top: 30px; }
.bracket-third-place h3 {
  font-size: 0.75rem; font-weight: 700; color: var(--text); margin: 0 0 0.5rem;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.bracket-third-place .bracket-match { display: flex; flex-direction: column; }

/* Une case = une ligne d'en-tête (terrain/statut, chrono) + deux lignes
   d'équipe avec leur score. Hauteur fixe (BRACKET_MATCH_H, public.js) :
   c'est ce qui permet de positionner les connecteurs par calcul. */
.bracket-match {
  position: relative;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0; font-size: 0.9rem;
}
.bracket-match-abs { position: absolute; display: flex; flex-direction: column; }
.bracket-match-meta {
  flex: 0 0 22px; display: flex; align-items: center; gap: 0.35rem;
  padding: 0 0.65rem; font-size: 0.72rem; line-height: 1;
  color: var(--muted); background: var(--surface-2); font-weight: 600;
  border-bottom: 1px solid var(--border);
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0; overflow: hidden;
}
.bracket-match-location { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.bracket-match-timer { white-space: nowrap; pointer-events: none; font-weight: 700; }
.bracket-match .slot {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0 0 0 0.65rem; overflow: hidden;
}
/* Sans score (match pas encore joué), le nom garde sa marge à droite. */
.bracket-match .slot:not(:has(.slot-score)) { padding-right: 0.65rem; }
.bracket-match .slot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-flex; align-items: center; gap: 0.3rem; min-width: 0; }
/* Score dans sa propre colonne teintée, sur toute la hauteur de la
   ligne : se lit d'un coup d'œil sans chercher le chiffre au bout du nom. */
.bracket-match .slot-score {
  font-weight: 800; font-size: 1rem; align-self: stretch; flex: 0 0 2.25rem;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border-left: 1px solid var(--border);
}
.bracket-match .slot.winner { font-weight: 700; }
.bracket-match .slot.winner .slot-score { color: var(--accent); background: var(--accent-weak); }
.bracket-match .slot.loser { color: var(--muted); }
.bracket-match .slot.tbd { color: var(--muted); font-style: italic; }
.bracket-match .slot.followed .slot-name { color: var(--accent); font-weight: 700; }
.bracket-match .slot-trophy { color: var(--accent); }
.bracket-divider { height: 1px; background: var(--border); flex: none; }
/* Match en cours : bordure et en-tête couleur "live", même repère que le
   badge "En cours" et les cartes "En direct". */
.bracket-match.live { border-color: var(--live); border-width: 2px; box-shadow: 0 0 0 3px var(--live-weak); }
/* En-tête plein : le match en cours se repère de loin (écran TV). */
.bracket-match.live .bracket-match-meta {
  background: var(--live); color: var(--on-live); font-weight: 700; border-bottom-color: var(--live);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.bracket-match.live .live-dot { background: var(--on-live); }
/* Case dont les deux équipes restent à déterminer : recule visuellement
   (pointillés, pas de fond) pour ne pas concurrencer les matchs réels ou
   déjà tranchés avec la même intensité visuelle. */
.bracket-match.tbd-both { background: transparent; border: 1.5px dashed var(--line); box-shadow: none; }
.bracket-match.tbd-both .bracket-match-meta { background: transparent; border-bottom-color: transparent; }
.bracket-match.tbd-both .bracket-divider { background: transparent; }

/* Chemin du vainqueur du tournoi une fois la finale jouée (voir
   `championId`/`playedIn`, public.js — pas affiché tant que le tournoi
   n'est pas terminé). Même couleur que les vainqueurs partout ailleurs. */
.bracket-match.champion-path { border-color: var(--accent); border-width: 2px; }
.bracket-match.champion-path .slot.winner { background: var(--accent-weak); color: var(--accent); }
.bracket-line.champion-path { stroke: var(--accent); stroke-width: 3; }

/* Bracket cliquable (admin : saisie ; public : détail en lecture seule) :
   la case s'indique interactive au survol, et le panneau s'ancre en
   dessous d'elle (`.bracket-match` a déjà `position: relative`). */
.bracket-match.clickable { cursor: pointer; }
.bracket-match.clickable:hover { border-color: var(--accent); }
.bracket-popover {
  position: absolute; top: calc(100% + 0.5rem); left: 0; z-index: 20;
  width: 400px; max-width: 90vw;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2); padding: 1.1rem;
  cursor: default; /* la case parente porte le curseur pointer, pas le panneau */
}
/* `scoreEntryRow` produit un `.match-row` pensé pour une ligne de tableau
   pleine largeur — dans un panneau ça déborderait, donc on retire son
   propre cadre (déjà porté par `.bracket-popover`) et on empile ses
   groupes verticalement à la place. */
.bracket-popover .match-row {
  border: none; padding: 0; background: transparent; box-shadow: none;
  flex-direction: column; align-items: stretch; gap: 0.8rem;
  font-size: 1.05rem;
}
.bracket-popover .teams { font-weight: 600; }
.bracket-popover .score-form input[type=number] {
  width: 4.5rem; font-size: 1.2rem; font-weight: 700; text-align: center;
}

/* ---------- Formulaires et boutons ---------- */
form.stacked { display: flex; flex-direction: column; gap: 0.5rem; max-width: 420px; }
form.stacked.panel { max-width: none; }
label { font-size: 0.85rem; color: var(--muted); }
input, select, button, textarea {
  font: inherit; padding: 0.5rem 0.7rem; border-radius: var(--radius); border: 1px solid var(--border);
}
/* Un bouton n'hérite pas de la casse/de l'espacement du texte autour
   (règles navigateur) — nécessaire pour les thèmes qui les changent
   partout (Phosphor, tout en capitales). */
button { text-transform: inherit; letter-spacing: inherit; }
input, select, textarea { width: 100%; background: var(--surface); color: var(--text); min-height: 40px; border-color: var(--border-strong); }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
textarea { resize: vertical; }
input[type="checkbox"] { width: auto; min-height: 0; accent-color: var(--accent); }
.checkbox-label { display: flex; align-items: center; gap: 0.4rem; }
/* Interrupteur (Réglages) : une vraie case à cocher (`role="switch"`,
   `switchInput`, admin.js), seulement habillée ici. */
input[type="checkbox"].switch {
  appearance: none; -webkit-appearance: none; position: relative; flex-shrink: 0;
  width: 44px; height: 26px; min-height: 0; margin: 0; padding: 0; border: 0; border-radius: 999px;
  background: var(--border-strong); cursor: pointer; transition: background 0.15s ease;
}
input[type="checkbox"].switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); transition: transform 0.15s ease;
}
input[type="checkbox"].switch:checked { background: var(--accent); }
input[type="checkbox"].switch:checked::after { transform: translateX(18px); }
input[type="checkbox"].switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Quatre niveaux de bouton, partout (public et admin) :
   - principal (par défaut) : une seule action principale par zone ;
   - `.secondary` : actions courantes, neutre ;
   - `.ghost` : action discrète, texte seul ;
   - `.danger` : destructrice (supprimer), idéalement dans un menu ou
     derrière une confirmation plutôt qu'à côté des actions courantes. */
button {
  background: var(--accent); color: var(--on-accent); border: 1px solid transparent; cursor: pointer; font-weight: 700;
  min-height: 40px; display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
}
button:hover:not(:disabled) { filter: brightness(1.05); }
button.secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); font-weight: 600; }
button.secondary:hover:not(:disabled) { border-color: var(--text); filter: none; }
button.ghost { background: transparent; color: var(--accent); border-color: transparent; font-weight: 600; }
button.ghost:hover:not(:disabled) { background: var(--accent-weak); filter: none; }
button.danger { background: var(--surface); color: var(--danger); border-color: var(--danger); }
button.danger:hover:not(:disabled) { background: var(--danger); color: #fff; filter: none; }
button.icon-btn { width: 40px; padding: 0; }
button:disabled { opacity: 0.5; cursor: not-allowed; }

.score-form { display: flex; align-items: center; gap: 0.4rem; }
.score-form input[type=number] { width: 3.4rem; text-align: center; font-weight: 700; }

/* Forfait (écran de saisie des scores, poules comme bracket — voir
   `scoreEntryRow`) : action rare, donc discrète (bouton secondaire) — le
   choix de l'équipe n'apparaît qu'au clic, dans un petit menu ancré au
   bouton, où l'action elle-même prend la couleur "danger". */
.forfeit-wrap { position: relative; display: inline-block; }
.forfeit-btn {
  background: var(--surface); color: var(--muted); border: 1px solid var(--border-strong);
  font-size: 0.8rem; font-weight: 600; padding: 0.3rem 0.7rem; min-height: 36px; white-space: nowrap;
}
.forfeit-btn:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); filter: none; }
.forfeit-menu {
  position: absolute; top: calc(100% + 0.3rem); right: 0; z-index: 20;
  min-width: 13rem; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: 0.3rem; display: flex; flex-direction: column; gap: 0.15rem;
}
/* `display: flex` ci-dessus a la même spécificité que la règle navigateur
   `[hidden] { display: none; }` — sans ce sélecteur plus spécifique pour la
   contrer, le menu restait visible en permanence malgré l'attribut `hidden`
   posé/retiré par admin.js. */
.forfeit-menu[hidden] { display: none; }
.forfeit-menu button {
  background: transparent; color: var(--text); border: none; font-weight: 500;
  justify-content: flex-start; text-align: left; padding: 0.4rem 0.6rem; border-radius: calc(var(--radius) - 2px);
}
.forfeit-menu button:hover, .forfeit-menu button:focus-visible { background: var(--danger); color: #fff; filter: none; }

/* Équipe déclarée forfait (globalement, voir `PATCH /api/teams/:id/forfeit`) :
   surlignée partout où sa carte apparaît (onglet Équipes, inscriptions d'une
   activité) — un simple contour plus marqué plutôt qu'un fond teinté. */
.card.forfeit { border-color: var(--danger); border-width: 2px; }
.forfeit-badge {
  display: inline-block; background: var(--danger); color: #fff; font-size: 0.7rem;
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 0.1rem 0.45rem; border-radius: 999px; margin-left: 0.4rem;
}

/* Onglets (admin, et onglets d'une activité) : style "soulignement" —
   porté à l'origine depuis tybuvette/app (.tab-btn) pour une ressemblance
   visuelle entre les deux interfaces admin. */
.tabs { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-bottom: 1.25rem; border-bottom: 1px solid var(--border); }
.tabs button {
  background: transparent;
  color: var(--muted);
  border: none;
  border-bottom: 3px solid transparent;
  border-radius: 0;
  padding: 0.6rem 0.1rem;
  font-weight: 700;
  min-height: 44px;
}
.tabs button:hover { color: var(--text); filter: none; }
.tabs button.active { background: transparent; color: var(--text); border-bottom-color: var(--accent); }

/* Onglets "segmentés" (pastilles dans un rail) : filtre de la liste de
   matchs d'une activité. */
.segmented {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 2px; padding: 4px; border-radius: 12px;
  background: color-mix(in srgb, var(--border) 55%, var(--bg)); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08);
}
.segmented button {
  background: transparent; color: var(--text); border: 0; min-height: 38px;
  font-weight: 600; font-size: 0.88rem; border-radius: 9px; padding: 0 0.3rem;
}
.segmented button:hover { filter: none; background: color-mix(in srgb, var(--surface) 60%, transparent); }
.segmented button.active { background: var(--surface); font-weight: 700; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }

.member-row { display: flex; gap: 0.4rem; }
.member-row input { flex: 1; }
.member-list { display: grid; gap: 0.4rem; }

.msg { font-size: 0.9rem; padding: 0.6rem 0.8rem; border-radius: var(--radius); margin-bottom: 0.75rem; }
.msg.error { background: #fdecea; color: #b3261e; }
.msg.ok { background: var(--accent-weak); color: var(--accent); }
@keyframes msg-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes msg-out { to { opacity: 0; transform: translateY(-6px); } }
.msg-enter { animation: msg-in 0.25s ease-out; }
.msg-leave { animation: msg-out 0.35s ease-in forwards; }

.rank-1 { font-weight: 700; color: var(--accent); }

/* ---------- Mode jour/nuit — organisateur ET public ----------
   Un seul jeu de règles pour les deux interfaces : `data-theme` (posé par
   `applyTheme()`, public.js — appelée aussi bien par admin.js au
   chargement de l'admin que par les pages publiques) reflète partout le
   même réglage `theme_override`. Ce bloc-ci ne concerne que le thème par
   défaut (sans thème arcade) — Ty Korrigans a sa propre variante jour plus
   bas ; les autres thèmes arcade n'ont pas de variante jour du tout. */
body[data-theme="dark"] {
  --bg: #0b0e0c;
  --surface: #1d2420;
  --border: #38423c;
  --line: #4a5550;
  --border-strong: #6b756f;
  --on-live: #0b0e0c;
  --text: #eef1ec;
  --muted: #a1a9a0;
  --accent: #5cc995;
  --accent-weak: #15301f;
  --warn: #f0a44b;
  --live: #b5e655;
  --live-weak: #223010;
  --danger: #f58b82;
  --on-accent: #08170f;
}

/* Ombre courte et dense plutôt qu'aucune : sans elle, seule la bordure
   détachait les cartes d'un fond sombre. Aussi pour les thèmes arcade
   sombres, qui ne posent pas `data-theme="dark"`. */
body[data-theme="dark"] :is(.card, .panel, .match-row, .bracket-match, .live-card, .activity-card, .list-card, .pool-card, .court-col, .match-group, .pilot-court),
body[data-arcade]:is([data-arcade="phosphor"], [data-arcade="oche"], [data-arcade="maitre"], [data-arcade="tykorrigans"]:not([data-theme="light"])) :is(.card, .panel, .match-row, .bracket-match, .live-card, .activity-card, .list-card, .pool-card, .court-col, .match-group, .pilot-court) {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
/* .msg.error a des couleurs codées en dur (pas de var --error-* existante)
   : sans ce correctif, la boîte resterait rose clair sur fond sombre. */
body[data-theme="dark"] .msg.error { background: #3a1a1a; color: #ff8a80; }

/* ==========================================================================
   Pages publiques (écran TV, téléphone)
   ========================================================================== */

body.public-page header.top h1 { font-size: 1.75rem; }
/* Plus large que l'admin (1320px) : ces écrans sont aussi pensés pour un
   affichage TV/projecteur pendant l'événement (voir le diaporama,
   `slideshow_enabled`). Sans effet sur mobile. */
body.public-page main { max-width: 1500px; }

/* Hiérarchie des titres : un seul style de titre de section, en gras
   lisible de loin, et un titre "principal" plus grand pour le classement
   général — avant la refonte, certains titres étaient en grosses lettres
   et d'autres en petites majuscules grises, comme deux pages collées. */
body.public-page h2 {
  font-size: 1.35rem; margin: 0 0 0.75rem; color: var(--text); font-weight: 700; line-height: 1.2;
}
body.public-page h2.section-title-primary { font-size: 1.75rem; font-weight: 800; }

body.public-page table { background: transparent; }
body.public-page td, body.public-page th { padding: 0.6rem 0.55rem; }
body.public-page td { font-size: 1rem; }
body.public-page .rank-1 { font-weight: 800; }

/* Tableau de bord de l'accueil (#leaderboard + colonne "en direct/à
   suivre/activités/résultats" à droite) : empilé en une seule colonne par
   défaut (mobile — le classement garde la priorité, toujours en premier),
   deux colonnes à partir de 900px pour exploiter la largeur d'un écran
   TV/tablette. Seule mise en page à deux colonnes de l'accueil — d'où un
   point de rupture dédié plutôt qu'une grille `auto-fit` : les deux
   colonnes n'ont pas la même forme (classement fixe vs empilement de
   sections), pas un jeu de cartes identiques. */
.home-dashboard { display: flex; flex-direction: column; gap: 1.75rem; }
.home-dashboard .home-side { display: flex; flex-direction: column; gap: 1.75rem; min-width: 0; }
.home-side > section, .home-side section { margin-bottom: 0; }
.leaderboard-panel { margin-bottom: 0; }
@media (min-width: 900px) {
  .home-dashboard { flex-direction: row; align-items: flex-start; }
  .home-dashboard .leaderboard-panel { flex: 0 0 min(580px, 46%); position: sticky; top: 1rem; }
  .home-dashboard .home-side { flex: 1; }

  /* Beaucoup de poules (ou un classement général à beaucoup d'équipes)
     dépasse largement la hauteur de l'écran — sans limite, le panneau
     pousserait toute la page, ce qui ne veut rien dire sur un écran TV non
     piloté (personne ne scroll). `stepLeaderboardScroll` (public.js) fait
     défiler le surplus par paliers plutôt que de le couper. */
  #leaderboard { max-height: 72vh; overflow: hidden; }
}

/* Bandeau d'annonce configurable (Réglages admin, "Message défilant") —
   vide par défaut, entièrement caché tant qu'il n'y a rien à montrer
   (`renderAnnouncement`, public.js). Info calme (icône + texte) plutôt que
   l'ancien bandeau orange défilant, qui se lisait comme une alerte. Ne
   défile que si le texte dépasse la largeur (`.scrolling`). */
.announcement-bar {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.7rem 1rem; margin-bottom: 1.25rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  font-size: 1.05rem;
}
.announcement-bar[hidden] { display: none; }
.announcement-icon { color: var(--accent); display: inline-flex; flex-shrink: 0; }
.announcement-viewport { overflow: hidden; flex: 1; min-width: 0; }
.announcement-track {
  display: inline-block; white-space: nowrap; font-weight: 600;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: middle;
}
/* Défilement horizontal continu, seulement si le texte dépasse ET que
   l'utilisateur n'a pas demandé de réduire les animations.
   `padding-left: 100%` place le texte juste hors du bord droit avant même
   la première frame — technique standard du "marquee" CSS. */
@media (prefers-reduced-motion: no-preference) {
  .announcement-bar.scrolling .announcement-track {
    overflow: visible; text-overflow: clip; max-width: none;
    padding-left: 100%;
    animation-name: announcement-scroll;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
  }
}
@keyframes announcement-scroll {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-100%, 0, 0); }
}

/* ---------- Carte "Vous suivez" (Suivre mon équipe) ---------- */
.follow-card {
  background: var(--accent); color: var(--on-accent);
  border-radius: var(--radius-lg); padding: 1rem 1.2rem; margin-bottom: 1.25rem;
  display: grid; gap: 0.55rem;
}
.follow-card[hidden] { display: none; }
.follow-card-head { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; font-weight: 600; opacity: 0.9; }
.follow-card-main { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
.follow-card-name { font-size: 1.5rem; font-weight: 800; line-height: 1.15; }
.follow-card-rank { font-weight: 700; }
.follow-now {
  background: color-mix(in srgb, var(--on-accent) 14%, transparent);
  border-radius: 12px; padding: 0.7rem 0.9rem; display: grid; gap: 0.15rem;
}
.follow-now-tag { display: flex; align-items: center; gap: 0.45rem; font-size: 0.75rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.follow-now .live-dot { background: currentColor; }
.follow-now-where { font-weight: 600; }
.follow-next-label { font-weight: 700; margin-right: 0.4rem; }

/* ---------- Podium + classement général ---------- */
.podium {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem;
  align-items: end; margin: 0.25rem 0 1.5rem;
}
.podium-step { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; text-align: center; min-width: 0; }
.podium-step.gold { order: 2; }
.podium-step.silver { order: 1; }
.podium-step.bronze { order: 3; }
.podium-name { font-weight: 700; font-size: 1.02rem; line-height: 1.2; overflow-wrap: anywhere; }
.podium-step.gold .podium-name { font-size: 1.3rem; font-weight: 800; }
body:not([data-arcade]) .podium-step.gold .podium-name { font-family: var(--font-display); }
.podium-points { color: var(--muted); font-weight: 600; font-size: 0.95rem; }
.podium-step.gold .podium-points { color: var(--accent); font-weight: 700; font-size: 1.1rem; }
.podium-trophy { color: var(--accent); }
.podium-block {
  width: 100%; height: 92px; margin-top: 0.3rem;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border-radius: 12px 12px 4px 4px;
}
.podium-step.gold .podium-block { height: 132px; background: var(--accent-weak); }
.podium-step.bronze .podium-block { height: 68px; }
.podium-step.is-followed { background: transparent; }
.podium-step.is-followed .podium-block { box-shadow: inset 0 0 0 2px var(--accent); }
.podium-rank {
  width: 2.8rem; height: 2.8rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.3rem;
}
.podium-step.gold .podium-rank { width: 3.6rem; height: 3.6rem; font-size: 1.7rem; }
/* Couleurs de médaille — lues aussi par les diplômes (`podiumRankColors`,
   admin.js) : chaque thème les redéfinit sous ces mêmes sélecteurs. */
.podium-step.gold .podium-rank { background: #d4af37; color: #3d2f00; }
.podium-step.silver .podium-rank { background: #b8bcc2; color: #2b2f33; }
.podium-step.bronze .podium-rank { background: #c98a4b; color: #35200f; }

/* Pastille de rang dans les listes (classement, classement final) :
   couleurs de médaille du podium pour les trois premiers — réutilise les
   mêmes classes (`rankChip`, public.js), remises à plat ici. */
.podium-step.rank-chip-wrap { display: inline-flex; order: 0; gap: 0; }
.podium-step.rank-chip-wrap .podium-rank.rank-chip,
.rank-chip {
  width: 1.9rem; height: 1.9rem; border-radius: 50%; font-size: 0.82rem; font-weight: 800;
  display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums;
}
.rank-chip.plain { background: var(--surface-2); color: var(--muted); }

.rank-change { font-size: 0.75rem; margin-left: 0.35rem; vertical-align: 1px; }
.rank-change.up { color: var(--accent); }
.rank-change.down { color: var(--warn); }

.leaderboard-table th { padding-top: 0; }
.leaderboard-table .col-rank { width: 3rem; }
.leaderboard-table .col-team .team-name { font-weight: 600; }
.leaderboard-table .team-sub { display: block; font-size: 0.8rem; color: var(--muted); margin-top: 0.1rem; }
.leaderboard-table .team-sub:empty { display: none; }
.leaderboard-table .col-act { width: 2.7rem; padding-left: 0.2rem; padding-right: 0.2rem; }
.leaderboard-table th.col-act { color: var(--text); }
.leaderboard-table th.col-act .icon { display: inline-flex; vertical-align: middle; }
.leaderboard-table th.col-act.pending, .leaderboard-table td.col-act.pending, .leaderboard-table td.col-act.none { color: var(--muted); }
.leaderboard-table .col-total { text-align: right; width: 3.5rem; }
.leaderboard-table td.col-total { font-weight: 800; font-size: 1.15rem; }
.leaderboard-table tr.is-followed td { background: var(--accent-weak); }
.leaderboard-table .team-sub.narrow-only { display: none; }
.leaderboard-note { font-size: 0.8rem; color: var(--muted); margin: 0.75rem 0 0; }
@media (max-width: 560px) {
  .leaderboard-table.with-columns .col-act { display: none; }
  .leaderboard-table .team-sub.narrow-only { display: block; }
}

/* ---------- Listes transverses de l'accueil (à suivre, résultats) ---------- */
.list-card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; }
.upcoming-row, .result-row {
  display: grid; align-items: center; gap: 0.75rem;
  padding: 0.7rem 1rem; border-bottom: 1px solid var(--border);
}
.list-card > :last-child { border-bottom: 0; }
.upcoming-row { grid-template-columns: minmax(150px, 210px) minmax(0, 1fr) auto; }
.result-row { grid-template-columns: minmax(150px, 210px) minmax(0, 1fr) auto minmax(0, 1fr); }
.match-meta { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; color: var(--muted); min-width: 0; }
.match-meta > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upcoming-teams { font-weight: 700; }
.upcoming-stage {
  font-size: 0.8rem; color: var(--muted); white-space: nowrap; font-weight: 600;
  background: var(--surface-2); padding: 0.15rem 0.55rem; border-radius: 999px;
}
.result-team.win { text-align: right; font-weight: 700; }
.result-team.win.draw { font-weight: 600; }
.result-team.lose { color: var(--muted); }
.result-score { font-weight: 800; font-size: 1.15rem; text-align: center; white-space: nowrap; min-width: 4.5rem; }
.result-wo { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
@media (max-width: 640px) {
  .upcoming-row { grid-template-columns: minmax(0, 1fr) auto; gap: 0.2rem 0.75rem; }
  .upcoming-row .match-meta { grid-column: 1 / -1; }
  .result-row { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 0.2rem 0.6rem; }
  .result-row .match-meta { grid-column: 1 / -1; }
}

/* ---------- Cartes "En direct" (accueil, terrains) ---------- */
.live-banner-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 0.75rem; }
.live-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.6rem; min-width: 0;
}
.live-card { border: 1.5px solid var(--live); overflow: hidden; }
.live-card.is-followed { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); background: var(--surface); }
/* Bandeau teinté "en direct" sur toute la largeur de la carte (marges
   négatives = padding de `.live-card`) : la carte se distingue d'une
   carte ordinaire autrement que par la seule couleur du chrono. */
.live-card-head {
  display: flex; align-items: center; gap: 0.45rem; font-size: 0.85rem; color: var(--muted); min-width: 0;
  margin: -0.9rem -1rem 0; padding: 0.5rem 1rem; background: var(--live-weak); font-weight: 600;
}
.live-card-activity { font-weight: 600; color: var(--text); }
.live-card-court { white-space: nowrap; }
.live-card-timer { margin-left: auto; display: inline-flex; align-items: center; gap: 0.35rem; color: var(--live); font-weight: 700; white-space: nowrap; }
.live-card-teams { display: flex; flex-direction: column; gap: 0.05rem; }
.live-card-team { font-weight: 700; font-size: 1.1rem; line-height: 1.25; overflow-wrap: anywhere; }
.live-card-team.followed { color: var(--accent); }
.live-card-vs { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.live-card-stage { font-size: 0.8rem; color: var(--muted); }

@keyframes pulse-live { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
}

/* ---------- Cartes d'activité (accueil) ---------- */
.activity-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0.75rem; }
.activity-card {
  display: flex; flex-direction: column; gap: 0.75rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  padding: 1rem 1.1rem; color: var(--text); text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.activity-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.activity-card-head { display: flex; align-items: center; gap: 0.75rem; }
.activity-card-title { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.activity-card-title strong { font-size: 1.05rem; }
.activity-card .progress-bar { margin-top: 0; }
.activity-card-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--muted); }
.activity-card-winner { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--text); font-weight: 600; }
.activity-card-winner .icon { color: var(--accent); }
.activity-card-arrow { color: var(--muted); }

/* ---------- Page d'une activité ---------- */
.activity-layout { display: grid; gap: 1.5rem; }
@media (min-width: 1100px) {
  .activity-layout { grid-template-columns: minmax(0, 1fr) 400px; align-items: start; }
}
.activity-main, .activity-aside { min-width: 0; display: grid; gap: 1.5rem; align-content: start; }
.activity-main > *, .activity-aside > * { min-width: 0; }
@media (min-width: 1100px) {
  .activity-layout.wide-main { grid-template-columns: minmax(0, 1fr); }
}
.activity-layout.wide-main .activity-aside { grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr)); align-items: start; }
.activity-main > section, .activity-aside section { margin-bottom: 0; }
#activity-side:empty { display: none; }

.pools-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: 1rem; }
.pools-live { display: grid; gap: 1rem; }
.pool-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1rem 1rem 0.35rem; }
.pool-card h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.pool-table { background: transparent; }
body.public-page .pool-table td, body.public-page .pool-table th, .pool-table td, .pool-table th { padding: 0.5rem 0.35rem; }
body.public-page .pool-table td { font-size: 0.97rem; }
.pool-table .col-rank { width: 2.4rem; color: var(--muted); font-weight: 700; }
.pool-table td.col-rank.rank-1 { color: var(--accent); }
.pool-table .col-pts { font-weight: 800; font-size: 1.05rem; }
.pool-table tr.qualif-cut td { border-bottom: 2px dashed var(--accent); }
.pool-table tr.is-followed td { background: var(--accent-weak); }
.qualif-badge {
  display: inline-block; margin-left: 0.45rem; font-size: 0.62rem; font-weight: 800;
  padding: 0.1rem 0.35rem; border-radius: 4px; vertical-align: 2px;
  background: var(--accent-weak); color: var(--accent);
}
.qualif-legend { display: inline-flex; align-items: center; gap: 0.5rem; }
.qualif-legend-line { display: inline-block; width: 18px; border-top: 2px dashed var(--accent); }
.form-dots { display: inline-flex; gap: 3px; justify-content: center; }
.form-dot { width: 9px; height: 9px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--muted); }
.form-dot.V { background: var(--accent); box-shadow: none; }
.form-dot.N { background: var(--muted); box-shadow: none; opacity: 0.6; }
.col-form { width: 3.6rem; }

.matches-panel { display: grid; gap: 0.75rem; }
.matches-panel[hidden] { display: none; }
.matches-panel .section-head { margin-bottom: 0; }
.match-group {
  display: grid; background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 0.1rem 1rem 0.3rem;
}
.match-group + .match-group { margin-top: 0.75rem; }
.match-group-label {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text); padding: 0.7rem 0 0.3rem;
}
.compact-match {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 0.6rem; align-items: center;
  padding: 0.5rem 0.25rem; border-top: 1px solid var(--border); font-size: 0.95rem;
}
.compact-match > :first-child { text-align: right; }
.compact-match .rank-1 { font-weight: 700; }
.compact-score {
  font-weight: 800; min-width: 4.6rem; text-align: center;
  display: inline-flex; justify-content: center; align-items: center; gap: 0.3rem; white-space: nowrap;
}
.compact-score [data-since] { color: var(--live); font-size: 0.85rem; }
.compact-court { font-size: 0.78rem; font-weight: 500; color: var(--muted); font-family: var(--font-body); }
.matches-panel > button.ghost, #matches-list > button.ghost { justify-self: center; margin-top: 0.4rem; width: 100%; }

/* Classement final (activité terminée) */
.final-ranking { display: grid; gap: 1rem; }
.champion-card {
  background: var(--accent-weak); border-radius: var(--radius-lg); padding: 1.4rem 1.2rem;
  text-align: center; display: grid; justify-items: center; gap: 0.35rem;
}
.champion-medal {
  width: 4.2rem; height: 4.2rem; border-radius: 50%; background: #d4af37; color: #3d2f00;
  display: inline-flex; align-items: center; justify-content: center; margin-bottom: 0.25rem;
}
.champion-label { font-size: 0.75rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); }
.champion-name { font-size: 1.9rem; font-weight: 800; line-height: 1.1; }
.final-list { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0.2rem 1.1rem; }
.final-row { display: grid; grid-template-columns: 2.4rem minmax(0, 1fr) auto; gap: 0.6rem; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid var(--border); }
.final-row:last-child { border-bottom: 0; }
.final-row.grouped { align-items: start; }
.final-row.is-followed { background: var(--accent-weak); }
.final-name, .final-group-title { font-weight: 600; }
.final-group-title { display: block; }
.final-group-names { display: block; color: var(--muted); font-size: 0.9rem; }

/* Classement en direct d'un bracket élimination en cours (accueil public,
   single_activity_mode — voir renderEliminationLive, public.js). Équipes
   encore en lice d'abord, puis éliminées groupées par tour de sortie. */
.still-in-list { display: flex; flex-direction: column; gap: 0.4rem; margin: 0 0 0.9rem; }
.still-in-row {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0.5rem 0.7rem; font-size: 0.95rem;
}
.still-in-row .status { font-size: 0.78rem; color: var(--muted); }
.out-round { margin-bottom: 0.7rem; }
.out-round .round-label { font-size: 0.75rem; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 0.3rem; }
.out-round ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem; }
.out-round li { font-size: 0.84rem; color: var(--muted); background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 0.2rem 0.5rem; }

/* ---------- Écran des terrains (/courts.html) : un terrain = une colonne ---------- */
.courts-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; align-items: start; }
.court-col {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 1rem; display: flex; flex-direction: column; gap: 0.6rem;
}
.court-col-header { display: flex; align-items: center; gap: 0.5rem; font-weight: 700; font-size: 1.1rem; }
body:not([data-arcade]) .court-col-header { font-family: var(--font-display); }
.court-free { color: var(--muted); font-style: italic; padding: 0.6rem 0; }
.court-warning { display: flex; align-items: center; gap: 0.4rem; color: var(--warn); font-weight: 700; font-size: 0.85rem; margin-top: 0.2rem; }
.court-upcoming-title { color: var(--text); font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; margin-top: 0.3rem; }
.court-next {
  display: flex; justify-content: space-between; align-items: center; gap: 0.5rem;
  padding: 0.55rem 0.7rem; border-radius: 10px; background: var(--surface-2); font-size: 0.95rem;
}
.court-next.is-followed { background: var(--accent-weak); }
.court-next-stage { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }

/* Confettis (fin de tournoi) : posés en overlay plein écran, transparents
   aux clics, retirés du DOM par public.js une fois l'animation terminée. */
.confetti-layer { position: fixed; inset: 0; pointer-events: none; z-index: 500; overflow: hidden; }
.confetti-piece {
  position: absolute; top: -12px; width: 8px; height: 14px; opacity: 0.9;
  animation-name: confetti-fall; animation-timing-function: ease-in; animation-fill-mode: forwards;
}
@keyframes confetti-fall { to { transform: translateY(105vh) rotate(600deg); opacity: 0.4; } }

/* ==========================================================================
   Espace organisateur
   ========================================================================== */

/* Lien stylé en bouton (navigation, pas action). */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  min-height: 40px; padding: 0.5rem 0.9rem; border-radius: var(--radius);
  background: var(--accent); color: var(--on-accent); border: 1px solid transparent;
  font-weight: 700; text-decoration: none; white-space: nowrap;
}
.btn.secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); font-weight: 600; }
.btn.secondary:hover { border-color: var(--text); }
.page-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.spacer { flex: 1; }

/* Menu "⋯" (`actionMenu`, admin.js) */
.action-menu-wrap { position: relative; display: inline-flex; }
.action-menu {
  position: absolute; top: calc(100% + 0.35rem); right: 0; z-index: 30;
  min-width: 14rem; background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  padding: 0.3rem; display: flex; flex-direction: column; gap: 0.1rem;
}
.action-menu[hidden] { display: none; }
.action-menu button {
  background: transparent; color: var(--text); border: 0; justify-content: flex-start; text-align: left;
  font-weight: 500; padding: 0.5rem 0.7rem; border-radius: 8px; min-height: 40px;
}
.action-menu button:hover, .action-menu button:focus-visible { background: var(--surface-2); filter: none; }
.action-menu button.danger-item { color: var(--danger); font-weight: 600; }
.action-menu button.danger-item:hover { background: var(--danger); color: #fff; }
.action-menu-sep { height: 1px; background: var(--border); margin: 0.25rem 0; }

/* Message temporaire "Score enregistré · Annuler" (`showUndoToast`). */
#toast-root { position: fixed; left: 50%; bottom: 1.5rem; transform: translateX(-50%); z-index: 200; width: max-content; max-width: calc(100vw - 2rem); }
.toast {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.6rem 0.6rem 0.6rem 1rem; border-radius: 12px;
  background: var(--text); color: var(--bg); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
  animation: msg-in 0.25s ease-out;
}
.toast .toast-undo { background: transparent; color: var(--bg); border: 0; text-decoration: underline; font-weight: 700; }
.toast .toast-undo:hover { background: color-mix(in srgb, var(--bg) 15%, transparent); filter: none; }

/* ---------- Accueil organisateur ---------- */
.admin-home { display: grid; gap: 1.5rem; }
@media (min-width: 1100px) {
  .admin-home { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; }
}
.admin-home-main, .admin-home-aside { display: grid; gap: 1.5rem; min-width: 0; align-content: start; }
.admin-home section { margin-bottom: 0; }
.todo-panel .section-head { margin-bottom: 0.25rem; }
.todo-panel h2 { font-size: 1.4rem; }
.todo-row {
  display: grid; grid-template-columns: 2.5rem minmax(0, 1fr) auto; align-items: center; gap: 0.9rem;
  padding: 0.8rem 0; border-top: 1px solid var(--border);
}
.todo-icon {
  width: 2.5rem; height: 2.5rem; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-weak); color: var(--accent);
}
.todo-row.warn .todo-icon { background: var(--warn-weak); color: var(--warn); }
.todo-title { font-weight: 700; }
.todo-sub { font-size: 0.88rem; color: var(--muted); }
.todo-empty { display: flex; align-items: center; gap: 0.5rem; color: var(--muted); margin: 0.5rem 0 0; }
.todo-empty .icon { color: var(--accent); }
@media (max-width: 640px) {
  .todo-row { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .todo-row > :last-child:not(.todo-text) { grid-column: 2; justify-self: start; }
}

.stat-tiles.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.stat-tiles.compact .stat-tile { padding: 0.8rem 1rem; }
.stat-tiles.compact .stat-value { font-size: 1.6rem; }

.compact-list .section-head { margin-bottom: 0.25rem; }
.compact-list h2 { font-size: 1.15rem; }
.progress-item { display: grid; gap: 0.35rem; padding: 0.65rem 0; border-top: 1px solid var(--border); color: var(--text); text-decoration: none; }
.progress-item:hover .progress-item-name { color: var(--accent); }
.progress-item-head { display: flex; align-items: center; gap: 0.5rem; }
.progress-item-name { flex: 1; font-weight: 600; }
.progress-item .progress-bar { margin-top: 0; }

.setup-done { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; padding: 0 0.25rem; }
.setup-done-label { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--muted); font-size: 0.9rem; }
.setup-done-label .icon { color: var(--accent); }
.setup-links { display: flex; gap: 0.25rem; flex-wrap: wrap; }
.setup-links button { min-height: 36px; font-size: 0.88rem; padding: 0.3rem 0.6rem; }

/* ---------- Éléments partagés des onglets organisateur ---------- */
.page-section-head { align-items: center; margin-bottom: 1rem; }
.section-head-title { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
.page-section-head h2 { font-size: 1.6rem; font-weight: 800; }
body:not([data-arcade]) .page-section-head h2 { font-family: var(--font-display); }
.field-title { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text); }
.field-hint { font-size: 0.85rem; color: var(--muted); margin: 0.3rem 0 0; }
.field-hint.warn { color: var(--warn); font-weight: 600; }
.field-group { display: grid; gap: 0.5rem; }
.empty-state { display: grid; gap: 0.4rem; }
.empty-state p { margin: 0; }
/* Étiquette (activité d'une équipe, catégorie du journal, aperçu d'import). */
.tag {
  display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.15rem 0.6rem; border-radius: 999px;
  font-size: 0.82rem; font-weight: 600; white-space: nowrap; text-decoration: none;
  background: var(--surface-2); color: var(--text);
}
a.tag:hover { background: var(--accent-weak); color: var(--accent); }
.tag.accent { background: var(--accent-weak); color: var(--accent); }
.tag.warn { background: var(--warn-weak); color: var(--warn); }
.tag.is-withdrawn { color: var(--muted); text-decoration: line-through; }
/* Bouton qui se lit comme un lien (action secondaire dans une phrase). */
button.link-btn {
  background: transparent; border: 0; padding: 0 0.2rem; min-height: 0; color: var(--accent);
  font-weight: 700; text-decoration: underline; text-underline-offset: 2px;
}
button.link-btn:hover:not(:disabled) { filter: none; color: var(--text); }
.notice {
  display: flex; align-items: center; gap: 0.6rem; padding: 0.7rem 1rem; border-radius: 12px;
  background: var(--surface-2); font-weight: 600; margin-bottom: 1rem;
}
.notice > span:not(.icon) { flex: 1; }
.notice.warn { background: var(--warn-weak); color: var(--warn); }
.notice.warn button.link-btn { color: var(--warn); }
.notice.card-foot { margin: 0; border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px); border-top: 1px solid var(--border); }
.segmented.full { display: grid; width: 100%; }

/* Panneau latéral (`openDrawer`, admin.js) : ajout d'équipes, inscription
   groupée. Plein écran sur téléphone. */
.drawer-backdrop { position: fixed; inset: 0; z-index: 290; background: rgba(15, 18, 16, 0.42); }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 300; width: min(480px, 100vw);
  display: flex; flex-direction: column; background: var(--surface); color: var(--text);
  box-shadow: -12px 0 40px rgba(0, 0, 0, 0.22); animation: drawer-in 0.2s ease-out;
}
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .drawer { animation: none; } }
.drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--border); }
.drawer-head h2 { margin: 0; font-size: 1.35rem; }
body:not([data-arcade]) .drawer-head h2 { font-family: var(--font-display); }
.drawer-head button { color: var(--muted); }
.drawer-body { flex: 1; overflow-y: auto; padding: 1.25rem; display: grid; gap: 1.25rem; align-content: start; }
.drawer-foot { display: flex; justify-content: flex-end; gap: 0.6rem; padding: 0.9rem 1.25rem; border-top: 1px solid var(--border); }
.drawer-pane { display: grid; gap: 1rem; }
.drawer .field label { color: var(--text); font-weight: 600; font-size: 0.9rem; }
.drawer textarea { line-height: 1.6; }
.choice-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.choice-chip {
  display: inline-flex; align-items: center; gap: 0.5rem; min-height: 42px; padding: 0 0.9rem;
  border: 1px solid var(--border-strong); border-radius: 10px; cursor: pointer;
  color: var(--text); font-size: 0.95rem; font-weight: 600; background: var(--surface);
}
.choice-chip:has(input:checked) { border: 2px solid var(--accent); background: var(--accent-weak); padding: 0 calc(0.9rem - 1px); }
.choice-chip input { width: 1.05rem; height: 1.05rem; margin: 0; min-height: 0; }
.bulk-preview { display: grid; gap: 0.4rem; }
.bulk-preview:empty { display: none; }
.bulk-preview-list { border: 1px solid var(--border); border-radius: 12px; padding: 0.15rem 0.9rem; max-height: 16rem; overflow-y: auto; }
.bulk-preview-row { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.5rem 0; }
.bulk-preview-row + .bulk-preview-row { border-top: 1px solid var(--border); }
.bulk-preview-name { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.bulk-preview-row:not(.new) .bulk-preview-name { color: var(--muted); text-decoration: line-through; }

/* ---------- Onglet Équipes ---------- */
.list-toolbar { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
.search-field { position: relative; flex: 1; min-width: 240px; max-width: 440px; display: block; color: var(--muted); }
.search-field .icon { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); pointer-events: none; }
.search-field input { padding-left: 2.4rem; min-height: 42px; }
/* Côté organisateur, les onglets segmentés prennent la largeur de leurs
   libellés (filtres, choix d'un réglage) plutôt que des colonnes égales
   étirées sur toute la ligne comme le filtre public des matchs. */
.admin-page .segmented:not(.full) { display: inline-grid; grid-auto-columns: auto; }
.admin-page .segmented:not(.full) button { padding: 0 0.8rem; white-space: nowrap; }
.team-table {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 4px 12px rgba(20, 20, 15, 0.06); overflow: visible;
}
.team-row-main {
  display: grid; grid-template-columns: 2rem minmax(0, 1.2fr) minmax(0, 1.4fr) 6.5rem 9rem;
  align-items: center; gap: 0.75rem; padding: 0.65rem 1.1rem;
}
.team-table-head {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  border-bottom: 1px solid var(--border); padding-top: 0.75rem; padding-bottom: 0.75rem;
}
.team-row + .team-row { border-top: 1px solid var(--border); }
.team-row.is-selected { background: var(--accent-weak); }
.team-row:last-child { border-radius: 0 0 calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px); }
.team-row-check { display: flex; align-items: center; }
.team-row-check input { width: 1.15rem; height: 1.15rem; min-height: 0; margin: 0; }
.team-row-name { display: flex; flex-direction: column; min-width: 0; }
.team-row-name strong { font-size: 1rem; overflow-wrap: anywhere; }
.team-row-name .meta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.team-row-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; min-width: 0; }
.team-row-status { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.team-row-status .forfeit-badge { margin-left: 0; }
.team-row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.2rem; }
.team-row-actions button.ghost { padding: 0 0.7rem; }
.team-row-actions .action-menu-wrap > button { border-color: transparent; background: transparent; color: var(--muted); }
.team-row-actions .action-menu-wrap > button:hover { border-color: var(--border-strong); color: var(--text); }
.team-row-edit { padding: 0.25rem 1.1rem 1rem calc(2rem + 1.85rem); }
.team-rows-empty { padding: 1.25rem 1.1rem; margin: 0; }
.list-count { text-align: center; margin: 0.75rem 0 0; }
.list-count:empty { display: none; }
@media (max-width: 760px) {
  .team-table-head { display: none; }
  .team-row-main { grid-template-columns: 2rem minmax(0, 1fr) auto; row-gap: 0.4rem; padding: 0.75rem 0.85rem; }
  .list-toolbar .segmented { max-width: 100%; overflow-x: auto; }
  .team-row-acts, .team-row-status { grid-column: 2 / 4; }
  .team-row-status:empty { display: none; }
  .team-row-actions { grid-column: 3; grid-row: 1; }
  .team-row-edit { padding: 0.25rem 0.85rem 1rem; }
}
/* Barre d'actions groupées, en bas de l'écran dès qu'une équipe est
   cochée. Couleurs inversées (texte/fond) : se détache de tout le reste. */
.bulk-bar {
  /* Centrée par `margin: auto` entre left/right plutôt que `left: 50%` +
     translate : ce dernier bridait sa largeur à la moitié de l'écran et la
     faisait passer sur deux lignes. */
  position: fixed; left: 1rem; right: 1rem; bottom: 1.25rem; margin: 0 auto; width: max-content; z-index: 150;
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; justify-content: center;
  max-width: calc(100vw - 2rem); padding: 0.55rem 0.6rem 0.55rem 1.1rem; border-radius: 14px;
  background: var(--text); color: var(--bg); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3);
}
.bulk-bar-count { font-weight: 700; margin-right: 0.4rem; }
/* Un message de confirmation passe au-dessus de la barre plutôt que dessus. */
body:has(#app > div:not([hidden]) > .bulk-bar:not([hidden])) #toast-root { bottom: 6rem; }
.bulk-bar button.on-dark { background: transparent; color: var(--bg); border-color: color-mix(in srgb, var(--bg) 45%, transparent); font-weight: 600; }
.bulk-bar button.on-dark:hover:not(:disabled) { background: color-mix(in srgb, var(--bg) 14%, transparent); filter: none; }
.bulk-bar button.on-dark.icon-btn { border-color: transparent; }
.bulk-bar button.on-dark-danger { background: var(--danger); color: #fff; }
/* Palettes sombres : `--danger` y est une couleur claire, le texte prend
   donc la couleur du fond (sombre). */
body[data-theme="dark"] .bulk-bar button.on-dark-danger,
body[data-arcade]:is([data-arcade="phosphor"], [data-arcade="oche"], [data-arcade="maitre"], [data-arcade="tykorrigans"]:not([data-theme="light"])) .bulk-bar button.on-dark-danger { color: var(--bg); }

/* ---------- Onglet Réglages ---------- */
.settings-layout { display: grid; gap: 1.75rem; }
@media (min-width: 1000px) {
  .settings-layout { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; }
  .settings-aside { position: sticky; top: 1rem; }
}
.settings-main, .settings-aside { display: grid; gap: 1.75rem; min-width: 0; align-content: start; }
.settings-section { margin: 0; display: grid; gap: 0.75rem; scroll-margin-top: 1rem; }
.settings-section > h2 { margin: 0; font-size: 1.35rem; }
.settings-nav { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.settings-nav .nav-pill { border-color: var(--border-strong); }
.settings-card { margin: 0; padding: 0; overflow: hidden; }
.settings-card > :not(.notice) { margin-left: 1.25rem; margin-right: 1.25rem; }
.settings-card > :first-child { margin-top: 1.1rem; }
.settings-card > :last-child:not(.notice) { margin-bottom: 1.1rem; }
.settings-card > * + * { margin-top: 0.9rem; }
.settings-card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.settings-card-head h3 { margin: 0; font-size: 1.05rem; }
.settings-card-head h3 label { color: inherit; font-size: inherit; }
.registration-status { display: flex; align-items: center; gap: 1rem; }
.status-icon {
  width: 2.9rem; height: 2.9rem; border-radius: 12px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--muted);
}
.status-icon.is-open { background: var(--live-weak); color: var(--live); }
.registration-status-text { flex: 1; display: grid; gap: 0.15rem; min-width: 0; }
.registration-status-text a { color: var(--accent); font-weight: 600; }
.registration-state { font-size: 1.2rem; font-weight: 800; color: var(--muted); }
.registration-state.is-open { color: var(--live); }
body:not([data-arcade]) .registration-state { font-family: var(--font-display); }
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 0.6rem; }
button.theme-swatch {
  display: grid; gap: 0.4rem; padding: 0.6rem; border-radius: 12px; text-align: left; font-weight: 400; min-height: 0;
  justify-content: stretch; align-content: start;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
}
button.theme-swatch:hover:not(:disabled) { border-color: var(--accent); filter: none; }
button.theme-swatch.is-on { border: 2px solid var(--accent); padding: calc(0.6rem - 1px); }
.theme-swatch-colors { display: flex; height: 3rem; border-radius: 8px; overflow: hidden; border: 1px solid var(--border); }
.theme-swatch-colors span { flex: 1; }
.theme-swatch-name { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; font-weight: 700; }
.theme-swatch-name .icon { color: var(--accent); }
.theme-swatch .meta { margin-top: -0.25rem; font-size: 0.8rem; }
.setting-row { display: flex; align-items: center; justify-content: space-between; gap: 1.25rem; padding-top: 0.9rem; border-top: 1px solid var(--border); flex-wrap: wrap; }
.settings-card > .setting-row:first-child { border-top: 0; padding-top: 0; }
.setting-row-text { display: grid; gap: 0.1rem; flex: 1; min-width: 200px; }
.setting-row-title { font-weight: 700; }
.setting-row-controls { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; }
.settings-subtitle { padding-top: 1rem; border-top: 1px solid var(--border); }
.settings-subtitle + .setting-row { border-top: 0; padding-top: 0; }
.inline-form { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.inline-form input { flex: 1; min-width: 200px; }
.announcement-preview { margin-bottom: 0; background: var(--bg); }
.poster-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; max-width: 340px; }
.settings-aside .qr-poster { margin: 0; }
.settings-aside .meta { margin: 0; max-width: 340px; }

/* ---------- Onglet Journal ---------- */
.journal-days { display: grid; gap: 1.25rem; max-width: 1000px; }
.journal-day { display: grid; gap: 0.5rem; }
.journal-card { padding: 0; margin: 0; overflow: hidden; }
.journal-row { display: grid; grid-template-columns: 3.3rem 2.3rem minmax(0, 1fr) auto; align-items: center; gap: 0.85rem; padding: 0.7rem 1.1rem; }
.journal-row + .journal-row { border-top: 1px solid var(--border); }
.journal-time { font-weight: 600; color: var(--muted); }
.journal-icon { width: 2.3rem; height: 2.3rem; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; background: var(--accent-weak); color: var(--accent); }
.journal-text { display: grid; min-width: 0; }
.journal-text .meta { overflow-wrap: anywhere; }
.journal-more { display: flex; justify-content: center; margin-top: 1rem; max-width: 1000px; }
@media (max-width: 640px) {
  .journal-row { grid-template-columns: 3rem minmax(0, 1fr); }
  .journal-icon, .journal-row > .tag { display: none; }
}

/* ---------- Onglet Activités (liste) ---------- */
.act-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr)); gap: 0.9rem; }
.act-card { display: flex; flex-direction: column; gap: 0.9rem; margin: 0; padding: 1rem 1.1rem; }
.act-card-head { display: flex; align-items: center; gap: 0.75rem; }
.act-card-title { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.act-card-title a { font-weight: 700; font-size: 1.08rem; color: var(--text); text-decoration: none; }
.act-card-title a:hover { color: var(--accent); }
.act-card-line { display: flex; align-items: center; gap: 0.45rem; margin: 0; font-size: 0.95rem; }
.act-card-line .icon { color: var(--accent); }
.act-card-line.warn, .act-card-line.warn .icon { color: var(--warn); font-weight: 600; }
.act-card-progress { display: grid; gap: 0.4rem; }
.act-card-progress .progress-bar { margin: 0; height: 7px; }
.act-card-progress-text { display: flex; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; }
.live-text { color: var(--live); }
.act-card-foot { display: flex; align-items: center; gap: 0.5rem; margin-top: auto; }
.act-card-foot > .btn, .act-card-foot > button { flex: 1; }
/* `.card a` (plus haut) passe tout lien d'une carte en bloc à la couleur du
   texte — à défaire pour un lien-bouton. */
.act-card a.btn { display: inline-flex; }
.act-card a.btn:not(.secondary) { color: var(--on-accent); }
.archived-activities { margin-top: 1.25rem; }
.archived-activities[open] > summary { margin-bottom: 0.75rem; }
button.ghost.danger-text { color: var(--danger); }
button.ghost.danger-text:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 12%, transparent); }

/* Panneau "Nouvelle activité" / "Modifier". */
.sport-pills { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.5rem; }
button.sport-pill {
  min-height: 36px; padding: 0 0.8rem; border-radius: 999px; font-size: 0.9rem; font-weight: 600;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
}
button.sport-pill[aria-pressed="true"] { background: var(--accent-weak); color: var(--accent); border-color: var(--accent); }
.format-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
button.format-card {
  display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 0.35rem;
  padding: 0.7rem; min-height: 0; border-radius: 12px; text-align: left; font-weight: 400;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
}
button.format-card:hover:not(:disabled) { border-color: var(--accent); filter: none; }
button.format-card[aria-pressed="true"] { background: var(--accent-weak); border: 2px solid var(--accent); padding: calc(0.7rem - 1px); }
.format-card-diagram { display: flex; justify-content: center; color: var(--muted); }
button.format-card[aria-pressed="true"] .format-card-diagram { color: var(--accent); }
.format-card-title { font-weight: 700; }
.format-card .meta { font-size: 0.8rem; line-height: 1.3; }
@media (max-width: 480px) { .format-cards { grid-template-columns: 1fr; } }
.form-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 0.85rem; border-top: 1px solid var(--border); }
.drawer-pane > .form-row:first-child { border-top: 0; padding-top: 0; }
.form-row-text { display: grid; gap: 0.1rem; min-width: 0; }
.form-row-title { font-weight: 700; }
.form-summary { margin: 0; padding: 0.7rem 0.9rem; border-radius: 10px; background: var(--surface-2); font-weight: 600; }
.num-stepper { display: inline-flex; align-items: center; gap: 0.35rem; flex-shrink: 0; }
.num-stepper button { width: 40px; padding: 0; font-size: 1.2rem; }
.num-stepper-input {
  width: 3.4rem; text-align: center; font-weight: 800; font-size: 1.05rem; padding: 0 0.2rem; border-width: 1.5px;
  -moz-appearance: textfield; appearance: textfield;
}
.num-stepper-input::-webkit-outer-spin-button, .num-stepper-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.drawer-foot-note { flex: 1; align-self: center; }
@media (max-width: 560px) { .drawer-foot-note { display: none; } }

/* ---------- Page d'une activité (admin) ---------- */
.prep-layout { display: grid; gap: 1.5rem; }
@media (min-width: 1000px) { .prep-layout { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; } }
.prep-main, .prep-aside { display: grid; gap: 0.75rem; min-width: 0; }
.prep-main > h2, .prep-aside > h2 { margin: 0; font-size: 1.3rem; }
.prep-steps { padding: 0; margin: 0; overflow: hidden; }
.prep-step { display: flex; align-items: center; gap: 0.9rem; padding: 0.95rem 1.2rem; }
.prep-step + .prep-step { border-top: 1px solid var(--border); }
.prep-step-mark {
  width: 2rem; height: 2rem; border-radius: 50%; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; background: var(--surface-2); color: var(--text);
}
.prep-step.is-done .prep-step-mark { background: var(--accent); color: var(--on-accent); }
.prep-step-text { flex: 1; display: grid; gap: 0.1rem; min-width: 0; }
.prep-step-title { font-weight: 700; }
.prep-main .notice { margin: 0; }
.prep-preview { margin: 0; display: grid; gap: 0.6rem; }
.prep-preview p { margin: 0; }
.prep-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); text-align: center; gap: 0.5rem; padding-bottom: 0.6rem; border-bottom: 1px solid var(--border); }
.prep-stat-value { font-size: 1.7rem; font-weight: 800; line-height: 1.1; }
body:not([data-arcade]) .prep-stat-value { font-family: var(--font-display); }
@media (max-width: 640px) {
  .prep-step { flex-wrap: wrap; }
  .prep-step-text { flex-basis: calc(100% - 3rem); }
  .prep-step > button { margin-left: 2.9rem; }
}
.finals-banner {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; padding: 0.85rem 1.1rem; border-radius: var(--radius-lg);
  background: var(--surface); border: 1px solid var(--border); margin-bottom: 1rem;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 4px 12px rgba(20, 20, 15, 0.06);
}
.finals-banner > .icon { color: var(--accent); }
.finals-banner-text { flex: 1; min-width: 220px; display: grid; gap: 0.4rem; }
.finals-banner-text .progress-bar { margin: 0; max-width: 420px; }
.matches-tab > .bracket-section-wide { margin-bottom: 1.25rem; }
.match-groups { display: grid; gap: 1rem; }
.match-group-card {
  margin: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 4px 12px rgba(20, 20, 15, 0.06);
}
.match-group-card > h3 { margin: 0; padding: 0.8rem 1rem 0.45rem; }
.match-group-card > .match-row { border: 0; border-top: 1px solid var(--border); border-radius: 0; box-shadow: none; padding: 0.6rem 1rem; }
.match-group-card > .match-row.next-match { background: var(--accent-weak); }
.reg-layout { display: grid; gap: 1.5rem; }
@media (min-width: 1000px) { .reg-layout { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; } }
.reg-main, .reg-aside { display: grid; gap: 0.75rem; margin: 0; min-width: 0; }
.reg-main .section-head { margin: 0; }
.reg-aside > h2 { margin: 0; }
.reg-table { padding: 0; margin: 0; overflow: hidden; }
.reg-row { display: grid; grid-template-columns: minmax(0, 1fr) auto 10rem; align-items: center; gap: 0.75rem; padding: 0.55rem 1.1rem; min-height: 3.2rem; }
.reg-row + .reg-row { border-top: 1px solid var(--border); }
.reg-row-name { display: flex; align-items: center; gap: 0.4rem; min-width: 0; flex-wrap: wrap; }
.reg-row-name .forfeit-badge { margin-left: 0; }
.reg-row.is-withdrawn .reg-row-name strong { color: var(--muted); text-decoration: line-through; }
.reg-row-actions { display: flex; align-items: center; justify-content: flex-end; gap: 0.4rem; }
.reg-add { margin: 0; display: grid; gap: 0.6rem; }
.reg-add .notice { margin: 0; }
.reg-choices { display: grid; max-height: 24rem; overflow-y: auto; }
.reg-choice { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0; color: var(--text); font-size: 0.95rem; cursor: pointer; }
.reg-choices .reg-choice + .reg-choice { border-top: 1px solid var(--border); }
.reg-choice input { width: 1.1rem; height: 1.1rem; min-height: 0; margin: 0; }
.reg-choice-name { flex: 1; font-weight: 600; }
.reg-choice-all { padding-bottom: 0.2rem; }
@media (max-width: 560px) { .reg-row { grid-template-columns: minmax(0, 1fr) auto; } .reg-row-actions { grid-column: 1 / -1; justify-content: flex-start; } }

/* ---------- Pilotage d'une activité ---------- */
.pilot { display: grid; gap: 1.25rem; }
.pilot-layout { display: grid; gap: 1.5rem; }
@media (min-width: 1100px) {
  .pilot-layout { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; }
  .pilot-layout.no-side { grid-template-columns: minmax(0, 1fr); }
}
.pilot-main, .pilot-side { display: grid; gap: 1.25rem; min-width: 0; align-content: start; }
.pilot-side section, .pilot-main section { margin-bottom: 0; }
.pilot-side .pool-card { border: 0; padding: 0; box-shadow: none; }
.pilot-side .pool-card + .pool-card { margin-top: 1rem; }
.pilot-banner section { margin: 0; }
.pilot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: 1rem; align-items: start; }
.pilot-court {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; display: flex; flex-direction: column;
}
.pilot-court.is-live { border: 2px solid var(--live); }
.pilot-court.is-live .pilot-court-head { background: var(--live-weak); }
.pilot-court-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.85rem 1rem; border-bottom: 1px solid var(--border); }
.pilot-court-head h3 { margin: 0; font-size: 1.15rem; }
body:not([data-arcade]) .pilot-court-head h3 { font-family: var(--font-display); }
.pilot-match { padding: 0.9rem 1rem; display: grid; gap: 0.7rem; border-bottom: 1px solid var(--border); }
.pilot-match-head { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
.pilot-match-head .badge [data-since] { margin-left: 0.25rem; }

.stepper-row { display: flex; align-items: center; gap: 0.75rem; }
.stepper-label { flex: 1; min-width: 0; font-size: 1.05rem; font-weight: 700; color: var(--text); overflow-wrap: anywhere; }
.stepper { display: flex; align-items: center; gap: 0.4rem; flex-shrink: 0; }
.stepper-btn { width: 44px; height: 44px; min-height: 44px; padding: 0; font-size: 1.4rem; font-weight: 600; }
.stepper-input {
  width: 4.2rem; height: 44px; text-align: center; font-size: 1.35rem; font-weight: 800; padding: 0 0.3rem; border-width: 1.5px;
  -moz-appearance: textfield; appearance: textfield;
}
.stepper-input::-webkit-outer-spin-button, .stepper-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
body:not([data-arcade]) .stepper-input { font-family: var(--font-display); }
.pilot-actions { display: flex; gap: 0.5rem; }
.pilot-save { flex: 1; min-height: 46px; font-size: 1rem; }

.pilot-queue { padding: 0.8rem 1rem; background: var(--surface-2); display: grid; gap: 0.45rem; }
.pilot-queue-title { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text); }
.pilot-queue-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.3rem 0.75rem; padding: 0.35rem 0; }
.pilot-queue-row + .pilot-queue-row { border-top: 1px solid var(--border); padding-top: 0.55rem; }
.pilot-queue-main { display: flex; flex-direction: column; min-width: 0; }
.pilot-queue-teams { font-weight: 600; }
.pilot-queue-row .pilot-conflict { grid-column: 1 / -1; }
.pilot-conflict {
  display: flex; align-items: flex-start; gap: 0.45rem; padding: 0.55rem 0.7rem; border-radius: 10px;
  background: var(--warn-weak); color: var(--warn); font-size: 0.88rem; font-weight: 600; line-height: 1.35;
}
.pilot-court > .pilot-conflict { margin: 0.75rem 1rem 0; }
.pilot-done { display: flex; align-items: center; gap: 0.9rem; }
.pilot-done > .icon { color: var(--accent); }
.pilot-done > div { flex: 1; }
.pilot-recent-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-top: 1px solid var(--border); }
.pilot-recent-score { font-weight: 800; min-width: 4rem; text-align: center; }
.pilot-recent .match-row { margin: 0.5rem 0; }

@media (max-width: 640px) {
  .page-actions { width: 100%; }
  .pilot-court-head, .pilot-match, .pilot-queue { padding-left: 0.8rem; padding-right: 0.8rem; }
  .stepper-row { flex-wrap: wrap; }
  .stepper-label { flex-basis: 100%; }
  .stepper { width: 100%; }
  .stepper-btn { width: 56px; height: 56px; min-height: 56px; }
  .stepper-input { flex: 1; height: 56px; font-size: 1.6rem; }
  .pilot-save { min-height: 52px; font-size: 1.05rem; }
}

/* ---------- Petits écrans ---------- */
@media (max-width: 640px) {
  header.top { padding: 0.8rem 1rem 0.7rem; }
  body.public-page header.top h1 { font-size: 1.35rem; }
  /* Médaillon réduit : à 108px il mangeait la moitié de l'en-tête sur un
     téléphone. Sélecteurs volontairement plus spécifiques que ceux de
     chaque thème (`body.public-page[data-arcade="…"] header.top::after`)
     pour s'appliquer à tous sans avoir à les répéter. */
  body[data-theme] header.top::after,
  body.public-page[data-theme][class] header.top::after { width: 64px; height: 64px; top: 0.6rem; right: 0.75rem; }
  body[data-theme] header.top h1,
  body.public-page[data-theme][class] header.top h1 { max-width: calc(100% - 80px); }
  header.top nav { max-width: none; }
  .nav-pill { min-height: 38px; padding: 0 0.75rem; font-size: 0.9rem; }
  main { padding: 1rem 0.75rem 2rem; }
  .page-title, body.public-page h2.page-title { font-size: 1.6rem; }
  body.public-page h2.section-title-primary { font-size: 1.4rem; }
  .panel { padding: 1rem; }
  .podium { gap: 0.5rem; }
  .podium-name { font-size: 0.88rem; }
  .podium-step.gold .podium-name { font-size: 1.02rem; }
  .podium-block { height: 70px; }
  .podium-step.gold .podium-block { height: 98px; }
  .podium-step.bronze .podium-block { height: 54px; }
  .podium-rank { width: 2.2rem; height: 2.2rem; font-size: 1.05rem; }
  .podium-step.gold .podium-rank { width: 2.8rem; height: 2.8rem; font-size: 1.3rem; }
  .page-head-progress { width: 100%; }
  /* Tableaux de poule : l'essentiel (joués, écart, points) sur téléphone,
     le détail V/N/D et la forme à partir d'une tablette. */
  .pool-table .hide-narrow { display: none; }
}

/* ==========================================================================
   Thèmes "arcade" de l'écran public — réglage `public_theme` (Réglages,
   admin), appliqué par public.js via `body.dataset.arcade`. Chacun
   redéfinit les mêmes variables que le mode sombre plus haut, donc la
   quasi-totalité des composants (cartes, badges, bracket, tableaux...) se
   réhabille automatiquement puisqu'ils sont déjà tous construits sur ces
   variables — seuls les quelques endroits en couleurs fixes (podium,
   ci-dessous) ont besoin d'un correctif dédié. Placé après le bloc
   `[data-theme="dark"]` : même spécificité (0,2,1), donc c'est l'ordre
   dans le fichier qui tranche quand les deux attributs sont présents en
   même temps (le jour/nuit continue de tourner en tâche de fond mais perd
   toujours face au thème arcade).
   ========================================================================== */
@keyframes arcade-blink { 50% { opacity: 0; } }

/* Chaque thème cible deux mondes : `body.public-page[data-arcade]` pour
   les quatre pages publiques (accueil, activité, terrains, inscription —
   toutes en `.public-page`), et `body[data-arcade]` tout court
   (spécificité volontairement égale) pour /admin.html, seule page sans
   cette classe. Le forçage jour/nuit (`data-theme`, voir plus haut) suit
   la même distinction quand un thème a une variante jour — admin compris,
   au même titre que les pages publiques. */

/* ---------- Phosphor : terminal ambre monochrome, écran de high-score ---------- */
/* `:has()` plutôt qu'une variable de plus dans le bloc ci-dessous : VT323
   a un x-height visuellement plus petit qu'une police normale à la même
   taille déclarée (caractéristique connue de cette police pixel) — un peu
   de texte fin illisible sinon sur tout le texte courant (tableaux,
   listes de matchs), pas seulement les titres. Sans risque pour la
   géométrie du bracket : ses dimensions sont posées en px direct par JS,
   jamais en rem, donc insensibles à la taille racine — seul le texte à
   l'intérieur grossit légèrement, marge déjà prévue par l'ellipse sur les
   noms d'équipe trop longs. */
:root:has(body[data-arcade="phosphor"]) { font-size: 107%; }

body.public-page[data-arcade="phosphor"],
body[data-arcade="phosphor"] {
  /* Lisibilité (2026-09, voir `:root`) : surface #0a1f12 → #0a2917, bordure
     #3a2c05 → #504205 (cartes quasi fondues dans le fond à 1,12:1). */
  --bg: #05100a; --surface: #0a2917; --border: #504205;
  --line: #5c4a08; --border-strong: #86700a; --on-live: #05100a;
  /* Le brun d'origine (#7a5200) tombait sous 3:1 de contraste avec --bg ET
     --surface (quasi illisible) — remonté pour rester nettement dans la
     même famille ambrée tout en repassant au-dessus de 4,5:1 sur les deux. */
  --text: #ffb000; --muted: #cc9933; --accent: #fff1c9; --accent-weak: #2a2005;
  --warn: #ff6b3d; --live: #33e1ff; --live-weak: #062830; --danger: #ff3b3b;
  --on-accent: #1a1200;
  font-family: "VT323", ui-monospace, monospace;
  text-transform: uppercase; letter-spacing: 0.03em;
  /* Pas de lueur ici (contrairement au h1 juste en dessous) : un flou sur
     du texte dense (tableaux, listes de matchs) le rend flou, pas juste
     stylé — la lueur reste réservée aux gros titres qui peuvent se le
     permettre. */
}
body.public-page[data-arcade="phosphor"] header.top h1,
body[data-arcade="phosphor"] header.top h1,
body[data-arcade="phosphor"] .qr-poster-title {
  font-family: "Press Start 2P", monospace; font-size: 1.35rem; letter-spacing: 0.02em; text-shadow: 0 0 8px rgba(255, 176, 0, 0.6);
}
/* Même lueur que le h1, mais sans passer en "Press Start 2P" : ce titre
   ("Classement général") est plus long qu'un nom d'événement — la police
   très large et carrée de la pastille h1 le ferait déborder ou passer à la
   ligne au lieu de simplement le mettre en valeur. */
body.public-page[data-arcade="phosphor"] h2.section-title-primary {
  text-shadow: 0 0 8px rgba(255, 176, 0, 0.6);
}
body.public-page[data-arcade="phosphor"] header.top h1::after,
body[data-arcade="phosphor"] header.top h1::after { content: "_"; margin-left: 0.15em; }
@media (prefers-reduced-motion: no-preference) {
  body.public-page[data-arcade="phosphor"] header.top h1::after,
  body[data-arcade="phosphor"] header.top h1::after { animation: arcade-blink 1s steps(1) infinite; }
}
/* Mascotte en médaillon dans l'en-tête, à droite du titre — même mécanisme
   que Ty Korrigans/le thème par défaut (`max-width` sur le seul h1,
   `header.top::after` positionné à droite). Lueur ambrée en guise
   d'ombre portée plutôt que le noir habituel des deux autres thèmes :
   cohérent avec la lueur déjà posée sur le h1 et le titre "Classement
   général" juste au-dessus. */
body.public-page[data-arcade="phosphor"] header.top,
body[data-arcade="phosphor"] header.top {
  position: relative;
}
body.public-page[data-arcade="phosphor"] header.top h1,
body[data-arcade="phosphor"] header.top h1 {
  max-width: calc(100% - 132px);
}
body.public-page[data-arcade="phosphor"] header.top::after,
body[data-arcade="phosphor"] header.top::after {
  content: ""; position: absolute; top: 0.5rem; right: 1rem;
  width: 108px; height: 108px;
  background: url("/img/phosphor-mascot.svg") no-repeat center / contain;
  filter: drop-shadow(0 0 10px rgba(255, 176, 0, 0.5));
}
body.public-page[data-arcade="phosphor"]::after,
body[data-arcade="phosphor"]::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 300;
  /* Bandes assombries à 28% à l'origine : assez pour noircir un peu de
     texte à chaque triplet de lignes de pixels, cumulé au flou déjà en
     moins ci-dessus. Adoucies pour garder l'effet CRT visible sans
     rogner davantage sur le contraste. */
  background: repeating-linear-gradient(rgba(0, 0, 0, 0) 0px, rgba(0, 0, 0, 0) 2px, rgba(0, 0, 0, 0.16) 3px);
  mix-blend-mode: multiply;
}
body.public-page[data-arcade="phosphor"] .podium-step.gold .podium-rank { background: #fff1c9; color: #1a1200; }
body.public-page[data-arcade="phosphor"] .podium-step.silver .podium-rank { background: #ffb000; color: #1a1200; }
body.public-page[data-arcade="phosphor"] .podium-step.bronze .podium-rank { background: #7a5200; color: #1a1200; }
/* Texte des boutons/CTA sur fond `--accent` clair (Phosphor, Oché, Le
   Maître, Ty Korrigans) : géré par `--on-accent`, défini dans le bloc de
   variables de chaque thème plus haut/bas — voir sa définition par défaut
   dans `:root` pour l'historique (a remplacé une règle par thème du type
   `button:not(.secondary):not(...) { color: ... }`, qui s'est fait piéger
   trois fois par un futur bouton à fond propre avant d'être abandonnée). */

/* ---------- Oché : soirée fléchettes façon retransmission pro (PDC) ---------- */
body.public-page[data-arcade="oche"],
body[data-arcade="oche"] {
  /* Lisibilité (2026-09, voir `:root`) : surface #131722 → #1e222d, bordure
     #262c3a → #393f52 (carte/fond 1,08:1 → 1,22:1). */
  --bg: #0b0e14; --surface: #1e222d; --border: #393f52;
  --line: #474e63; --border-strong: #6a7288; --on-live: #0b0e14;
  --text: #eef1f5; --muted: #b7c0cc; --accent: #ffcc00; --accent-weak: #3a3210;
  /* `--live` servait à la fois de texte sur `--live-weak` ET sur
     `--surface` (étiquette "En cours" des `.live-card`, voir style.css plus
     bas) — le vert sombre d'origine (#0b6e4f) donnait ~2,9:1 sur ces deux
     fonds, illisible (min. lisible : 4,5:1). Vert plus vif, ~7,8:1 sur
     `--surface`. */
  --warn: #ff6b7a; --live: #22c55e; --live-weak: #0d2b20; --danger: #ff6b7a;
  --on-accent: #0b0e14;
  font-family: "Barlow Condensed", "Arial Narrow", sans-serif;
}
body.public-page[data-arcade="oche"] header.top,
body[data-arcade="oche"] header.top { border-bottom-width: 4px; border-bottom-color: var(--accent); }
body.public-page[data-arcade="oche"] header.top h1,
body[data-arcade="oche"] header.top h1,
body.public-page[data-arcade="oche"] h2.section-title-primary,
body.public-page[data-arcade="oche"] .score,
body.public-page[data-arcade="oche"] .podium-rank,
body[data-arcade="oche"] .qr-poster-title {
  font-family: "Anton", "Barlow Condensed", sans-serif; letter-spacing: 0.02em;
}
/* Titres de section en capitales espacées, façon bandeau de retransmission
   — propre à ce thème depuis la refonte (le socle commun ne met plus les
   titres en capitales). */
body.public-page[data-arcade="oche"] h2 { letter-spacing: 0.08em; text-transform: uppercase; }
body.public-page[data-arcade="oche"] .badge, body.public-page[data-arcade="oche"] .badge-btn,
body[data-arcade="oche"] .badge, body[data-arcade="oche"] .badge-btn {
  text-transform: uppercase; letter-spacing: 0.05em; border-radius: 3px;
}
body.public-page[data-arcade="oche"] .podium-step.gold .podium-rank { background: #ffcc00; color: #0b0e14; }
body.public-page[data-arcade="oche"] .podium-step.silver .podium-rank { background: #b7c0cc; color: #0b0e14; }
body.public-page[data-arcade="oche"] .podium-step.bronze .podium-rank { background: #ff6b7a; color: #0b0e14; }
/* Logo en médaillon dans l'en-tête, à droite du titre — même mécanisme
   que Ty Korrigans/le thème par défaut/Phosphor (`max-width` sur le seul
   h1, `header.top::after` positionné à droite). `position: relative` en
   plus de la règle `border-bottom` déjà posée sur `header.top` plus haut,
   pas à la place. */
body.public-page[data-arcade="oche"] header.top,
body[data-arcade="oche"] header.top {
  position: relative;
}
body.public-page[data-arcade="oche"] header.top h1,
body[data-arcade="oche"] header.top h1 {
  max-width: calc(100% - 132px);
}
body.public-page[data-arcade="oche"] header.top::after,
body[data-arcade="oche"] header.top::after {
  content: ""; position: absolute; top: 0.5rem; right: 1rem;
  width: 108px; height: 108px;
  background: url("/img/oche-badge.svg") no-repeat center / contain;
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
}

/* ---------- Le Maître : palets bretons, granite/marin ---------- */
body.public-page[data-arcade="maitre"],
body[data-arcade="maitre"] {
  /* Lisibilité (2026-09, voir `:root`) : surface #232f35 → #29353b, bordure
     #3c4f57 → #3f525d. `--live` #e2593f → #f2856b : le rouge d'origine ne
     faisait que 3,4 à 3,8:1 en texte sur les cartes ("en cours", chrono),
     sous le minimum de 4,5:1 — corail plus clair, 5:1 sur `--surface`.
     `--muted` #93a5ac → #a8b8be : 4,3:1 seulement sur `--surface-2`
     (badge "À venir", en-tête des cases du bracket). */
  --bg: #1c262b; --surface: #29353b; --border: #3f525d;
  --line: #50636d; --border-strong: #71858f; --on-live: #1c262b;
  --text: #eef2f0; --muted: #a8b8be; --accent: #d9a441; --accent-weak: #3a2f15;
  --warn: #e2593f; --live: #f2856b; --live-weak: #3a1912;
  /* `--warn`/`--live` (#e2593f) sont assez clairs pour porter du texte
     sombre (voir le bronze du podium juste plus bas) — trop clair pour du
     texte blanc lisible sur le bouton "Forfait" (.forfeit-btn), d'où un
     rouge plus soutenu dédié plutôt qu'une réutilisation. */
  --danger: #c23b28;
  --on-accent: #1c262b;
  font-family: "Public Sans", "Segoe UI", sans-serif;
}
body.public-page[data-arcade="maitre"]::before,
body[data-arcade="maitre"]::before {
  content: ""; position: fixed; top: 0; left: 0; right: 0; height: 10px; z-index: 300;
  background: repeating-linear-gradient(90deg, #0f171b 0 26px, #eef2f0 26px 52px);
}
body.public-page[data-arcade="maitre"] header.top,
body[data-arcade="maitre"] header.top { padding-top: calc(1.25rem + 10px); }
body.public-page[data-arcade="maitre"] header.top h1,
body[data-arcade="maitre"] header.top h1,
body.public-page[data-arcade="maitre"] h2.section-title-primary,
body[data-arcade="maitre"] .qr-poster-title {
  font-family: "Fjalla One", "Barlow Condensed", sans-serif; font-weight: 400; letter-spacing: 0.01em;
}
body.public-page[data-arcade="maitre"] .podium-step.gold .podium-rank { background: #d9a441; color: #1c262b; }
body.public-page[data-arcade="maitre"] .podium-step.silver .podium-rank { background: #3c4f57; color: #eef2f0; }
body.public-page[data-arcade="maitre"] .podium-step.bronze .podium-rank { background: #e2593f; color: #1c262b; }

/* ---------- Boulodrome : pétanque, plaque émaillée de terrasse de café.
   Volontairement clair (gravier au soleil) — rompt avec les quatre pistes
   sombres qui précèdent. ---------- */
body.public-page[data-arcade="boulodrome"],
body[data-arcade="boulodrome"] {
  /* Lisibilité (2026-09, voir `:root`) : fond #f3e9d2 → #efe3c7 ; `--muted`
     #7a6a4a → #6e5f40 pour rester ≥4,5:1 posé directement sur ce fond. */
  --bg: #efe3c7; --surface: #fffaf3; --border: #d9c9a0;
  --line: #a8956a; --border-strong: #9a8760;
  --text: #1f3d2b; --muted: #6e5f40; --accent: #b5121b; --accent-weak: #f8dede;
  --warn: #a85d1f; --live: #2f6b3f; --live-weak: #dfe9df;
  /* `--accent` est déjà un rouge profond ici (plaque émaillée) — un rouge
     un peu plus sombre pour le forfait garde les deux boutons
     distinguables au lieu de sembler être la même action. */
  --danger: #7a0f18;
  font-family: "PT Sans", Arial, sans-serif;
}
body.public-page[data-arcade="boulodrome"] header.top,
body[data-arcade="boulodrome"] header.top {
  border-top-width: 6px; border-top-style: solid; border-top-color: var(--accent);
  border-bottom-width: 6px; border-bottom-color: var(--accent);
}
body.public-page[data-arcade="boulodrome"] header.top h1,
body[data-arcade="boulodrome"] header.top h1,
body.public-page[data-arcade="boulodrome"] h2.section-title-primary,
body[data-arcade="boulodrome"] .qr-poster-title {
  font-family: "Bebas Neue", "PT Sans", sans-serif; letter-spacing: 0.04em;
}
body.public-page[data-arcade="boulodrome"] .podium-step.gold .podium-rank { background: #1f3d2b; color: #fff8ec; }
body.public-page[data-arcade="boulodrome"] .podium-step.silver .podium-rank { background: #7a6a4a; color: #fff8ec; }
body.public-page[data-arcade="boulodrome"] .podium-step.bronze .podium-rank { background: #b5121b; color: #fff8ec; }

/* ---------- Quilles du Nord : molkky/jeux en bois, minimalisme scandinave.
   Le plus épuré des cinq — un seul accent (le pin), pas de code couleur
   or/argent/bronze : juste des chiffres, comme des quilles numérotées. ---------- */
body.public-page[data-arcade="molkky"],
body[data-arcade="molkky"] {
  /* Lisibilité (2026-09, voir `:root`) : fond #f5f0e6 → #ece5d6, bordure
     #e2dccb → #d5cfbe. `--muted` #8c8672 → #67614f : l'ancien gris ne
     faisait que 3,6:1 sur les cartes blanches, sous le minimum de 4,5:1. */
  --bg: #ece5d6; --surface: #ffffff; --border: #d5cfbe;
  --line: #a8a08a; --border-strong: #99927d;
  --text: #1c1c1a; --muted: #67614f; --accent: #2f5233; --accent-weak: #e3ebe1;
  --warn: #9c5b3c; --live: #2f5233; --live-weak: #e3ebe1;
  /* Thème volontairement réduit à un seul accent (le pin, voir plus haut) —
     un rouge vif jurerait ; une terre cuite reste dans la même famille
     "bois/nature" tout en se distinguant clairement du pin. */
  --danger: #a13d2b;
  font-family: "Karla", "Segoe UI", sans-serif;
}
body.public-page[data-arcade="molkky"] header.top h1,
body[data-arcade="molkky"] header.top h1,
body.public-page[data-arcade="molkky"] h2.section-title-primary,
body[data-arcade="molkky"] .qr-poster-title {
  font-family: "Archivo", sans-serif; font-weight: 800; letter-spacing: 0.01em;
}
body.public-page[data-arcade="molkky"] .podium-step.gold .podium-rank,
body.public-page[data-arcade="molkky"] .podium-step.silver .podium-rank,
body.public-page[data-arcade="molkky"] .podium-step.bronze .podium-rank {
  background: #1c1c1a; color: #fff;
}

/* ---------- Ty Korrigans : club de fléchettes, thème "secret" fait sur
   mesure pour leur instance dédiée — volontairement absent de
   `ARCADE_OPTIONS` (admin.js), pas proposé dans le menu déroulant des
   réglages, activable seulement via un réglage direct
   (`public_theme: "tykorrigans"`). Palette tirée du blason du club
   (public/img/tykorrigans-crest.png) : vert "sous-bois" presque noir en
   fond, rouge "braise" (béret/cible du blason) réservé au statut "en
   direct" plutôt qu'au vert habituel des autres thèmes — les deux accents
   ne se marchent pas dessus. Accent principal en or plutôt qu'en vert
   "korrigan" d'origine (#4fbf93) : un vert clair sur ce fond vert sombre
   passait le seuil de contraste WCAG (~5,5:1) mais restait dur à lire en
   pratique, deux teintes trop proches (highlight des équipes gagnantes,
   badges, boutons...) — l'or, hors famille de couleur, se détache
   toujours du fond quel que soit l'éclairage. Évoque aussi la médaille du
   vainqueur, cohérent avec son usage (`.rank-1`). ---------- */
body.public-page[data-arcade="tykorrigans"],
body[data-arcade="tykorrigans"] {
  /* Lisibilité (2026-09, voir `:root`) : carte/fond déjà correct (1,45:1),
     seule la bordure passe de #1f5445 à #245c4c. */
  --bg: #081815; --surface: #123a30; --border: #245c4c;
  --line: #2f6b5a; --border-strong: #4a8a78; --on-live: #081815;
  --text: #eef7f2; --muted: #9fc4b6; --accent: #d4af37; --accent-weak: #163f34;
  /* `--live` (vignette/badge "en cours", `.badge.en_cours`) même souci que
     l'ancien `--accent` : le rouge "braise" d'origine (#d43b35) sur son
     fond `--live-weak` (#3a1210, même famille rouge sombre) ne passait que
     ~3,5:1 — un rouge saturé plafonne de toute façon assez bas en
     luminance perçue (canal rouge peu pondéré), impossible à corriger sans
     l'éclaircir vers le corail. Remonté vers un rouge-corail plus clair :
     ~6,4:1 sur `--live-weak`, ~4,9:1 sur `--surface` (utilisé aussi hors
     badge, ex. `.live-tag`). `--live-weak`/`--danger` inchangés. */
  --warn: #e2a33d; --live: #ff7a5c; --live-weak: #3a1210; --danger: #e2483f;
  --on-accent: #08211a;
  font-family: "Mulish", -apple-system, "Segoe UI", sans-serif;
}
/* Variante "jour" — organisateur ET public (`data-theme`, posé par
   `applyTheme()`/`theme_override`, public.js, aussi bien pour l'admin que
   les pages publiques — même mécanisme jour/nuit que le thème par défaut
   plus haut dans ce fichier, longtemps resté sans effet visible pour un
   thème arcade puisqu'aucun n'en profitait). Plus spécifique que le bloc
   ci-dessus (3 sélecteurs simples contre 2), l'emporte sans dépendre de
   l'ordre des règles. Mêmes teintes de marque (or, corail) mais toutes
   réassombries pour rester lisibles sur fond clair plutôt que sur fond
   très sombre — inverser juste fond/texte du bloc ci-dessus aurait cassé
   le contraste de `--accent` (l'or clair, pensé pour se détacher d'un fond
   sombre, tombe à ~1,8:1 sur blanc) : chaque couleur est repensée pour son
   propre fond, même logique que `:root` vs le mode sombre système plus
   haut. Contrastes vérifiés ≥4,5:1 sur toutes les paires réellement
   utilisées (texte/fond, badge fond/texte, bouton blanc/fond). Pense à
   ajouter la même variante (les deux sélecteurs, avec et sans
   `.public-page`) pour un futur thème "club custom" qui en aurait besoin
   (voir CLAUDE.md). */
body.public-page[data-arcade="tykorrigans"][data-theme="light"],
body[data-arcade="tykorrigans"][data-theme="light"] {
  /* Lisibilité (2026-09, voir `:root`) : fond #f2f6ee → #e5ecdf, bordure
     #d8e0d1 → #c9d3c1, `--muted` #5c6f63 → #56685c (≥4,5:1 sur ce fond). */
  --bg: #e5ecdf; --surface: #ffffff; --border: #c9d3c1;
  --line: #9aa894; --border-strong: #8a9884; --on-live: #fff;
  --text: #10261c; --muted: #56685c;
  --accent: #7d621b; --accent-weak: #faf3de;
  --warn: #9a5f13; --live: #c23f2d; --live-weak: #fdedea; --danger: #b3261e;
  --on-accent: #fff;
}
body.public-page[data-arcade="tykorrigans"] header.top h1,
body[data-arcade="tykorrigans"] header.top h1,
body.public-page[data-arcade="tykorrigans"] h2.section-title-primary,
body[data-arcade="tykorrigans"] .qr-poster-title {
  font-family: "Baloo 2", "Comic Sans MS", sans-serif; font-weight: 700;
}
/* Laisse la place au blason agrandi (voir header.top::after plus bas) : sans
   ça un long nom d'événement passerait dessous sur un écran étroit. */
body.public-page[data-arcade="tykorrigans"] header.top h1,
body[data-arcade="tykorrigans"] header.top h1 {
  max-width: calc(100% - 132px);
}
/* Halo de lune discret derrière le titre — seul effet de signature du
   thème, pensé pour rester lisible plutôt que spectaculaire. */
body.public-page[data-arcade="tykorrigans"] header.top,
body[data-arcade="tykorrigans"] header.top {
  position: relative; overflow: hidden;
}
body.public-page[data-arcade="tykorrigans"] header.top::before,
body[data-arcade="tykorrigans"] header.top::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(420px 220px at 88% -20%, rgba(79, 191, 147, 0.18), transparent 70%);
}
/* Blason en médaillon dans l'en-tête, à côté du titre plutôt qu'en
   remplacement de la marque "Tournamant" — image plutôt que texte, seul
   thème dans ce cas puisque c'est le seul avec un vrai logo de club à
   faire vivre. */
body.public-page[data-arcade="tykorrigans"] header.top::after,
body[data-arcade="tykorrigans"] header.top::after {
  content: ""; position: absolute; top: 0.5rem; right: 1rem;
  width: 108px; height: 108px;
  background: url("/img/tykorrigans-crest.png") no-repeat center / contain;
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
}
body.public-page[data-arcade="tykorrigans"] .podium-step.gold .podium-rank { background: #4fbf93; color: #08211a; }
body.public-page[data-arcade="tykorrigans"] .podium-step.silver .podium-rank { background: #7fa89c; color: #08211a; }
body.public-page[data-arcade="tykorrigans"] .podium-step.bronze .podium-rank { background: #d43b35; color: #2a0605; }

/* ---------- Correctifs communs aux thèmes arcade, utiles surtout côté
   admin (l'écran le plus chargé en badges/messages) : `.msg.error` a une
   couleur codée en dur plutôt que les variables --bg/etc, donc elle
   restait un carré rose clair figé au-dessus d'un fond soudain sombre au
   lieu de suivre le thème. (`.badge.a_venir`/`.badge-btn.a_venir` avaient
   le même souci — corrigé directement à la source plutôt que par une règle
   à part, voir plus haut.) ---------- */
body[data-arcade] .msg.error { background: #3a1a1a; color: #ff8a80; }

/* ---------- Affiche QR code (admin, onglet Réglages) ---------- */
/* `isolation: isolate` : `position: relative` seul ne crée pas de contexte
   d'empilement (il faudrait aussi un `z-index` explicite) — sans lui, le
   filigrane à `z-index: -1` ci-dessous ne reste pas juste "derrière le
   contenu, devant le fond de la carte" comme voulu : il s'échappe carrément
   derrière le fond opaque de `.card` (`background: var(--surface)`), qui
   appartient au même élément mais se peint dans un contexte d'empilement
   plus haut dans l'arbre faute de contexte propre ici — invisible en
   pratique. Bug resté muet jusqu'ici (opacité déjà très faible, jamais
   vraiment comparé à l'œil au rendu voulu). */
.qr-poster { position: relative; isolation: isolate; overflow: hidden; text-align: center; max-width: 340px; }
/* N'existent dans le DOM que pour un thème "sur mesure" avec un blason de
   club (voir CUSTOM_CLUB_CRESTS, admin.js) — pas besoin d'un sélecteur
   `[data-arcade]` ici. */
.qr-poster-crest { display: block; width: 160px; height: auto; margin: 0 auto 0.6rem; }
/* Filigrane derrière toute la carte, jamais derrière le QR lui-même : sa
   lisibilité vient du contraste noir-sur-blanc dans son propre pavé
   (`.qr-poster-imgwrap`, fond blanc opaque), un logo même discret dans son
   dos resterait un risque inutile pour le scan — voir `.qr-poster-img`.
   `z-index: -1` (pas juste "avant les autres dans le DOM") : passe
   automatiquement derrière tout contenu en flux normal de la carte, sans
   avoir à toucher chacun des autres éléments pour les repasser devant.
   Mêmes réglages que le filigrane des diplômes (`drawCrestWatermark`,
   admin.js : opacité 0.07, rotation ~-7°) pour un rendu identique.
   Centré sur toute la carte, il finissait quasi entièrement recouvert par
   le pavé blanc opaque du QR (`.qr-poster-imgwrap`, ~55% de la hauteur de
   la carte) — invisible en pratique, vérifié à l'écran (capture avant/
   après, y compris à pleine opacité) avant un premier correctif (calé en
   haut, sous le blason). Repositionné en bas à droite pour matcher la
   même ancre que sur les diplômes (`drawCrestWatermark`, ~72%/80%) — la
   moitié gauche déborde sous le pavé du QR (masquée, sans effet sur le
   scan puisqu'il reste toujours au-dessus), seule la partie qui dépasse
   en bas et à droite du pavé est réellement visible, comme prévu. */
.qr-poster-watermark {
  position: absolute; z-index: -1; top: 85%; left: 78%;
  width: 55%; max-width: 190px; height: auto;
  transform: translate(-50%, -50%) rotate(-6.9deg);
  opacity: 0.07; pointer-events: none;
}
.qr-poster-title { font-weight: 700; font-size: 1.3rem; color: var(--accent); }
.qr-poster-tagline { color: var(--muted); font-size: 0.85rem; margin-top: 0.3rem; }
/* Le QR reste toujours noir sur blanc dans son propre pavé, quel que soit
   le thème actif — c'est le contraste qui garantit qu'il reste scannable,
   pas une question de goût. Seul le cadre autour (bordure couleur --accent)
   porte l'identité du thème. */
.qr-poster-imgwrap {
  display: inline-block; background: #fff; padding: 0.9rem; border-radius: 10px;
  border: 3px solid var(--accent); margin-top: 1.1rem;
}
.qr-poster-img { width: 230px; height: 230px; display: block; }
.qr-poster-url { margin-top: 0.9rem; font-size: 0.78rem; color: var(--muted); word-break: break-all; }
/* Signature de marque, toujours en bas à droite de la carte quel que soit
   son contenu — même repère que sur les diplômes ("tournamant.bzh" en bas
   à droite, voir `drawDiploma`/`drawPodiumRecap`, admin.js), pour la même
   raison : cette affiche quitte l'appli une fois imprimée ou partagée. */
.qr-poster-brand {
  position: absolute; right: 0.7rem; bottom: 0.5rem;
  font-size: 0.68rem; color: var(--muted); pointer-events: none;
}

/* N'imprime que l'affiche (masque le reste de l'admin) — technique
   classique "print just this element" : tout est caché via `visibility`
   (pas `display`, sans quoi l'affiche — nichée dans <main>/#app/une
   section — disparaîtrait avec ses ancêtres masqués), puis l'affiche et
   son contenu sont explicitement rendus visibles. Elle reprend les
   couleurs du thème actif (fond, bordure, titre) plutôt qu'un noir sur
   blanc générique — les navigateurs n'impriment pas les couleurs de fond
   par défaut, il faut le demander explicitement via `print-color-adjust`. */
@media print {
  @page { size: A4; margin: 0; }
  body * { visibility: hidden; }
  /* Les bandeaux décoratifs de certains thèmes (rayure marinière de Le
     Maître, trame CRT de Phosphor) sont posés en ::before/::after sur
     `body` lui-même, donc pas atteints par `body *` ci-dessus. */
  body::before, body::after { visibility: hidden !important; }
  /* `visibility: hidden` masque le reste de la page mais ne le retire pas
     du flux — resté à sa hauteur normale (toute la page organisateur),
     ça dépassait la hauteur d'une page A4 et imprimait une deuxième page
     blanche derrière l'affiche. Aplati à hauteur 0 (et débordement coupé)
     pour ne laisser que l'affiche, qui n'en dépend plus une fois sortie du
     flux ci-dessous (`position: fixed`). */
  body { height: 0; overflow: hidden; }
  .qr-poster, .qr-poster * { visibility: visible; }
  .qr-poster {
    /* `fixed` plutôt que le `absolute` d'avant : ancrée directement à la
       page d'impression (100% = la feuille A4 posée par `@page` ci-dessus)
       plutôt qu'à un ancêtre — et donc bien en 210×297mm plutôt qu'un petit
       pavé de 380px perdu au milieu de la page. */
    position: fixed; inset: 0; width: 100%; height: 100%;
    box-sizing: border-box; margin: 0; max-width: none; padding: 20mm;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1.5rem;
    box-shadow: none; border: 3px solid var(--accent);
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  /* Le QR doit dominer la page — c'est lui qu'on scanne de loin, tout le
     reste (blason, titre, url) n'est là qu'en repère secondaire. Blason et
     titre réduits pour lui laisser la place plutôt que de rester à leur
     taille "carte d'aperçu" agrandie telle quelle sur une A4 entière. */
  .qr-poster-crest { width: 44mm; margin: 0; }
  .qr-poster-watermark { width: 140mm; max-width: none; }
  .qr-poster-title { font-size: 1.9rem; }
  .qr-poster-tagline { font-size: 1rem; }
  .qr-poster-imgwrap { padding: 12mm; }
  .qr-poster-img { width: 140mm; height: 140mm; }
  .qr-poster-url { font-size: 1.05rem; }
  .qr-poster-brand { right: 12mm; bottom: 10mm; font-size: 0.85rem; }
}

/* ---------- Diplômes (admin, onglet Diplômes d'une activité terminée) ----------
   Le canvas est dessiné en pixels fixes (1600x1131, voir admin.js) puis
   affiché à taille réduite ici (`width: 100%` sur une carte étroite) — le
   fichier téléchargé garde sa pleine résolution, seul l'aperçu est mis à
   l'échelle. */
.diplomas-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.5rem; padding: 1rem 1.25rem;
}
.diplomas-summary-text { display: flex; align-items: center; gap: 0.9rem; }
.diplomas-summary-title { font-size: 1.2rem; font-weight: 700; }
.diplomas-summary .meta { margin: 0; }
.diplomas-layout { display: grid; gap: 1.75rem; align-items: start; }
@media (min-width: 900px) {
  .diplomas-layout.has-recap { grid-template-columns: 300px minmax(0, 1fr); }
}
.diplomas-main { display: grid; gap: 1.75rem; min-width: 0; }
.diplomas-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.25rem 1rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.diplomas-section-head .group-label { margin: 0; }
.diplomas-section-head p { margin: 0; font-size: 0.88rem; color: var(--muted); }
.diploma-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1rem; }
.diploma-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 0.75rem; display: grid; gap: 0.6rem; align-content: start;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 4px 12px rgba(20, 20, 15, 0.06);
}
.diploma-thumb {
  display: block; width: 100%; min-height: 0; padding: 0; border: 0; border-radius: 4px;
  background: none; cursor: zoom-in;
}
.diploma-thumb:hover { filter: none; }
.diploma-thumb:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.diploma-canvas { width: 100%; height: auto; display: block; border-radius: 4px; box-shadow: 0 0 0 1px var(--border); }
.diploma-card-foot { display: flex; align-items: center; gap: 0.35rem; }
.diploma-card-foot button.ghost { padding: 0 0.7rem; }
.diploma-card-text { flex: 1; min-width: 0; display: grid; line-height: 1.3; }
.diploma-card-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.diploma-card-sub { font-size: 0.85rem; color: var(--muted); }
.diploma-note {
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius-lg); padding: 1rem;
  display: grid; gap: 0.35rem; align-content: center; font-size: 0.9rem; color: var(--muted);
}
.diploma-note strong { color: var(--text); }
.diploma-empty {
  display: flex; align-items: flex-start; gap: 0.9rem; padding: 1.1rem 1.25rem;
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius-lg); background: var(--surface-2);
}
.diploma-empty .todo-icon { flex-shrink: 0; background: var(--surface); color: var(--muted); }
.diploma-empty p { margin: 0.2rem 0 0; color: var(--muted); max-width: 60rem; }

/* Aperçu en grand d'un diplôme (`openDiplomaPreview`) : fenêtre centrée,
   fond `.drawer-backdrop` partagé avec le panneau latéral. */
.diploma-preview {
  position: fixed; z-index: 300; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(1040px, calc(100vw - 2rem)); max-height: calc(100vh - 2rem);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); color: var(--text); border-radius: 20px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
}
.diploma-preview-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.9rem 1rem 0.9rem 1.4rem; border-bottom: 1px solid var(--border);
}
.diploma-preview-head h2 { margin: 0; font-size: 1.3rem; }
.diploma-preview-sub { color: var(--muted); font-size: 0.9rem; }
.diploma-preview-actions { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
.diploma-preview-stage {
  flex: 1; min-height: 0; display: flex; align-items: center; gap: 1rem;
  padding: 1.25rem; background: var(--surface-2);
}
.diploma-preview-frame { flex: 1; min-width: 0; display: flex; justify-content: center; }
.diploma-preview-canvas {
  display: block; width: auto; height: auto; max-width: 100%; max-height: calc(100vh - 13rem);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14);
}
.diploma-preview-nav { flex-shrink: 0; border-radius: 999px; width: 46px; height: 46px; }
.diploma-preview-nav.prev .icon { transform: rotate(180deg); }
.diploma-preview-keys { padding: 0.7rem; border-top: 1px solid var(--border); text-align: center; font-size: 0.85rem; color: var(--muted); }
.diploma-preview-keys kbd {
  font: inherit; font-size: 0.78rem; font-weight: 700; padding: 0.05rem 0.45rem;
  border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface-2); color: var(--text);
}
@media (max-width: 640px), (hover: none) {
  .diploma-preview-keys { display: none; }
}
@media (max-width: 640px) {
  .diploma-preview-stage { padding: 0.75rem 0.4rem; gap: 0.3rem; }
  .diploma-preview-nav { width: 40px; height: 40px; }
  .diploma-preview-head { padding-left: 1rem; }
  .diploma-preview-actions button:not(.icon-btn) { padding: 0 0.8rem; }
}

/* ---------- Connexion organisateur (`showGate`, admin.js) ---------- */
#gate:empty { display: none; }
.gate-card {
  width: min(440px, 100%); box-sizing: border-box; margin: 3.5rem auto 2rem;
  display: flex; flex-direction: column; gap: 1.1rem; padding: 2rem;
  background: var(--surface); border: 1px solid var(--border); border-radius: 20px;
  box-shadow: 0 1px 2px rgba(20, 20, 15, 0.08), 0 8px 24px rgba(20, 20, 15, 0.08);
}
.gate-icon {
  width: 52px; height: 52px; border-radius: 14px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-weak); color: var(--accent);
}
.gate-head { display: grid; gap: 0.35rem; }
.gate-head h2 { margin: 0; font-size: 1.75rem; line-height: 1.1; }
.gate-head p { margin: 0; }
.gate-head strong { color: var(--text); }
.gate-field { display: grid; gap: 0.4rem; }
.gate-field label { color: var(--text); font-weight: 700; font-size: 0.95rem; }
.gate-field .field-hint { margin: 0; }
.gate-input {
  display: flex; align-items: center; padding-right: 4px;
  background: var(--surface); border: 1.5px solid var(--border-strong); border-radius: var(--radius);
}
.gate-input:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.gate-input input { border: 0; min-height: 46px; font-size: 1rem; letter-spacing: 0.06em; background: transparent; }
.gate-input input:focus-visible { outline: none; }
.gate-input:has(input[aria-invalid="true"]) { border-color: var(--danger); border-width: 2px; }
.gate-input button { color: var(--muted); flex-shrink: 0; }
.gate-card > button[type="submit"] { min-height: 48px; font-size: 1rem; }
.gate-alert {
  display: flex; gap: 0.65rem; padding: 0.75rem 0.9rem; border-radius: 12px;
  background: color-mix(in srgb, var(--danger) 10%, var(--surface)); color: var(--danger);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--surface));
}
.gate-alert .icon { flex-shrink: 0; margin-top: 1px; }
.gate-alert > div { display: grid; gap: 0.1rem; }
.gate-alert span { font-size: 0.9rem; }
.gate-foot {
  display: flex; align-items: flex-start; gap: 0.6rem; margin: 0; padding-top: 1rem;
  border-top: 1px solid var(--border); font-size: 0.88rem; color: var(--muted);
}
.gate-foot .icon { flex-shrink: 0; margin-top: 1px; }
.gate-foot a { font-weight: 700; color: var(--accent); }
@media (max-width: 520px) {
  .gate-card { margin-top: 1.25rem; padding: 1.4rem 1.25rem; border-radius: 18px; }
  .gate-head h2 { font-size: 1.55rem; }
}

/* ---------- Inscription publique (register.html, `registerTeamForm`) ---------- */
.register-page { display: grid; gap: 1rem; }
.register-intro { display: grid; gap: 0.5rem; justify-items: start; }
.register-intro p { margin: 0; }
.register-open { font-size: 0.8rem; }
.register-open .live-dot { background: currentColor; animation: none; width: 7px; height: 7px; }
.register-form { display: grid; gap: 1.25rem; }
.register-field { display: grid; gap: 0.4rem; }
.register-field .field-hint { margin: 0; }
.register-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.register-form label, .register-members legend { color: var(--text); font-weight: 700; font-size: 0.95rem; }
.register-counter { font-size: 0.82rem; color: var(--muted); }
.register-form input { min-height: 48px; font-size: 1rem; border-width: 1.5px; }
#register-team-name { font-size: 1.05rem; font-weight: 600; }
.register-members { margin: 0; padding: 0; border: 0; display: grid; gap: 0.5rem; min-width: 0; }
.register-members legend { padding: 0; margin-bottom: 0.4rem; }
.register-optional { font-weight: 500; color: var(--muted); }
.register-members .member-row button { width: 46px; min-height: 46px; color: var(--muted); flex-shrink: 0; }
.register-add { justify-self: start; padding: 0 0.3rem; }
.register-submit { width: 100%; min-height: 52px; font-size: 1.05rem; border-radius: 12px; }
.register-again { width: 100%; min-height: 48px; border-radius: 12px; justify-content: center; }
.register-foot { margin: 0; font-size: 0.85rem; color: var(--muted); text-align: center; }
.status-card { display: grid; justify-items: center; gap: 0.75rem; text-align: center; padding: 1.75rem 1.25rem; }
.status-card h3 { margin: 0; font-size: 1.6rem; line-height: 1.15; outline: none; }
.status-card p { margin: 0; max-width: 34rem; }
.status-card-icon {
  width: 64px; height: 64px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-weak); color: var(--accent);
}
.status-card-icon.muted { background: var(--surface-2); color: var(--muted); }
.register-recap {
  width: 100%; box-sizing: border-box; display: grid; gap: 0.15rem; text-align: left;
  padding: 0.75rem 0.9rem; border-radius: 12px; background: var(--surface-2);
}
.register-recap .meta { margin: 0; }
