/* ═══════════════════════════════════════════════════════════════════════════
   PETANQUE BELGE (Pétanque Belge) — 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;
  font-size: var(--tv-t-menu);
  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;
}

.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 {
  font-size: var(--tv-t-equipe);
  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:
    clamp(2.2rem, 4vw, 5rem)      /* 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:
    clamp(2.2rem, 4vw, 5rem)
    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;
  font-size: var(--tv-t-menu);
  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; }
}
