/* ═══════════════════════════════════════════════════════════════════════════
   PETANQUE BELGE (Tires et Pointes) — MODE TV
   La television de la buvette. Pas une page web : un panneau d'affichage.

   Quatre partis pris commandent tout ce fichier.

   1. AUCUNE COUCHE. app.css declare @layer socle/composants/pages/utilitaires ;
      une regle hors couche l'emporte sur toutes les regles en couche, quelle
      que soit sa specificite. Ce fichier reste donc volontairement hors
      couche : il gagne sur le site sans avoir a surencherir en selecteurs.

   2. TOUT EN UNITES DE FENETRE. Les tailles sont en vh borne par clamp() :
      la meme page remplit un 43 pouces et un 24 pouces, et se replie sans
      casse en 1280x720 ou sur une tablette.

   3. FOND NUIT, TEXTE CLAIR, OR EN ACCENT. L'or #F5B41C ne descend qu'a
      1,9:1 sur blanc — inutilisable en texte clair. Sur le bleu abysse il
      monte a pres de 10:1 et il chante. Aucun texte or sur fond clair ici.

   4. L'ECHELLE EST UNE ENTREE, PAS UNE CONSTANTE. Un 3vh fait 11,6 mm de
      capitale sur un 43 pouces : lisible a deux metres cinquante, illisible
      du comptoir. La bonne taille depend de deux mesures que seule la
      buvette connait — la diagonale de la dalle et la longueur de la salle.
      Le facteur --tv-echelle arrive donc du calibrage (Controllers/Tv.php
      fait le calcul, quinze secondes au premier allumage) et TOUTE la
      typographie de contenu se multiplie par lui. A 1, l'ecran est celui
      d'hier, au pixel pres.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─────────────────────────────────────────────────────────────── JETONS ── */

.tv {
  /* Jetons de marque repris de app.css, avec un repli en dur : si la feuille
     du site n'est pas arrivee, l'ecran garde ses couleurs au lieu de virer
     au noir sur noir. */
  --tv-or:        var(--jaune, #F5B41C);
  --tv-or-vif:    var(--jaune-vif, #FFCE4A);
  --tv-nuit:      var(--bleu-nuit, #101838);
  --tv-abysse:    var(--bleu-abysse, #0A1028);
  --tv-papier:    #FAF3E3;   /* l'ivoire du site — la craie de cette ardoise */

  --tv-encre:     #F3F6FF;   /* 17:1 sur l'abysse */
  --tv-encre-2:   #A3AED4;   /*  7:1 sur l'abysse */
  --tv-encre-3:   #6E7AA4;   /*  4:1, decor uniquement */
  --tv-trait:     #1D2A5C;
  --tv-panneau:   #131C42;
  --tv-vert:      #4CD69A;   /* 11:1 — le vert du site est trop sombre ici */
  --tv-ambre:     #FFCE4A;

  --tv-titre: "Big Shoulders Display", "Archivo Narrow", "Arial Narrow", Impact, sans-serif;
  --tv-texte: Archivo, "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;

  --tv-gouttiere: clamp(.9rem, 2.2vh, 2.4rem);
  --tv-fondu:     700ms;
  /* La Louche : la courbe unique du motion de la marque. Part vite, se pose
     doucement — le geste du bras qui donne. */
  --tv-louche:    cubic-bezier(.22, .61, .36, 1);
}

/*
 * L'ECHELLE, ET LES CORPS QUI EN DECOULENT.
 *
 * Ce bloc est declare sur DEUX selecteurs, et ce n'est pas une coquetterie :
 * une propriete personnalisee resout ses var() la ou elle est DECLAREE, puis
 * s'herite toute resolue. Declares sur body seul, ces corps seraient figes a
 * l'echelle du body partout — y compris dans l'apercu du panneau de reglage,
 * qui doit precisement montrer une AUTRE echelle que celle en cours. En les
 * redeclarant sur .tv-apercu, l'apercu les recalcule avec sa propre valeur
 * de --tv-echelle, posee par tv.js au fil des choix.
 *
 * Les plafonds sont en vh, plus en rem : un plafond en rem etait un mur
 * absolu que le facteur d'echelle ne pouvait pas franchir — calibrer un
 * ecran pour dix metres n'aurait rien change du tout. En vh, le plafond
 * reste ce qu'il doit etre : une part maximale de la dalle, pas des pixels.
 *
 * --tv-echelle-tete est volontairement amortie : l'entete, le titre de vue
 * et le pied sont lus une fois ou de pres, pas epeles depuis le comptoir.
 * S'ils grandissaient au meme rythme que les noms d'equipes, le chrome
 * mangerait les lignes — et les lignes sont la seule chose qui compte.
 */
.tv,
.tv-apercu {
  --tv-echelle: 1;
  --tv-echelle-tete: calc(1 + (var(--tv-echelle) - 1) * .45);

  --tv-t-titre:   clamp(1.7rem,  calc(4.6vh  * var(--tv-echelle-tete)), 12vh);
  --tv-t-equipe:  clamp(1.05rem, calc(3vh    * var(--tv-echelle)),      10.5vh);
  --tv-t-score:   clamp(1.5rem,  calc(4.1vh  * var(--tv-echelle)),      13vh);
  --tv-t-rang:    clamp(1.2rem,  calc(3.4vh  * var(--tv-echelle)),      11.5vh);
  --tv-t-menu:    clamp(.72rem,  calc(1.85vh * var(--tv-echelle)),      6vh);
}

/* body porte l'echelle en style en ligne (posee par le serveur, ajustee par
   tv.js) : elle est donc deja la avant le premier octet de JavaScript. */

/* ───────────────────────────────────────────────────────────────── SOCLE ── */

html:has(> body.tv) { overflow: hidden; }

body.tv {
  margin: 0;
  /* dvh d'abord pour les navigateurs recents, vh en repli pour les vieilles
     box Android qui ne connaissent pas l'unite dynamique. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--tv-abysse);
  background-image: linear-gradient(174deg, var(--tv-nuit) 0%, var(--tv-abysse) 62%);
  color: var(--tv-encre);
  font-family: var(--tv-texte);
  font-size: 16px;
  line-height: 1.2;
  -webkit-font-smoothing: antialiased;
}

/* Personne n'a de souris devant cette television, et un curseur oublie au
   milieu de l'ecran s'y voit pendant huit heures. Les deux exceptions sont
   plus bas : la page de choix et le panneau de reglage, les deux seuls
   endroits du mode TV ou une main touche quelque chose. */
body.tv,
body.tv * { cursor: none; }

body.tv ::-webkit-scrollbar { width: 0; height: 0; }
body.tv * { scrollbar-width: none; }

body.tv [hidden] { display: none !important; }

/*
 * Colonne souple, et non une grille a quatre rangs : le bandeau de panne est
 * masque la plupart du temps, et sur une grille positionnelle sa disparition
 * decalait tout d'un rang — le pied de page heritait de la rangee souple et
 * le corps s'ecrasait a zero. En colonne flexible, un enfant masque ne laisse
 * ni rangee ni gouttiere derriere lui.
 */
.tv-scene {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--tv-gouttiere) clamp(1rem, 2.4vw, 3rem);
  gap: clamp(.5rem, 1.4vh, 1.4rem);
  box-sizing: border-box;
}

.tv-tete,
.tv-bandeau,
.tv-pied { flex: 0 0 auto; }

/* ───────────────────────────────────────────────────────────────── ENTETE ── */

.tv-tete {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(1rem, 2.6vw, 3rem);
  padding-bottom: clamp(.4rem, 1.1vh, 1rem);
  border-bottom: 2px solid var(--tv-trait);
}

/* La marque reste a l'ecran en permanence, discrete : c'est le circuit qui
   affiche, pas le club, et un ecran photographie doit rester identifiable. */
.tv-marque {
  display: flex;
  align-items: center;
  gap: clamp(.4rem, .9vw, .9rem);
}

/*
 * LA BOULE D'ACIER. La marque du circuit est la boule de petanque, pas une
 * pastille d'or : l'or, c'est le pastis, et il reste dans le logo du site.
 * Trois couches, du fond vers l'avant : le metal froid en diagonale, le
 * reflet chaud de la buvette qui s'y mire (l'or a 55 %, en bas a droite),
 * et l'eclat blanc du neon. En rgba et non en oklch : la box Android de la
 * buvette a le droit d'etre vieille, le degrade doit passer partout.
 *
 * Jamais deux boules cote a cote dans un meme bloc — sauf le podium, qui est
 * l'exception fondatrice : trois boules, trois marches.
 */
.tv-boule-acier {
  width: 1em;
  aspect-ratio: 1;
  border-radius: 50%;
  flex: none;
  background:
    radial-gradient(circle at 34% 26%, #FFFFFF 0 6%, #DDE3EE 12%, rgba(221, 227, 238, 0) 32%),
    radial-gradient(circle at 68% 78%, rgba(245, 180, 28, .55) 0 12%, rgba(245, 180, 28, 0) 44%),
    linear-gradient(158deg, #9AA3B8 0%, #6E778C 38%, #454C60 72%, #2A3042 100%);
  box-shadow: 0 2px 3px rgba(5, 8, 27, .35), 0 8px 16px -8px rgba(5, 8, 27, .5);
}

.tv-boule-acier--marque { font-size: clamp(1.4rem, calc(3.4vh * var(--tv-echelle-tete)), 7vh); }

.tv-marque-mot {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(.95rem, calc(2.4vh * var(--tv-echelle-tete)), 5vh);
  line-height: .9;
  letter-spacing: .02em;
  color: var(--tv-encre);
  display: grid;
}

.tv-marque-mot small {
  font-family: var(--tv-texte);
  font-weight: 800;
  font-size: .42em;
  letter-spacing: .3em;
  color: var(--tv-or);
}

.tv-journee { min-width: 0; text-align: center; }

.tv-journee-nom {
  margin: 0;
  font-family: var(--tv-titre);
  font-weight: 800;
  font-size: clamp(1.15rem, calc(3.2vh * var(--tv-echelle-tete)), 6.5vh);
  /* Au-dessus de 1 : le debordement masque, indispensable pour les points de
     suspension, rognait sinon l'accent des capitales — « Écran » perdait le
     sien. Les capitales accentuees se portent, elles ne se coupent pas. */
  line-height: 1.14;
  letter-spacing: .01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-journee-lieu {
  margin: .15em 0 0;
  /* LA LIGNE SE SERRE PLUTOT QUE DE SE COUPER.

     Au calibrage « du fond de la buvette », « Samedi 5 septembre 2026 ·
     Petanque Longeau · Longeau » ne tenait plus et se terminait par
     « Petanque Lo… ». Un nom de club tronque sur le mur du club, c'est la
     premiere chose que quelqu'un remarque. La taille suit donc la largeur
     disponible autant que le calibrage : elle descend d'un cran quand il le
     faut, et rien ne se perd. */
  font-size: min(var(--tv-t-menu), 2.35vw);
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--tv-encre-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-tour {
  display: flex;
  align-items: baseline;
  gap: .35em;
  padding: .12em .7em .2em;
  border-radius: 999px;
  background: var(--tv-or);
  color: var(--tv-nuit);
  font-weight: 800;
}

.tv-tour-mot   { font-size: var(--tv-t-menu); text-transform: uppercase; letter-spacing: .12em; }
.tv-tour-total { font-size: var(--tv-t-menu); opacity: .8; }

.tv-tour-chiffre {
  font-family: var(--tv-titre);
  font-size: clamp(1.3rem, calc(3.6vh * var(--tv-echelle-tete)), 7vh);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────────── LE BANDEAU ── */

/*
 * Il ne dit jamais que tout va bien. Il n'apparait que pour annoncer l'age
 * de ce qui est affiche, et il le fait en grand : un ecran qui montre un
 * score perime avec aplomb est pire qu'un ecran eteint.
 */
.tv-bandeau {
  margin: 0;
  padding: clamp(.4rem, 1.2vh, 1rem) clamp(.8rem, 1.8vw, 1.8rem);
  border-radius: 8px;
  background: var(--tv-or);
  color: var(--tv-nuit);
  font-weight: 800;
  font-size: clamp(.95rem, calc(2.5vh * var(--tv-echelle-tete)), 5.5vh);
  letter-spacing: .01em;
  text-align: center;
}

.tv-bandeau.est-grave {
  background: #C4361C;
  color: #FFFFFF;
}

/* ───────────────────────────────────────────────────────────────── VUES ── */

.tv-corps {
  position: relative;
  flex: 1 1 auto;
  /* Indispensable : sans lui, un enfant souple refuse de descendre sous la
     hauteur de son contenu et la pagination ne peut pas mesurer le cadre. */
  min-height: 0;
  overflow: hidden;
}

/*
 * Les vues sont empilees au meme endroit : le passage de l'une a l'autre est
 * un fondu, pas un carrousel. Des gens mangent en dessous.
 *
 * Colonne souple et non grille a rangs fixes : le rang du milieu — decompte
 * du tour sur le plateau, note des equipes mixtes sur les classements du
 * jour — n'existe que sur certaines vues, et une grille positionnelle
 * laisserait un rang vide derriere lui partout ailleurs.
 */
.tv-vue {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(.3rem, 1vh, 1rem);
  opacity: 0;
  transition: opacity var(--tv-fondu) ease-in-out;
}

.tv-vue.est-active { opacity: 1; }

/*
 * Le titre est un rang, pas une ligne : le libelle a gauche, le repere de
 * pagination a droite. « Places 22 a 28 · page 4 sur 14 » est ce qui
 * transforme une liste qu'on subissait en liste qu'on consulte — le joueur
 * qui se sait vingt-cinquieme sait exactement quand lever les yeux.
 */
.tv-titre {
  margin: 0;
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1em;
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: var(--tv-t-titre);
  line-height: .95;
  letter-spacing: .015em;
  color: var(--tv-or);
  text-transform: uppercase;
}

.tv-titre-mot {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* « Classement general · dames » finissait en « Classement general · D… ».
     Le mot coupe est justement celui qui dit de QUI on parle : sans lui, le
     panneau des dames et celui des hommes se ressemblent. La taille cede
     avant le texte. */
  font-size: min(1em, 3.4vw);
}

.tv-titre-suite {
  color: var(--tv-encre-2);
  font-weight: 700;
  font-size: .58em;
  letter-spacing: .06em;
  margin-left: .5em;
}

.tv-pages {
  flex: none;
  font-family: var(--tv-texte);
  font-size: clamp(.72rem, calc(1.85vh * var(--tv-echelle-tete)), 4vh);
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--tv-encre-2);
  font-variant-numeric: tabular-nums;
}

/*
 * LE DECOMPTE DU TOUR — la reponse a la question du comptoir : on repart
 * quand ? Encre pleine tant qu'il reste des rencontres ; il passe a l'or
 * quand tout est rentre et que le tirage va tomber. C'est le seul moment ou
 * cette ligne a le droit de crier.
 */
.tv-decompte {
  margin: 0;
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .2em 1.2em;
  font-size: clamp(.85rem, calc(2.2vh * var(--tv-echelle)), 6.5vh);
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--tv-encre);
}

.tv-decompte.est-imminent .tv-decompte-mot { color: var(--tv-or); text-transform: uppercase; letter-spacing: .05em; }

.tv-decompte-quand {
  font-size: .72em;
  font-weight: 600;
  color: var(--tv-encre-2);
  white-space: nowrap;
}

/* La note sous un titre : une phrase, jamais deux. Elle porte l'explication
   des equipes mixtes — l'ecran repond a la question avant qu'on la pose. */
.tv-note {
  margin: 0;
  flex: 0 0 auto;
  font-size: var(--tv-t-menu);
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--tv-encre-2);
}

.tv-note--centre { text-align: center; }

.tv-cadre {
  flex: 1 1 auto;
  display: grid;
  align-content: start;
  min-height: 0;
  overflow: hidden;
}

/* Les deux vues qui ne portent qu'un bloc, et non une liste, se centrent dans
   la hauteur disponible plutot que de se tasser sous le titre. */
.tv-vue[data-vue="attente"] .tv-cadre,
.tv-vue[data-vue="prochaine"] .tv-cadre { align-content: center; }

/*
 * LA PISTE, ET LA PAGE QUI TOURNE.
 *
 * Une liste trop longue ne defile plus : elle tourne, page par page, par
 * rangees ENTIERES. Le glissement continu obligeait a lire un texte en
 * mouvement — la pire condition de lecture qui soit, a plus forte raison a
 * six metres — et le masque en degrade qui adoucissait la coupe est parti
 * avec lui : quand la page ne coupe aucune ligne, il n'y a rien a masquer.
 *
 * Le mouvement lui-meme reste aux deux seules proprietes que le materiel
 * faible d'une buvette encaisse sans saccade : transform et opacity. La
 * choregraphie (la page se souleve, la suivante se pose) est menee par
 * tv.js en styles en ligne, sur la courbe --tv-louche.
 */
.tv-piste {
  will-change: transform, opacity;
  transform: translate3d(0, 0, 0);
}

/* ────────────────────────────────────────────────────────────── LIGNES ── */

/*
 * La hauteur d'une ligne suit le corps du texte, plus une constante d'air.
 * C'etait un vh fixe : en grandissant, le texte aurait creve sa ligne — et
 * l'air, lui, n'a aucune raison de grandir au meme rythme que les lettres.
 * A l'echelle 1, le resultat est la hauteur historique, au pixel pres.
 */
.tv-ligne {
  display: grid;
  align-items: center;
  gap: clamp(.5rem, 1.3vw, 1.6rem);
  grid-template-columns:
    clamp(2.6rem, 5vw, 6.4rem)   /* terrain */
    1fr                          /* equipe A */
    auto                         /* score   */
    1fr                          /* equipe B */
    minmax(5.5rem, auto);        /* etat    */
  min-height: calc(var(--tv-t-equipe) * 1.3 + clamp(.35rem, 1.9vh, 1.5rem));
  padding: clamp(.15rem, .55vh, .55rem) clamp(.4rem, .9vw, 1rem);
  border-bottom: 1px solid var(--tv-trait);
}

.tv-ligne:last-child { border-bottom: 0; }

/*
 * LA PETITE AFFICHE. Une mêlée du mardi tient en quatre rencontres : sur un
 * 43 pouces, elles occupaient le tiers haut et laissaient un mur d'émail
 * vide. Quand la piste porte SIX lignes ou moins, elles se répartissent sur
 * toute la hauteur et grandissent d'un cran — l'écran redevient une affiche.
 * Une vraie journée (trente lignes) ne passe jamais par ici : :has() ne
 * s'arme que si la 6e ligne au plus est aussi la dernière. Les navigateurs
 * sans :has() gardent l'affichage compact : rien ne casse.
 */
.tv-vue[data-vue="plateau"] .tv-cadre:has(.tv-piste > .tv-ligne:nth-child(-n+6):last-child) {
  display: flex;
  flex-direction: column;
}
.tv-vue[data-vue="plateau"] .tv-cadre:has(.tv-piste > .tv-ligne:nth-child(-n+6):last-child) .tv-piste {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
}
.tv-piste:has(> .tv-ligne:nth-child(-n+6):last-child) .tv-ligne {
  border-bottom: 0;
  font-size: 1.18em;
  padding-block: clamp(.6rem, 2.2vh, 2.4rem);
}

.tv-terrain {
  display: inline-grid;
  place-items: center;
  min-width: 1.9em;
  padding: .06em .3em;
  border-radius: 6px;
  background: var(--tv-panneau);
  color: var(--tv-or);
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1rem, calc(2.7vh * var(--tv-echelle)), 9vh);
  font-variant-numeric: tabular-nums;
}

.tv-equipe {
  /* Un nom de vingt-cinq lettres se serre d'un cran plutot que de perdre sa
     fin : « Jean-Marc LONGUEHALEINE » vaut mieux que « Jean-Marc LONGUEHA… »
     sur un ecran que personne ne peut faire defiler. */
  font-size: min(var(--tv-t-equipe), 3.1vw);
  font-weight: 600;
  letter-spacing: .005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* L'equipe de gauche se serre contre le score : sans cela le regard traverse
   un vide de plusieurs centimetres entre le nom et le resultat. */
.tv-ligne:not(.tv-ligne--rang) > .tv-equipe:nth-child(2) { text-align: right; }

.tv-equipe--pale { color: var(--tv-encre-3); font-style: italic; }
.tv-equipe.est-gagnant { font-weight: 800; color: var(--tv-encre); }
.tv-ligne--resultat .tv-equipe:not(.est-gagnant) { color: var(--tv-encre-2); }

/* Les chiffres ne doivent pas danser d'une ligne a l'autre : chasse fixe. */
.tv-score {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: var(--tv-t-score);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: .01em;
  white-space: nowrap;
  color: var(--tv-encre);
}

.tv-score b { font-weight: 900; display: inline-block; min-width: 1.15ch; text-align: center; }
.tv-score i { font-style: normal; color: var(--tv-encre-3); margin: 0 .18em; }
.tv-score--vide { color: var(--tv-encre-3); }

/* Deux lignes plutot qu'une : « TERMINÉ · il y a 14 min » sur un seul rang
   depassait la colonne et se coupait en « il y a… », qui n'apprend rien. */
.tv-etat {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  line-height: 1.15;
  font-size: var(--tv-t-menu);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tv-encre-2);
  text-align: right;
  overflow: hidden;
}

.tv-age {
  font-weight: 600;
  font-size: .88em;
  text-transform: none;
  letter-spacing: .01em;
  color: var(--tv-encre-3);
  white-space: nowrap;
}

/* Statut d'une rencontre — gris en attente, clair en cours, vert termine. */
/* Assez pale pour se distinguer d'une rencontre lancee, assez dense pour
   rester lisible a cinq metres : sous .7 d'opacite, le gris de l'etat tombe
   a deux pour un sur le fond nuit et disparait. */
.tv-ligne--attente  { opacity: .74; }
.tv-ligne--attente .tv-etat { color: var(--tv-encre-2); }
.tv-ligne--en_cours { background: rgba(245, 180, 28, .06); }
.tv-ligne--en_cours .tv-etat { color: var(--tv-or); }
.tv-ligne--termine  .tv-score { color: var(--tv-vert); }
.tv-ligne--termine  .tv-etat  { color: var(--tv-vert); }
.tv-ligne--propose  .tv-score { color: var(--tv-ambre); }
.tv-ligne--propose  .tv-etat  { color: var(--tv-ambre); }
.tv-ligne--resultat .tv-score { color: var(--tv-vert); }

/*
 * LES TROIS TEMPERATURES D'UNE RENCONTRE QUI BASCULE.
 *
 * est-chaud     il reste un point a mettre et un seul point d'ecart : la
 *               buvette se retourne. Discret, jamais clignotant — huit
 *               heures d'affilee, un clignotement est une punition.
 * est-fanny-vue l'un des deux est encore a zero et l'autre touche au but :
 *               ca sent la Fanny, le lisere s'allume, rien de plus. Elle
 *               n'existe pas encore.
 * est-fanny     c'est fait — treize a zero au tableau. Le badge le dit en
 *               toutes lettres et il RESTE : l'exploit qu'on chante ne
 *               s'efface pas au moment precis ou il se produit, c'etait
 *               tout le bug.
 */
.tv-ligne.est-chaud     { background: rgba(245, 180, 28, .14); box-shadow: inset 3px 0 0 var(--tv-or); }
.tv-ligne.est-fanny-vue { box-shadow: inset 3px 0 0 rgba(76, 214, 154, .55); }
.tv-ligne.est-fanny     { background: rgba(76, 214, 154, .09); box-shadow: inset 3px 0 0 var(--tv-vert); }

/* Le badge : une pilule, le mot, le score. Il ne nomme personne — les noms
   sont deja sur la ligne, le badge marque le score, pas le perdant. */
.tv-fanny {
  display: inline-flex;
  align-items: baseline;
  gap: .35em;
  padding: .08em .55em .14em;
  border-radius: 999px;
  background: var(--tv-vert);
  color: var(--tv-nuit);
  font-weight: 900;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.tv-fanny b { font-family: var(--tv-titre); font-weight: 900; font-size: 1.25em; }

/* Le point qui vient de tomber : la ligne s'illumine une seconde. Sans ca,
   l'ecran a l'air mort meme quand il est a jour. */
.tv-ligne.est-neuve { animation: tv-eclair 1.6s ease-out 1; }

@keyframes tv-eclair {
  0%   { background: rgba(255, 206, 74, .42); }
  100% { background: transparent; }
}

/* ─────────────────────────────────────────────────────────── CLASSEMENTS ── */

.tv-ligne--rang {
  grid-template-columns:
    /* DEUX CHIFFRES, PAS UN.
       La colonne etait taillee sur « 9 » : des la dixieme place, le rang
       debordait sur le prenom et les deux se touchaient. Le classement d'un
       circuit de six cents licencies passe surtout du 10e au 30e rang. */
    minmax(min-content, clamp(2.6rem, 5vw, 6rem))   /* place  */
    1fr                                             /* nom    */
    auto                                            /* club   */
    minmax(3rem, auto);                             /* chiffre */
  min-height: calc(var(--tv-t-rang) * 1.2 + clamp(.3rem, 1.4vh, 1.2rem));
}

/* Le classement du jour n'a pas de colonne club : la quatrieme cellule y est
   un second chiffre, l'average. */
.tv-vue[data-vue^="jour-"] .tv-ligne--rang {
  grid-template-columns:
    minmax(min-content, clamp(2.6rem, 5vw, 6rem))
    1fr
    minmax(3rem, auto)
    minmax(3.4rem, auto);
}

.tv-place {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: var(--tv-t-rang);
  font-variant-numeric: tabular-nums;
  color: var(--tv-encre-3);
  text-align: right;
}

.tv-ligne--rang.est-podium .tv-place { color: var(--tv-or); }
.tv-ligne--rang.est-podium .tv-equipe { font-weight: 800; }

.tv-club {
  font-size: var(--tv-t-menu);
  font-weight: 800;
  letter-spacing: .1em;
  color: var(--tv-encre-3);
  text-align: right;
}

.tv-chiffre {
  font-family: var(--tv-titre);
  font-weight: 800;
  font-size: var(--tv-t-rang);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--tv-encre-2);
  text-align: right;
}

.tv-chiffre--fort { color: var(--tv-or); font-weight: 900; }

.tv-chiffre small {
  font-family: var(--tv-texte);
  font-weight: 800;
  font-size: .48em;
  letter-spacing: .08em;
  margin-left: .12em;
}

.tv-sous-titre {
  margin: 0 0 clamp(.2rem, .7vh, .7rem);
  font-family: var(--tv-titre);
  font-weight: 800;
  font-size: clamp(1rem, calc(2.6vh * var(--tv-echelle-tete)), 6vh);
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--tv-encre-2);
  border-bottom: 2px solid var(--tv-trait);
  padding-bottom: .18em;
}

/* ─────────────────────────────────────────────────────────────── PODIUM ── */

/*
 * LA REMISE DES PRIX. La vue n'existe que journee close : c'est le pic du
 * dimanche, le seul moment ou toute la salle regarde dans la meme direction.
 *
 * Le seuil de bascule des colonnes est multiplie par l'echelle : a l'echelle
 * du comptoir, deux series cote a cote couperaient les noms en deux — la
 * grille passe alors seule en une colonne, sans media query, parce que le
 * point de rupture suit la taille du texte et non celle de l'ecran.
 */
.tv-podium {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(24rem * var(--tv-echelle))), 1fr));
  gap: clamp(.6rem, 1.6vh, 1.6rem) clamp(1rem, 3vw, 4rem);
  align-content: start;
}

.tv-podium-serie { min-width: 0; }

.tv-ligne--podium {
  grid-template-columns:
    auto                          /* la boule  */
    clamp(2.6rem, 5vw, 7rem)      /* 1re, 2e…  */
    1fr                           /* l'equipe  */
    minmax(3rem, auto)            /* victoires */
    minmax(3rem, auto);           /* average   */
  min-height: calc(var(--tv-t-rang) * 1.25 + clamp(.3rem, 1.4vh, 1.2rem));
}

/* Trois boules, trois marches : la premiere pleine, les deux autres plus
   petites et plus sourdes. C'est l'unique bloc du circuit ou plusieurs
   boules se cotoient. */
.tv-ligne--podium .tv-boule-acier { font-size: calc(var(--tv-t-rang) * .8); }
.tv-ligne--podium .tv-boule-acier[data-place="2"] { font-size: calc(var(--tv-t-rang) * .62); opacity: .85; }
.tv-ligne--podium .tv-boule-acier[data-place="3"] { font-size: calc(var(--tv-t-rang) * .52); opacity: .7; }

.tv-rang-mot {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: var(--tv-t-rang);
  font-variant-numeric: tabular-nums;
  color: var(--tv-encre-2);
  text-align: right;
}

.tv-rang-mot sup { font-size: .5em; font-weight: 800; }

.tv-ligne--podium .tv-equipe { font-weight: 700; }
.tv-ligne--podium.est-premiere .tv-equipe { font-weight: 800; font-size: calc(var(--tv-t-equipe) * 1.12); }
.tv-ligne--podium.est-premiere .tv-rang-mot { color: var(--tv-or); }

/*
 * LE TROUBLE. Une seconde signature de la marque : l'or du nom des
 * vainqueurs se trouble vers l'ivoire en six cents millisecondes — le
 * pastis qui rencontre l'eau. Il est reserve a la consecration : podium et
 * tournee, sur cet ecran. Nulle part ailleurs.
 *
 * @property permet d'animer le pourcentage lui-meme ; la premiere
 * declaration color est le repli pour la box qui ignore color-mix — elle
 * garde alors l'or, sans transition, et ne perd rien d'essentiel : l'or sur
 * l'abysse tient a 10:1, l'ivoire a 15:1, les deux etats sont lisibles.
 */
@property --tv-trouble {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

.tv-consacre {
  --tv-trouble: 0%;
  color: var(--tv-or);
  color: color-mix(in oklab, var(--tv-or), var(--tv-papier) var(--tv-trouble));
  transition: --tv-trouble 600ms var(--tv-louche);
}

.tv-consacre.est-trouble { --tv-trouble: 100%; }

.tv-tournee {
  grid-column: 1 / -1;
  margin: clamp(.3rem, 1.2vh, 1.2rem) 0 0;
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.3rem, calc(3.8vh * var(--tv-echelle)), 11vh);
  line-height: 1.02;
  text-transform: uppercase;
  letter-spacing: .02em;
  text-align: center;
  text-wrap: balance;
}

/* Le grand sous-bock de la cloture : le seul moment ou un code se flashe
   vraiment de loin, parce que toute la salle est deja tournee vers l'ecran. */
.tv-flashe {
  grid-column: 1 / -1;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: clamp(.8rem, 2vw, 2rem);
  margin-top: clamp(.3rem, 1vh, 1rem);
}

.tv-flashe-code {
  width: clamp(6.5rem, 24vh, 30vh);
  height: auto;
  display: block;
  border-radius: 10px;
}

.tv-flashe-mot {
  margin: 0;
  display: grid;
  gap: .1em;
  font-size: clamp(.9rem, calc(2.3vh * var(--tv-echelle-tete)), 5vh);
  font-weight: 800;
  letter-spacing: .03em;
  color: var(--tv-encre);
  text-transform: uppercase;
}

.tv-flashe-mot span { color: var(--tv-or); font-family: var(--tv-titre); font-weight: 900; font-size: 1.35em; }

/* ──────────────────────────────────────────────────────────── ANNONCES ── */

.tv-annonce {
  padding: clamp(.6rem, 1.8vh, 1.8rem) clamp(.8rem, 1.8vw, 2rem);
  margin-bottom: clamp(.5rem, 1.5vh, 1.5rem);
  border-radius: 10px;
  border-left: 6px solid var(--tv-encre-3);
  background: var(--tv-panneau);
}

.tv-annonce--urgent      { border-left-color: #E2563A; }
.tv-annonce--meteo       { border-left-color: var(--tv-or); }
.tv-annonce--convocation { border-left-color: var(--tv-vert); }

.tv-etiquette {
  display: inline-block;
  font-size: var(--tv-t-menu);
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--tv-encre-2);
}

.tv-annonce-titre {
  margin: .15em 0 .2em;
  font-family: var(--tv-titre);
  font-weight: 800;
  font-size: clamp(1.2rem, calc(3.3vh * var(--tv-echelle)), 9vh);
  line-height: 1.02;
  color: var(--tv-encre);
}

.tv-annonce-texte {
  margin: 0;
  font-size: clamp(.9rem, calc(2.3vh * var(--tv-echelle)), 7vh);
  line-height: 1.32;
  color: var(--tv-encre-2);
}

/* ───────────────────────────────────────────────── PROCHAINE ET ATTENTE ── */

.tv-rendezvous,
.tv-attente {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: clamp(.2rem, .9vh, .9rem);
}

.tv-rendezvous-date {
  margin: 0;
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.9rem, calc(6.4vh * var(--tv-echelle)), 18vh);
  line-height: .95;
  color: var(--tv-or);
  text-transform: uppercase;
}

.tv-rendezvous-club {
  margin: 0;
  font-family: var(--tv-titre);
  font-weight: 800;
  font-size: clamp(1.3rem, calc(4vh * var(--tv-echelle)), 12vh);
  line-height: 1;
}

.tv-rendezvous-lieu,
.tv-rendezvous-nom {
  margin: 0;
  font-size: clamp(.9rem, calc(2.4vh * var(--tv-echelle)), 7vh);
  font-weight: 600;
  color: var(--tv-encre-2);
}

.tv-rendezvous-nom { color: var(--tv-encre-3); }

.tv-attente-titre {
  margin: 0;
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.6rem, calc(5.2vh * var(--tv-echelle)), 15vh);
  line-height: 1;
  color: var(--tv-encre);
  text-transform: uppercase;
}

.tv-attente-texte {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(.9rem, calc(2.4vh * var(--tv-echelle)), 7vh);
  color: var(--tv-encre-2);
}

/* ────────────────────────────────────────────────────────────────── PIED ── */

.tv-pied {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto;
  align-items: center;
  gap: clamp(.6rem, 1.6vw, 2rem);
  padding-top: clamp(.3rem, .9vh, .9rem);
  border-top: 2px solid var(--tv-trait);
}

.tv-compte { display: flex; gap: clamp(.4rem, 1vw, 1.2rem); flex-wrap: wrap; }

.tv-jeton {
  font-size: var(--tv-t-menu);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tv-encre-3);
  white-space: nowrap;
}

.tv-jeton b {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: 1.5em;
  font-variant-numeric: tabular-nums;
  color: var(--tv-encre-2);
  margin-right: .18em;
}

.tv-jeton--fini b { color: var(--tv-vert); }
.tv-jeton--vif  b { color: var(--tv-or); }

/* Le temps restant sur la vue courante. Seul mouvement permanent de l'ecran,
   et le seul repere qui dit d'un coup d'oeil que la boucle tourne encore. */
.tv-progression {
  width: clamp(6rem, 18vw, 22rem);
  height: 4px;
  border-radius: 2px;
  background: var(--tv-trait);
  overflow: hidden;
}

.tv-progression span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--tv-or);
}

.tv-fraicheur {
  margin: 0;
  text-align: right;
  /* « A jour · 17h07 » se terminait par « A jour · … » : la seule chose que
     cette ligne apporte est justement l'heure. */
  font-size: min(var(--tv-t-menu), 2.1vw);
  font-weight: 700;
  letter-spacing: .05em;
  color: var(--tv-encre-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-fraicheur.est-tiede { color: var(--tv-ambre); }
.tv-fraicheur.est-froide { color: #FF9A80; }

/*
 * LE SOUS-BOCK. Un carre de papier ivoire au coin de l'ecran, comme sur la
 * table : le code vers /activer, et quatre mots. Il ne demande rien. De
 * pres il se flashe, de loin il dit qu'il y a quelque chose a flasher — la
 * version en grand vit sur la vue de remise des prix, quand la salle
 * regarde. C'est un lien : inerte sur la television, utile a quiconque
 * ouvre cette page sur un telephone.
 */
.tv-sousbock {
  display: flex;
  align-items: center;
  gap: .6em;
  text-decoration: none;
  justify-self: end;
}

.tv-sousbock-code {
  width: clamp(3rem, calc(6.5vh * var(--tv-echelle-tete)), 14vh);
  height: auto;
  display: block;
  border-radius: 6px;
}

.tv-sousbock-mot {
  font-size: var(--tv-t-menu);
  font-weight: 800;
  letter-spacing: .04em;
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--tv-encre-2);
}

/* ──────────────────────────────────────────────────────────── CALIBRAGE ── */

/*
 * Le panneau de reglage — un <dialog> natif : mise au premier plan, piege a
 * focus et fond inerte viennent du navigateur. Il ne s'ouvre qu'a la
 * premiere mise en route (ou sur ?calibrer=1, ou la touche C) et c'est le
 * second des deux seuls endroits du mode TV ou une main touche quelque
 * chose : le curseur y revit, les cibles sont enormes, et les fleches de la
 * telecommande suffisent — tv.js fait circuler le focus.
 */
.tv-calibrage {
  border: 0;
  padding: 0;
  width: min(94vw, 88rem);
  max-height: 94vh;
  background: var(--tv-nuit);
  color: var(--tv-encre);
  border-radius: 18px;
  box-shadow: 0 30px 80px -24px rgba(0, 0, 0, .85);
  overflow: auto;
}

.tv-calibrage::backdrop { background: rgba(5, 8, 27, .93); }

/* Le prefixe body.tv n'est pas decoratif : il faut battre « body.tv * »
   (0-1-1) sur l'element dialog lui-meme, pas seulement sur ses enfants. */
body.tv .tv-calibrage, body.tv .tv-calibrage * { cursor: auto; }
body.tv .tv-calibrage button { cursor: pointer; }

.tv-calibrage-corps {
  margin: 0;
  display: grid;
  gap: clamp(.7rem, 1.8vh, 1.6rem);
  padding: clamp(1.2rem, 4vh, 3.2rem) clamp(1.2rem, 4vw, 3.6rem);
}

.tv-calibrage-sur {
  margin: 0;
  font-size: clamp(.72rem, 1.6vh, 1.2rem);
  font-weight: 800;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--tv-or);
}

.tv-calibrage-titre {
  margin: 0;
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.5rem, 4.4vh, 3.6rem);
  line-height: .98;
  text-transform: uppercase;
  letter-spacing: .01em;
}

.tv-calibrage-titre--deux { margin-top: clamp(.4rem, 1.4vh, 1.2rem); }

.tv-calibrage-rang {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: clamp(.5rem, 1.2vw, 1.2rem);
}

.tv-choix {
  display: grid;
  gap: .2em;
  justify-items: start;
  text-align: left;
  padding: clamp(.8rem, 2.2vh, 1.8rem) clamp(.9rem, 1.6vw, 1.6rem);
  border: 3px solid var(--tv-trait);
  border-radius: 12px;
  background: var(--tv-panneau);
  color: var(--tv-encre);
  font-family: var(--tv-texte);
}

.tv-choix-mot {
  font-size: clamp(.85rem, 2.1vh, 1.5rem);
  font-weight: 700;
}

.tv-choix-mesure {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.3rem, 3.6vh, 2.9rem);
  line-height: 1;
  color: var(--tv-or);
}

.tv-choix:focus-visible,
.tv-valider:focus-visible { outline: 4px solid var(--tv-or); outline-offset: 3px; }

.tv-choix.est-choisi {
  border-color: var(--tv-or);
  background: rgba(245, 180, 28, .12);
}

/* L'apercu rend une ligne de plateau A L'ECHELLE CANDIDATE : on voit le
   reglage avant de le valider, on ne l'imagine pas. Le bloc redeclare les
   jetons de corps (voir la note en tete de fichier) — c'est ce qui lui
   permet d'avoir sa propre --tv-echelle, posee par tv.js. */
.tv-apercu {
  border-block: 1px solid var(--tv-trait);
  padding: clamp(.4rem, 1.2vh, 1rem) 0;
  overflow: hidden;
}

.tv-calibrage-aide {
  margin: 0;
  font-size: clamp(.8rem, 1.9vh, 1.4rem);
  font-weight: 600;
  color: var(--tv-encre-2);
}

.tv-calibrage-pied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(.8rem, 2vw, 2rem);
  flex-wrap: wrap;
}

.tv-valider {
  padding: clamp(.6rem, 1.8vh, 1.4rem) clamp(1.4rem, 3vw, 3rem);
  border: 0;
  border-radius: 999px;
  background: var(--tv-or);
  color: var(--tv-nuit);
  font-family: var(--tv-texte);
  font-size: clamp(1rem, 2.6vh, 2rem);
  font-weight: 800;
  letter-spacing: .02em;
}

.tv-valider:disabled { opacity: .35; cursor: not-allowed; }

.tv-calibrage-note {
  margin: 0;
  font-size: clamp(.72rem, 1.7vh, 1.2rem);
  font-weight: 600;
  color: var(--tv-encre-2);
}

.tv-calibrage-note b { color: var(--tv-encre); }

/* ────────────────────────────────────────────────────── PAGE DE CHOIX ── */

.tv-corps--choix { display: grid; align-content: center; gap: clamp(1rem, 3vh, 3rem); overflow: auto; }

/* Le premier des deux endroits ou une main touche quelque chose : ici on
   clique, le curseur revit. */
body.tv--choix,
body.tv--choix * { cursor: auto; }
body.tv--choix a { cursor: pointer; }

/* Le pied de la page de choix : la phrase de relecture et le sous-bock. */
.tv--choix .tv-pied { grid-template-columns: 1fr auto; }
.tv--choix .tv-fraicheur { text-align: left; }

.tv-tuiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  gap: clamp(.8rem, 2vw, 2rem);
}

.tv-tuiles--pale { opacity: .62; }

.tv-tuile {
  display: grid;
  gap: .25em;
  padding: clamp(.9rem, 2.4vh, 2.2rem) clamp(1rem, 2vw, 2rem);
  border-radius: 12px;
  border: 2px solid var(--tv-trait);
  background: var(--tv-panneau);
  color: var(--tv-encre);
  text-decoration: none;
}

.tv-tuile:hover,
.tv-tuile:focus-visible { border-color: var(--tv-or); }

.tv-tuile:focus-visible { outline: 4px solid var(--tv-or); outline-offset: 3px; }

.tv-tuile-date {
  font-family: var(--tv-titre);
  font-weight: 900;
  font-size: clamp(1.2rem, 3.4vh, 2.6rem);
  line-height: 1;
  color: var(--tv-or);
  text-transform: uppercase;
}

.tv-tuile-nom  { font-size: clamp(.95rem, 2.4vh, 1.8rem); font-weight: 700; }
.tv-tuile-lieu { font-size: var(--tv-t-menu); font-weight: 600; color: var(--tv-encre-2); }

/* Pas de capitalisation automatique ici : elle transformait « 10h30 » en
   « 10H30 », qui ne s'ecrit pas ainsi en francais. */
.tv-tuile-etat {
  font-size: var(--tv-t-menu);
  font-weight: 800;
  letter-spacing: .1em;
  color: var(--tv-encre-3);
}

.tv-tuile-etat.est-vif { color: var(--tv-vert); }

/* ──────────────────────────────────────────────────────────── REPLIS ── */

/* Ecran haut ou tablette dressee : le plateau perd sa colonne d'etat, qui est
   la moins utile des cinq, plutot que de tout ecraser. */
@media (orientation: portrait), (max-width: 900px) {
  .tv-ligne {
    grid-template-columns: clamp(2.2rem, 9vw, 4rem) 1fr auto 1fr;
  }
  .tv-ligne > .tv-etat { display: none; }
  /* Sur une tablette dressee, une equipe sur une seule ligne se coupe au
     milieu du nom du partenaire. Mieux vaut deux lignes et une liste plus
     longue — la pagination s'en charge — qu'un nom illisible. */
  .tv-equipe { white-space: normal; overflow-wrap: anywhere; line-height: 1.08; }
  .tv-ligne--rang { grid-template-columns: clamp(2rem, 8vw, 3.4rem) 1fr auto auto; }
  .tv-ligne--rang > .tv-etat { display: none; }
  .tv-ligne--podium { grid-template-columns: auto clamp(2rem, 8vw, 3.4rem) 1fr auto auto; }
  .tv-tete { grid-template-columns: auto 1fr; }
  .tv-tour { grid-column: 1 / -1; justify-self: start; }
  .tv-pied { grid-template-columns: 1fr auto; }
  .tv-progression { display: none; }
  .tv-flashe { flex-direction: column; text-align: center; }
  .tv-flashe-mot { justify-items: center; }
}

/* Ecran tres bas (720p, vieux moniteur) : on resserre les interlignes plutot
   que de reduire la taille des caracteres, qui est la seule chose qui compte
   a cinq metres. */
@media (max-height: 760px) {
  .tv-ligne { min-height: 0; padding-block: .1rem; }
  .tv-scene { gap: .35rem; }
}

/*
 * Mouvement reduit : plus de fondu, plus d'illumination, plus de Trouble
 * anime — l'or des vainqueurs reste or, ce qui ne retire aucun sens. La
 * rotation reste, elle est le fond de la fonction ; la page tourne alors
 * d'un coup sec, sans se soulever — tv.js lit la meme preference.
 */
@media (prefers-reduced-motion: reduce) {
  .tv-vue { transition: none; }
  .tv-piste { transition: none !important; }
  .tv-ligne.est-neuve { animation: none; }
  .tv-progression span { transition: none !important; }
  .tv-consacre { transition: none; }
}

/* ══════════════════════════════════════════ LE TOUR QUI SE NOMME ═══════ */

/* En championnat, les tours ne se comptent pas : ils s'appellent « Poules ·
   barrages », « Seizièmes de finale », « Finale ». Le mot « Tour » et le
   « sur N » n'ont alors plus de sens — la télé annonçait « Tour 30 sur 30 »
   un dimanche de finale. On les retire, et le nom prend toute la place. */
.tv-tour.est-nomme .tv-tour-mot,
.tv-tour.est-nomme .tv-tour-total { display: none; }

.tv-tour.est-nomme .tv-tour-chiffre {
  font-size: clamp(1.1rem, 2.1vw, 2rem);
  letter-spacing: -.01em;
  white-space: nowrap;
}

/* ══════════════════════════════════ LE CLASSEMENT EST RETENU ══
   Une page pleine, lisible du fond de la salle, à la place du tableau. Elle
   remplace un panneau qui aurait été sauté sans un mot — et un écran qui
   saute une page passe pour une panne devant trente personnes. */
.tv-retenu {
  display: grid;
  place-content: center;
  gap: 1.2rem;
  min-height: 46vh;
  padding: 2rem 3rem;
  text-align: center;
}

.tv-retenu-titre {
  font-size: clamp(2rem, 3.4vw, 3.4rem);
  font-weight: 800;
  line-height: 1.1;
  text-wrap: balance;
}

.tv-retenu-dit {
  font-size: clamp(1.1rem, 1.6vw, 1.7rem);
  line-height: 1.5;
  max-width: 46ch;
  margin-inline: auto;
  opacity: .82;
  text-wrap: pretty;
}

.tv-retenu-note {
  font-size: clamp(.95rem, 1.2vw, 1.3rem);
  opacity: .58;
}

/* ═══════════════════════════════════════════════════ LES SPONSORS ══
   Trois places, trois traitements. Une seule règle les commande toutes : un
   logo ne se déforme JAMAIS. Le commerçant qui voit son logo étiré sur
   l'écran du club retient ça et rien d'autre — et c'est justement la personne
   qu'on remercie.

   Le carré blanc derrière chaque logo est délibéré, y compris sur ce fond
   sombre : la plupart des logos de commerçants sont dessinés pour du papier,
   et beaucoup disparaissent sur du marine. Un fond neutre les rend tous
   lisibles sans avoir à demander une version claire à chacun. */

/* ── LA PAGE ENTIÈRE ─────────────────────────────────────────────────── */

.tv-sponsor {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: clamp(.7rem, 2vh, 1.6rem);
  min-height: 52vh;
  padding: clamp(1.2rem, 4vh, 3rem);
  border-radius: 1.2rem;
  background: var(--tv-sponsor-fond, transparent);
  text-align: center;
}

.tv-sponsor-logo {
  display: grid;
  place-items: center;
  width: min(58vw, 42rem);
  height: clamp(6rem, 26vh, 16rem);
  padding: clamp(.6rem, 1.6vh, 1.4rem);
  border-radius: 1rem;
  background: #fff;
}

.tv-sponsor-logo img {
  max-width: 100%;
  max-height: 100%;
  /* JAMAIS `cover` : il recadre, et un logo recadré perd son nom. */
  object-fit: contain;
}

.tv-sponsor-nom {
  margin: 0;
  font-family: var(--tv-titre);
  font-size: clamp(1.8rem, 4.4vw, 4rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -.01em;
  text-wrap: balance;
}

.tv-sponsor-ville {
  margin: 0;
  font-size: clamp(.9rem, 1.5vw, 1.6rem);
  font-weight: 800;
  letter-spacing: .18em;
  text-transform: uppercase;
  opacity: .72;
}

.tv-sponsor-texte {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1rem, 1.8vw, 1.9rem);
  line-height: 1.45;
  text-wrap: pretty;
  opacity: .88;
}

.tv-sponsor-mot {
  margin: 0;
  font-size: clamp(.7rem, 1.1vw, 1.1rem);
  font-weight: 800;
  letter-spacing: .22em;
  text-transform: uppercase;
  opacity: .5;
}

/* Le fond posé par le panel peut être clair : l'encre bascule alors en
   sombre. Un nom blanc sur du jaune ne se lit pas, et personne ne viendra
   régler ça un dimanche matin. */
.tv-sponsor.est-clair,
.tv-sponsor.est-clair .tv-sponsor-nom,
.tv-sponsor.est-clair .tv-sponsor-texte,
.tv-sponsor.est-clair .tv-sponsor-ville,
.tv-sponsor.est-clair .tv-sponsor-mot { color: #12162B; }

/* ── LE MUR DE VIGNETTES ─────────────────────────────────────────────── */

.tv-mur {
  display: grid;
  gap: clamp(.6rem, 1.6vw, 1.4rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  align-content: start;
  padding: clamp(.4rem, 1.4vh, 1rem) 0;
}

.tv-mur-carte {
  display: grid;
  justify-items: center;
  gap: .45rem;
  padding: clamp(.5rem, 1.4vh, 1rem);
  border: 1px solid var(--tv-trait);
  border-radius: .9rem;
}

.tv-mur-logo {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: .5rem;
  border-radius: .6rem;
  background: #fff;
  overflow: hidden;
}

.tv-mur-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }

.tv-mur-logo--mot {
  font-family: var(--tv-titre);
  font-size: clamp(.95rem, 1.9vw, 1.7rem);
  font-weight: 900;
  line-height: 1.05;
  color: #12162B;
  text-align: center;
  text-wrap: balance;
}

.tv-mur-nom {
  margin: 0;
  font-size: clamp(.85rem, 1.3vw, 1.25rem);
  font-weight: 800;
  text-align: center;
  text-wrap: balance;
}

.tv-mur-ville {
  margin: 0;
  font-size: clamp(.65rem, .95vw, .95rem);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .6;
}

/* ── LE RUBAN, AU PIED, PENDANT LES AUTRES VUES ──────────────────────── */

.tv-ruban {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(.7rem, 2vw, 1.8rem);
  flex-wrap: wrap;
  padding: clamp(.3rem, .9vh, .7rem) clamp(.6rem, 2vw, 1.4rem);
  border-top: 1px solid var(--tv-trait);
  opacity: .78;
}

.tv-ruban-item {
  display: grid;
  place-items: center;
  height: clamp(1.5rem, 3.4vh, 2.6rem);
  padding: .18rem .45rem;
  border-radius: .35rem;
  background: #fff;
}

.tv-ruban-item img { max-height: 100%; width: auto; object-fit: contain; }

.tv-ruban-item b {
  font-size: clamp(.6rem, .95vw, .95rem);
  font-weight: 900;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #12162B;
  padding: 0 .2rem;
}
