/* Polices du club (fichiers dans fonts/ — voir README : licences) */
@font-face { font-family: 'Grenat'; font-weight: 900; src: url('fonts/Grenat0_2-Black.woff2') format('woff2'); }
@font-face { font-family: 'AT Name Sans'; font-weight: 1 1000; src: url('fonts/AT-NameSansSplitVF-Upright.woff2') format('woff2'); }
@font-face { font-family: 'AT Name Sans'; font-style: italic; font-weight: 1 1000; src: url('fonts/AT-NameSansSplitVF-Italic.woff2') format('woff2'); }

/* =====================================================================
   1. L'INTERFACE (formulaire + zone d'aperçu)
   Principe : fond papier, texte noir. Le NOIR marque ce qui est choisi (une rangée choisie
   ressemble à l'image qu'elle va produire) ; l'ORANGE est réservé à l'action et à l'heure.
   Angles droits, comme les pastilles des visuels. Zones tactiles d'au moins 44 px.
   ===================================================================== */
:root { --orange: #FF7A00; --noir: #0D0D0D; --blanc: #F5F2EC; --gris: #BDB8AE; --s: .3; --h: 1920; --haut: 270px; --bas: 360px; --bas-base: 360px; --logo-h: 48px;
  --papier: #F5F2EC; --encre-douce: #6B675F; --filet: #D9D4C8; --fond-apercu: #E4DFD3; --largeur-formulaire: 420px;
  --papier-creux: #EDE8DC; --encre-forte: #4A4740; --erreur: #B00020;   /* survol des boutons, filet des matchs cochés, message d'erreur */
  /* Utopia (https://utopia.fyi) : texte 18 → 20 px, ratio 1,2 → 1,25, écrans de 360 à 1240 px. Interface seulement : la scène garde des tailles fixes. */
  --step--2: 0.875rem;   /* 14 px (avant : 12,5 px, trop petit pour le menu du bas et les sous-titres) */
  --step--1: clamp(0.9375rem, 0.9119rem + 0.1136vw, 1rem);
  --step-0: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem);
  --step-1: clamp(1.35rem, 1.2631rem + 0.3864vw, 1.5625rem);
  --step-2: clamp(1.62rem, 1.4837rem + 0.6057vw, 1.9531rem);
  --space-3xs: clamp(0.2812rem, 0.2685rem + 0.0568vw, 0.3125rem);
  --space-2xs: clamp(0.5625rem, 0.5369rem + 0.1136vw, 0.625rem);
  --space-xs: clamp(0.8438rem, 0.8054rem + 0.1705vw, 0.9375rem);
  --space-s: clamp(1.125rem, 1.0739rem + 0.2273vw, 1.25rem);
  --space-m: clamp(1.6875rem, 1.6108rem + 0.3409vw, 1.875rem);
  --space-l: clamp(2.25rem, 2.1477rem + 0.4545vw, 2.5rem);
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }   /* l'attribut hidden doit gagner même sur un élément dont le CSS fixe un display */
/* --barre-h = hauteur réelle de la barre d'action fixe (mesurée par app.js : elle grandit quand « Copier le texte du post » apparaît).
   Quand le navigateur fait défiler vers un champ, il le garde au-dessus de cette barre. */
html { scroll-padding-bottom: calc(var(--barre-h, 150px) + 20px); }
body { margin: 0; font-family: 'AT Name Sans', system-ui, -apple-system, 'Segoe UI', sans-serif; font-weight: 500; font-size: var(--step-0);
  line-height: 1.4; background: var(--papier); color: var(--noir); -webkit-text-size-adjust: 100%; }

.appli { display: grid; grid-template-columns: var(--largeur-formulaire) minmax(0, 1fr); min-height: 100vh; }   /* minmax(0, …) : une colonne ne s'élargit jamais à cause de son contenu */
.formulaire { min-width: 0; background: var(--papier); padding: var(--space-s) var(--space-s) calc(var(--barre-h, 150px) + 20px); border-right: 1px solid var(--filet); }

/* En-tête : la marque, en Grenat (autorisé pour "HBC Moudon") */
.entete { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: var(--space-s); }
.marque { font-family: 'Grenat', 'AT Name Sans', sans-serif; font-weight: 900; font-size: var(--step-2); line-height: 1; margin: 0; }
.aide { color: var(--encre-douce); font-size: var(--step--1); margin: 0 0 12px; }
.petit { font-size: var(--step--1); margin: 10px 0 0; }
#etat-calendrier:empty { display: none; }   /* calendrier chargé sans souci : rien à dire */
.aide-courte { margin-bottom: 0; }   /* suivie de son « Comment faire ? » */
/* « Comment faire ? » / « Ça ne marche pas ? » : l'aide rarement utile, repliée (une seule ligne visible) */
details.astuce { margin: 0 0 12px; }
details.astuce > summary { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: var(--step--1); font-weight: 600; color: var(--encre-douce); cursor: pointer; }
details.astuce > summary::after { content: "+"; }
details.astuce[open] > summary::after { content: "–"; }
details.astuce .aide { margin: 0 0 4px; }

/* Aide « Ajouter à l'écran d'accueil » (iPhone / iPad, une seule fois) : un encadré noir, avec le bouton « Compris ».
   Elle est dans le formulaire : sur téléphone, les écrans Aperçu, Texte et Publier la cachent déjà. */
.aide-accueil { border: 2px solid var(--noir); padding: 12px 14px; margin: 0 0 14px; }
.aide-accueil h2 { font-size: var(--step-0); font-weight: 800; line-height: 1.2; margin: 0 0 6px; }
.aide-accueil p { font-size: var(--step--1); margin: 0 0 10px; }
.aide-accueil .icone-partage { width: 1.2em; height: 1.2em; vertical-align: -.2em; stroke: currentColor; fill: none; stroke-width: 2; }
.aide-accueil button { min-height: 44px; padding: 0 20px; }

/* Choix à deux (Annonce / Résultat, Calendrier / Saisie manuelle) */
.choix { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; border: 2px solid var(--noir); }
.choix label { margin: 0; }
.choix input { position: absolute; opacity: 0; }
/* Les 5 types : en haut les deux les plus fréquents, en grand (Match à venir, Résultat : 3 colonnes chacun), en bas les trois autres
   (Classement, Message, Photo : 2 colonnes chacun). Grille de 6 colonnes → pas de case vide. */
.choix.choix-modes { grid-auto-flow: row; grid-template-columns: repeat(6, minmax(0, 1fr)); }
.choix.choix-modes label { grid-column: span 2; }
.choix.choix-modes label.mode-grand { grid-column: span 3; }
.choix.choix-modes span, .choix.choix-compact span { padding: 14px 2px; font-size: var(--step--1); }
.choix.choix-modes span { min-height: 44px; padding: 10px 2px; }   /* zone tactile de 44 px : la liste des matchs reste haute dans l'écran */
.choix.choix-modes .mode-grand span { min-height: 60px; padding: 8px 4px; display: grid; align-content: center; }
.choix.choix-modes .mode-grand b { font-size: var(--step-0); line-height: 1.15; }
.choix.choix-modes .mode-grand small { font-size: var(--step--2); font-weight: 500; line-height: 1.2; }
/* Format, sous le choix du type : même style, un peu moins haut (c'est un réglage, pas le choix principal) */
.choix.choix-format { margin-top: 10px; }
/* Air sous un choix suivi d'un autre champ (remplace les « margin-bottom » écrits dans index.html) */
.choix.choix-air { margin-bottom: 14px; }
.choix.choix-air-court { margin-bottom: 12px; }
/* Paysage : seulement pour le « Album photos » (toutes les images d'un carrousel doivent avoir la même taille) */
body:not([data-mode="photo"]) .format-paysage, body:not([data-style-photo="carrousel"]) .format-paysage { display: none; }
/* Présentation des images : 4 choix, sur 2 rangées de 2 (4 colonnes seraient trop étroites sur un téléphone) */
.choix.choix-presentation { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.choix.choix-format span { min-height: 44px; padding: 11px 2px; }
/* Le format sur deux lignes (nom, puis rapport) : les trois choix ont toujours la même hauteur, avec ou sans « Paysage » */
.choix.choix-format small { display: block; font-size: var(--step--2); font-weight: 500; line-height: 1.2; }
.choix span { display: block; margin: 0; text-align: center; padding: 14px 8px; min-height: 52px; font-weight: 700; cursor: pointer; }
.choix input:checked + span { background: var(--orange); color: var(--noir); box-shadow: inset 0 -6px 0 var(--noir); }   /* la barre noire dit « choisi » sans dépendre de la couleur */
/* Survol (souris seulement : sur téléphone, le survol resterait collé après un appui). La case choisie ne change pas. */
@media (hover: hover) { .choix input:not(:checked):not(:disabled) + span:hover { background: var(--papier-creux); } }
/* Désactivé (option impossible pour l'instant) : texte gris doux, pas de survol, curseur « interdit ». Une case choisie puis désactivée devient grise, avec une barre grise. */
.choix input:disabled + span { color: var(--encre-douce); font-weight: 500; cursor: not-allowed; }
.choix input:disabled:checked + span { background: var(--gris); box-shadow: inset 0 -6px 0 var(--encre-douce); }
.choix input:focus-visible + span { outline: 3px solid var(--noir); outline-offset: -10px; }   /* à l'intérieur, au-dessus de la barre noire */

/* Période : le titre de la liste */
/* Titre vide (avant le chargement) : invisible, donc non lu par les lecteurs d'écran, mais la place reste réservée */
.periode h2:empty { visibility: hidden; }
.periode h2 { font-size: var(--step-1); font-weight: 800; line-height: 1.15; letter-spacing: -.01em; margin: 18px 0 8px; }
/* « Modifier les dates », « Toutes les équipes » : même repère + / – que les autres blocs repliables */
details.dates summary { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 0; min-height: 48px; color: var(--noir);
  font-weight: 700; border-bottom: 1px solid var(--filet); cursor: pointer; }
details.dates summary::after { content: "+"; font-size: var(--step-1); font-weight: 500; }
details.dates[open] summary::after { content: "–"; }
details.dates .corps { padding: 4px 0 12px; }
/* Dates et équipes : deux puces sur une ligne (la liste des matchs commence plus haut) ; ouvert, un filtre prend toute la largeur */
.filtres { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.filtres details.dates { flex: 1 1 0; min-width: 0; }
.filtres #bloc-dates { flex: 0 0 auto; }   /* « Les dates » est court : « Toutes les équipes » garde le reste de la ligne */
.filtres #bloc-dates[open] { flex: 1 1 100%; }   /* ouvert : toute la largeur (sinon, avec la règle ci-dessus, les deux champs de date débordent de l'écran) */
.filtres details.dates[open] { flex: 1 1 100%; }
.filtres details.dates summary { min-height: 44px; padding: 0 10px; gap: 6px; border: 2px solid var(--noir); font-size: var(--step--1); white-space: nowrap; }
.filtres details.dates summary > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.filtres details.dates[open] summary { background: var(--noir); color: var(--papier); }   /* le noir marque ce qui est ouvert / choisi */
.filtres details.dates .corps { padding: 10px 0 4px; }
details > summary::-webkit-details-marker { display: none; }
details > summary { list-style: none; }

.entete-liste { display: flex; justify-content: space-between; align-items: center; margin: 8px 0 6px; font-weight: 700; }
.entete-liste .liens { white-space: nowrap; }   /* « Tous » et « Aucun » restent côte à côte */
button.lien { border: 0; background: none; padding: 10px 6px; min-height: 44px; font-weight: 600; text-decoration: underline; }

/* Case à cocher : un seul dessin (carré de 24 px, coche à l'intérieur), voir docs/composants/case-a-cocher.md.
   Sur le papier : vide = cadre gris, cochée = noir + coche orange. Sur une rangée noire (.match .coche) : cochée = orange + coche noire.
   La vraie case (<input>) est cachée ; le carré est dessiné par ::before sur son étiquette. */
.puce label::before, .case span::before { content: ""; box-sizing: border-box; width: 24px; height: 24px; flex: none; border: 2px solid var(--encre-douce); background: transparent center / 16px no-repeat; }
.puce input:checked + label::before, .case input:checked + span::before { border-color: var(--noir); background-color: var(--noir);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 8.5l4 4 7-8' fill='none' stroke='%23FF7A00' stroke-width='3'/%3E%3C/svg%3E"); }   /* %23FF7A00 = l'orange du club */
.case input:focus-visible + span::before { outline: 3px solid var(--noir); outline-offset: 2px; }

/* Équipes : des puces que l'on coche (noir = incluse) */
.puces { display: flex; flex-wrap: wrap; gap: var(--space-2xs); margin: 0 0 22px; }
.puce input { position: absolute; opacity: 0; }
.puce label { display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 12px; min-height: 44px; border: 2px solid var(--encre-douce); color: var(--encre-douce);
  font-weight: 600; font-size: var(--step--1); cursor: pointer; }
.puce input:checked + label { border-color: var(--noir); color: var(--noir); font-weight: 700; }
.puce input:focus-visible + label { outline: 3px solid var(--noir); outline-offset: 2px; }

/* Matchs : une rangée par match ; choisie = noire, l'heure passe en orange */
.matchs { border-top: 2px solid var(--noir); margin-top: 6px; }
.match input { position: absolute; opacity: 0; }
.match label { display: grid; grid-template-columns: 92px 1fr 28px; gap: 14px; align-items: center; margin: 0; min-height: 76px; padding: 14px 14px 14px 12px;
  border-bottom: 1px solid var(--filet); cursor: pointer; }
.match .quand { font-family: 'Grenat', 'AT Name Sans', sans-serif; font-weight: 900; font-size: var(--step-1); line-height: 1; color: var(--encre-douce); }
.match .quand.score { font-size: var(--step-0); }
.match .quoi > span { display: block; }
.match .quoi > .etiquette { width: fit-content; background: var(--orange); color: var(--noir); font-weight: 800; font-size: var(--step--2); line-height: 1.2; padding: 2px 8px 3px; margin-bottom: 5px; }
.match .adv { font-weight: 800; font-size: var(--step-0); line-height: 1.2; overflow-wrap: anywhere; }   /* nom long : retour à la ligne, jamais de rangée plus large que l'écran */
.match .detail { color: var(--encre-douce); font-size: var(--step--1); margin-top: 2px; }
.match .coche { box-sizing: border-box; width: 24px; height: 24px; border: 2px solid var(--noir); }
.match input:checked + label { background: var(--noir); color: var(--papier); border-bottom-color: var(--encre-forte); }   /* filet gris foncé : on distingue chaque match choisi */
.match input:checked + label .quand { color: var(--orange); }
.match input:checked + label .detail { color: var(--gris); }
.match input:checked + label .coche { background: var(--orange); border-color: var(--orange); position: relative; }
.match input:checked + label .coche::after { content: ""; position: absolute; left: 6px; top: 1px; width: 6px; height: 12px; border: solid var(--noir); border-width: 0 3px 3px 0; transform: rotate(45deg); }
.match input:focus-visible + label { outline: 3px solid var(--orange); outline-offset: -3px; }
.vide { margin: 0; padding: 22px 4px; color: var(--encre-douce); }
/* Aucun match à proposer : une phrase qui dit pourquoi, et un bouton qui règle le problème quand c'est possible */
.vide p { margin: 0 0 8px; }
.vide p:first-child { color: var(--noir); font-weight: 700; }
.vide .bouton-vide { min-height: 48px; padding: 0 20px; margin-top: 6px; }
/* Chargement du calendrier : des rangées grises qui « respirent » (fixes si la personne a réduit les animations) */
.match-attente { height: 84px; background: var(--fond-apercu); border-bottom: 1px solid var(--papier); animation: attente 1.2s ease-in-out infinite alternate; }
@keyframes attente { from { opacity: .45; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .match-attente { animation: none; } }

/* Réglages repliables (présentation, photo, autres) */
details.reglage { border-top: 1px solid var(--filet); }
details.reglage:last-of-type { border-bottom: 1px solid var(--filet); }
details.reglage > summary { display: flex; justify-content: space-between; align-items: center; min-height: 56px; font-weight: 700; cursor: pointer; }
details.reglage > summary::after { content: "+"; font-size: var(--step-1); font-weight: 500; }
details.reglage[open] > summary::after { content: "–"; }
/* Petit texte dans le titre replié (ex. « Sponsors : 3 sur 7 »), juste avant le « + » */
.resume-reglage { margin-left: auto; margin-right: 12px; font-size: var(--step--1); font-weight: 500; color: var(--encre-douce); text-align: right; }
details.reglage > .corps { padding: 0 0 18px; }
/* Version de l'outil, tout en bas du formulaire (version.js) */
.version-outil { margin: var(--space-m) 0 0; font-size: var(--step--2); color: var(--encre-douce, #4a4a4a); }
.version-outil a { color: inherit; text-decoration: underline; display: inline-block; padding: 12px 0; }   /* zone tactile confortable */
/* Nouveautés lues dans l'outil : fenêtre plein écran sur téléphone, avec un bouton « Fermer » toujours visible */
.nouveautes { width: min(100%, 44rem); max-width: 100%; height: 100%; max-height: 100%; margin: 0 auto; padding: var(--space-s); border: 0; background: var(--papier, #F5F2EC); color: inherit; overflow: hidden; }
.nouveautes[open] { display: flex; flex-direction: column; }
.nouveautes h2 { margin: 0 0 var(--space-xs); font-size: var(--step-1); }
.nouveautes .journal { flex: 1; min-height: 0; overflow: auto; }
/* Journal des versions mis en forme (nouveautes.js) : fenêtre de l'écran d'accueil et page nouveautes.html */
.journal { font-size: var(--step--1); line-height: 1.5; }
.journal h2 { margin: var(--space-m) 0 var(--space-3xs); font-size: var(--step-0); }
.journal h2:first-child { margin-top: 0; }
.journal .journal-date { display: block; margin-top: 2px; font-size: var(--step--2); font-weight: 400; color: var(--encre-douce); }
.journal h3 { margin: var(--space-xs) 0 var(--space-3xs); font-size: var(--step--1); }
.journal ul { margin: 0; padding-left: 1.2em; }
.journal li { margin-bottom: var(--space-3xs); }
.journal code { font-size: .9em; background: var(--papier-creux); padding: 0 .25em; }
.journal-page { max-width: 44rem; margin: 0 auto; padding: var(--space-s); }
.journal-page h1 { margin: 0 0 var(--space-s); font-size: var(--step-2); }
.journal-page a { color: inherit; display: inline-block; padding: 12px 0; }
.page-nouveautes { background: var(--papier, #F5F2EC); }
.nouveautes button { min-height: 48px; margin-top: var(--space-xs); border: 0; background: #0D0D0D; color: #FF7A00; font: inherit; font-weight: 700; cursor: pointer; }

details.reglage:first-of-type { margin-top: var(--space-m); }
summary:focus-visible { outline: 3px solid var(--noir); outline-offset: 2px; }

/* Champs */
fieldset { border: 0; padding: 0; margin: var(--space-m) 0 0; }
legend { font-weight: 800; font-size: var(--step-1); padding: 0; margin-bottom: 12px; }
label { display: block; font-size: var(--step--1); margin-bottom: var(--space-xs); }
label > span { display: block; margin-bottom: 4px; font-weight: 700; }
input[type=text], input[type=password], input[type=date], input[type=time], input[type=number], select, textarea {
  width: 100%; min-height: 48px; padding: 10px 12px; font-size: var(--step-0); font-family: inherit; color: inherit; background: #FBF9F4; border: 2px solid var(--noir); border-radius: 0; }
.outils-texte { display: flex; gap: 8px; margin-bottom: 8px; }
.outils-texte button { display: flex; align-items: center; gap: 8px; padding: 8px 14px; min-width: 56px; justify-content: center; }
.outils-texte i { display: block; font-style: normal; }
.echantillon-orange { width: 14px; height: 14px; background: var(--orange); }
/* Aide de l'IA (voir ia.js) : tout est caché tant que api/ia.php n'est pas disponible (body[data-ia="oui"] posé par ia.js) */
.bloc-ia { display: none; margin: 0 0 var(--space-xs); }
body[data-ia="oui"] .bloc-ia { display: block; }
/* Annonce, Résultat, Classement : le texte alternatif reprend déjà tout le texte de l'image, la photo de fond n'est qu'un décor : pas de description par l'IA */
body[data-mode="annonce"] .ia-photo, body[data-mode="resultat"] .ia-photo, body[data-mode="classement"] .ia-photo { display: none !important; }
#ia-idees { min-height: 96px; }
/* Type Photo : « À l'honneur » et « Album photos » ont un texte du post fabriqué (noms, motifs, scores, dates) : pas de retouche non plus */
body[data-mode="photo"][data-style-photo="honneur"] .retouche-ia[data-cible="texte-post"], body[data-mode="photo"][data-style-photo="carrousel"] .retouche-ia[data-cible="texte-post"] { display: none !important; }
/* Annonce, Résultat, Classement : le texte du post vient des modèles fixes (scores, noms, heures exacts) : pas de retouche par l'IA */
body[data-mode="annonce"] .retouche-ia[data-cible="texte-post"], body[data-mode="resultat"] .retouche-ia[data-cible="texte-post"], body[data-mode="classement"] .retouche-ia[data-cible="texte-post"] { display: none !important; }
.bouton-ia { display: block; width: 100%; margin: 0 0 8px; }
.boutons-ia { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; margin: 0 0 8px; }
.boutons-ia button { min-height: 44px; padding: 8px 14px; font-size: var(--step--1); }
.bloc-ia button:disabled { opacity: .55; cursor: wait; }
.annuler-ia { border: 0; background: none; padding: 10px 6px; min-height: 44px; font-weight: 600; text-decoration: underline; }
.echantillon-puce { width: 8px; height: 8px; background: var(--orange); }
code { font-family: ui-monospace, Menlo, monospace; font-size: .92em; background: var(--papier-creux); padding: 1px 5px; }
textarea { min-height: 180px; resize: vertical; line-height: 1.4; }
/* Texte du post : la case prend la hauteur de son texte (réglée par texte-post.js), on voit tout sans défiler dedans */
#texte-post { min-height: 120px; resize: none; overflow: hidden; }
#texte-alt { min-height: 96px; resize: none; overflow: hidden; margin-bottom: 8px; }
details.texte-alt { margin: 14px 0 0; }
input[type=range] { width: 100%; accent-color: var(--noir); min-height: 32px; }
input:focus-visible, button:focus-visible, textarea:focus-visible, select:focus-visible { outline: 3px solid var(--noir); outline-offset: 2px; }   /* noir : 17:1 sur le papier (l'orange n'y fait que 2,3:1) ; l'orange reste sur les rangées noires */
.deux { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }   /* minmax(0, …) : les champs ne débordent jamais */
.ligne-sponsor { display: flex; align-items: center; gap: 2px; }
.ligne-sponsor .case { flex: 1; min-width: 0; margin: 0; }
.ligne-sponsor button.lien { min-width: 44px; padding: 10px 0; text-decoration: none; }
.ligne-sponsor button.lien:disabled { opacity: .3; }
.case { position: relative; display: flex; align-items: center; min-height: 44px; font-size: var(--step--1); margin-bottom: 8px; }
.case input { position: absolute; opacity: 0; }
.case span { display: flex; align-items: center; gap: 12px; margin: 0; font-weight: 500; }

/* Photo de fond : grand bouton « + Ajouter une photo ». Le vrai champ fichier est posé dessus, invisible (opacity 0) :
   un appui n'importe où sur le bouton ouvre le choix de photo, et le texte est en français sur tous les téléphones. */
.bouton-photo { position: relative; display: flex; align-items: center; justify-content: center; min-height: 56px; margin: 0 0 8px;
  border: 2px solid var(--noir); background: var(--papier); font-size: var(--step-0); cursor: pointer; }
.bouton-photo > span { margin: 0; }
.bouton-photo input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.bouton-photo:hover { background: var(--papier-creux); }
.bouton-photo:focus-within { outline: 3px solid var(--noir); outline-offset: 2px; }
.facultatif { font-size: var(--step-0); font-weight: 500; color: var(--encre-douce); }
/* Photo choisie : vignette carrée à gauche, aide et « Retirer » à droite */
.fond-choisi { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 14px; align-items: center; }
.fond-choisi img { display: block; width: 96px; height: 96px; object-fit: cover; border: 2px solid var(--noir); }
.fond-infos .aide { margin: 0 0 8px; }
.deplacer-photo { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 0 0 12px; }   /* déplacer la photo sans glisser (clavier, lecteur d'écran) */
.deplacer-photo button { padding: 0; font-size: var(--step-1); }
.cadrer-photo { display: block; width: 100%; margin: 0 0 8px; }
.photo-ligne .cadrer-photo, .ligne-fond .cadrer-photo { width: auto; min-height: 44px; padding: 8px 14px; }
body[data-disposition="cadre"]:not([data-style-photo="carrousel"]) .photo-ligne .cadrer-photo { display: none; }   /* photo entière : rien à cadrer */   /* bouton « Cadrer la photo » (téléphone) : ouvre l'aperçu */
.indice-cadrage { position: absolute; z-index: 5; top: 14px; left: 50%; transform: translateX(-50%); width: max-content; max-width: calc(100% - 24px);
  margin: 0; padding: 10px 14px; background: var(--noir); color: var(--papier); font-size: var(--step--1); font-weight: 600; text-align: center; pointer-events: none; }

/* Plusieurs images : une rangée par image. Vignette (ou « + Photo ») à gauche : c'est le bouton pour choisir la photo
   (le champ fichier est posé dessus, invisible) ; le nom au milieu (un appui montre l'image dans l'aperçu) ; ✕ pour retirer. */
.ligne-fond { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 4px 12px; align-items: start; padding: 8px 0; border-top: 1px solid var(--filet); }
.ligne-fond:last-child { border-bottom: 1px solid var(--filet); }
.vignette-fond { position: relative; display: flex; align-items: center; justify-content: center; width: 72px; height: 72px; margin: 0;
  border: 2px solid var(--noir); background: var(--papier); font-size: var(--step--1); font-weight: 700; text-align: center; cursor: pointer; overflow: hidden; }
.vignette-fond > span { margin: 0; line-height: 1.1; }
.vignette-fond img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vignette-fond input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.vignette-fond:focus-within { outline: 3px solid var(--noir); outline-offset: 2px; }
.ligne-fond.actuelle .vignette-fond { box-shadow: 0 0 0 3px var(--noir); }   /* l'image montrée dans l'aperçu */
.infos-fond { min-width: 0; }   /* un nom très long se coupe avec « … » au lieu de déborder */
button.nom-fond { min-width: 0; max-width: 100%; min-height: 48px; padding: 4px 0; border: 0; background: none; text-align: left; }
button.nom-fond:hover { background: none; text-decoration: underline; }
.nom-fond b, .nom-fond small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nom-fond small { font-weight: 500; font-size: var(--step--1); color: var(--encre-douce); }

/* "Choisir un fichier" : le bouton par défaut du navigateur est minuscule, surtout au doigt */
input[type=file] { width: 100%; font-size: var(--step--1); color: var(--encre-douce); }
input[type=file]::file-selector-button { font: inherit; font-size: var(--step-0); font-weight: 700; color: var(--noir); min-height: 56px; padding: 14px 22px; margin-right: 14px;
  background: var(--papier); border: 2px solid var(--noir); border-radius: 0; cursor: pointer; }
input[type=file]:hover::file-selector-button { background: var(--orange); }

button { font: inherit; font-weight: 700; min-height: 48px; padding: 11px 16px; border: 2px solid var(--noir); border-radius: 0; background: var(--papier); color: var(--noir); cursor: pointer; }
button:hover { background: var(--papier-creux); }
button.principal { background: var(--orange); border-color: var(--noir); font-size: var(--step-0); min-height: 56px; width: 100%; }
button.principal:hover { background: #FF8D26; }
/* Images prêtes (téléphone) : le bouton devient noir, comme les matchs choisis, pour montrer que l'étape a changé */
button.principal.pret { background: var(--noir); color: var(--orange); border-color: var(--noir); }
button.principal.pret:hover { background: #262626; }
/* Boutons secondaires sur toute la largeur (Copier le texte, Copier la description, Partager) */
button.pleine-largeur { width: 100%; }
button:disabled { opacity: .6; cursor: wait; }
/* Il manque quelque chose (aucun match coché, pas de photo…) : le bouton est grisé et dit quoi faire (voir ceQuiManque() dans app.js) */
button.principal.manque, button.principal.manque:hover { background: var(--filet); color: var(--encre-forte); border-color: var(--gris); cursor: default; }   /* var(--encre-forte) sur --filet : 6:1 (avec --encre-douce : 3,8:1, trop faible pour une consigne) */

/* Barre d'action fixe : résumé + bouton principal toujours sous le pouce */
.barre-action { position: fixed; left: 0; bottom: 0; width: var(--largeur-formulaire); z-index: 20; background: var(--papier); border-top: 2px solid var(--noir);
  padding: 8px 16px calc(8px + env(safe-area-inset-bottom)); }
.barre-action .resume:empty { display: none; }
.barre-action .infos { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 12px; margin: 0; }
.barre-action .resume { margin: 0 0 6px; min-height: 1.4em; font-size: var(--step--1); font-weight: 600; color: var(--encre-douce); }
.barre-action .actions { display: grid; grid-template-columns: 1fr; gap: 10px; }
.barre-action .conseil { margin: 0 0 6px; font-size: var(--step--1); color: var(--encre-douce); }   /* une ligne à 390 px (textes courts) */
.barre-action .copier-barre { width: 100%; min-height: 44px; margin: 0 0 8px; }
/* Marges intérieures réduites : « Enregistrer les 3 images » tient sur une ligne à côté de « Aperçu » / « Modifier » dès 360 px de large */
.barre-action .actions button { padding-left: 10px; padding-right: 10px; }

/* Écran Publier (téléphone) : ces éléments n'existent que là ; ailleurs ils sont cachés */
.publier-vide, .vignette-apercu, .conseil-publier, .copier-publier { display: none; }
.publier-vide { border: 2px dashed var(--encre-douce); padding: 20px 16px; text-align: center; }
.publier-vide p { margin: 0 0 6px; }
.conseil-publier { margin: 0 0 12px; padding: 10px 12px; border: 2px solid var(--noir); font-weight: 700; font-size: var(--step--1); }
.copier-publier { width: 100%; min-height: 48px; margin: 0 0 14px; }

/* Résultat de la création */
#sortie { margin-top: 28px; padding-top: 18px; border-top: 2px solid var(--noir); }
#sortie p { font-size: var(--step--1); margin: 0 0 12px; }
.galerie { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
.galerie figure { margin: 0; min-width: 0; }
.galerie img { width: 100%; display: block; border: 1px solid var(--noir); }
.galerie figcaption { font-size: var(--step--1); margin-top: 6px; }
.galerie a { color: var(--noir); font-weight: 700; }

/* Aperçu */
.zone { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; padding: 24px 16px; background: var(--fond-apercu); }
.nav-images { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; font-weight: 700; }
.nav-images button { min-width: 52px; }
/* Le "cadre" a la taille de l'aperçu réduit ; la scène (1080 de large, --h de haut) est réduite à l'intérieur */
.cadre { position: relative; width: calc(1080px * var(--s)); height: calc(var(--h) * 1px * var(--s)); overflow: hidden; box-shadow: 0 6px 30px rgba(0,0,0,.35); background: #000; }
.interdit { position: absolute; left: 0; right: 0; background: rgba(255, 0, 80, .28); display: none; pointer-events: none;
  color: #fff; font: 12px system-ui; padding: 4px 8px; }
body.zones .interdit { display: block; }
body[data-format="post"] .interdit, body[data-format="paysage"] .interdit { display: none !important; }   /* le post et le paysage n'ont pas de zones masquées */
.cadre.avec-photo { touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; }   /* touch-action : le doigt déplace la photo au lieu de faire défiler la page */
.cadre.avec-photo:active { cursor: grabbing; }

/* Réglages sans effet sur certains types : la source (calendrier / saisie) ne sert pas au Message ni à la Photo ;
   la bande de sponsors n'existe pas sur la Photo */
body[data-mode="message"] .s-source, body[data-mode="photo"] .s-source { display: none; }
/* (« Album photos » garde le choix des sponsors : ils servent à l'image « Merci à nos partenaires ») */
body[data-mode="photo"]:not([data-style-photo="carrousel"]) .s-sponsors { display: none; }

/* Type "Message" : ni calendrier ni saisie de match, seulement le titre et le texte */
body:not([data-mode="message"]) .s-message { display: none; }
body[data-mode="message"] .s-calendrier, body[data-mode="message"] .s-manuel, body[data-mode="message"] .cache-message { display: none; }
/* Texte du post : pour tous les types (texte-post.js), il n'y a donc plus rien à cacher ici */

/* Type "Classement" : pas de dates, de liste de matchs, de présentation ni de saisie manuelle ; on choisit seulement les équipes.
   Le message d'état n'est montré que pour ce type. */
body[data-mode="classement"] .periode, body[data-mode="classement"] #bloc-dates, body[data-mode="classement"] #liste-matchs,
body[data-mode="classement"] .s-presentation, body[data-mode="classement"] .s-manuel, body[data-mode="classement"] .cache-message { display: none; }
/* Sans le titre de période (caché ci-dessus), « Toutes les équipes » toucherait le choix du format : on remet de l'air */
body[data-mode="classement"] .filtres { margin-top: 16px; }
body:not([data-mode="classement"]) #etat-classement, #etat-classement:empty, body:not([data-mode="classement"]) #reessayer-classement { display: none; }

/* Type "Photo" : ni calendrier, ni saisie de match, ni photo de fond (les photos se choisissent dans leur propre bloc) */
body:not([data-mode="photo"]) .s-photo { display: none; }
body[data-mode="photo"] .s-calendrier, body[data-mode="photo"] .s-manuel, body[data-mode="photo"] .cache-message, body[data-mode="photo"] .s-fond { display: none; }
.sr-seulement { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }   /* lu par les lecteurs d'écran, invisible */
.legende-champ { min-height: 96px; }

/* Liste des photos choisies : une vignette, un nom, (une légende) et un bouton pour retirer */
.liste-photos { margin: 0 0 22px; }
.liste-photos:empty { display: none; }
.photo-ligne { display: grid; grid-template-columns: 72px minmax(0, 1fr) 48px; gap: 12px; align-items: start; padding: 10px 0; border-top: 1px solid var(--filet); }
.photo-ligne:last-child { border-bottom: 1px solid var(--filet); }
.photo-ligne .vignette { padding: 0; width: 72px; height: 72px; min-height: 72px; overflow: hidden; }
.photo-ligne .vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-ligne.actuelle .vignette { border-color: var(--orange); box-shadow: 0 0 0 3px var(--noir); }   /* la photo montrée dans l'aperçu */
.photo-ligne .nom-photo { font-weight: 700; font-size: var(--step--1); margin: 2px 0 4px; overflow-wrap: anywhere; }
.photo-ligne textarea { min-height: 72px; font-size: var(--step-0); }
/* Texte alternatif d'une photo : sur toute la largeur de la ligne (sous la vignette, le nom et les boutons ▲ ▼) */
.photo-ligne .alt-photo, .ligne-fond .alt-photo { grid-column: 1 / -1; margin: 0; }
.alt-champ { min-height: 96px; resize: none; overflow: hidden; margin-bottom: 8px; }
.alt-photo button { width: 100%; margin: 0 0 8px; }
.photo-ligne .legende-photo { display: none; }
.liste-photos.avec-legendes:not(.avec-honneur) .photo-ligne .legende-photo { display: block; }
/* Style « À l'honneur » : les champs de la personne sous chaque photo */
.photo-ligne .champs-honneur { display: none; gap: 6px; }
.liste-photos.avec-honneur .photo-ligne .champs-honneur { display: grid; }
.champs-honneur textarea.nom-saisie { min-height: 64px; }
.champs-honneur input { min-height: 44px; padding: 8px 10px; font-size: var(--step-0); }
.champs-honneur textarea { min-height: 64px; }
body:not([data-style-photo="honneur"]) .aide-honneur, body:not([data-style-photo="simple"]) .photo-simple { display: none; }
/* Style « Album photos » : le match, le photographe et l'image des partenaires ; un champ « Autre photographe » sous chaque photo */
body:not([data-style-photo="carrousel"]) .album-reglages, body:not([data-style-photo="carrousel"]) .album-sujet { display: none; }
body[data-style-photo="carrousel"] .choix-disposition { display: none; }   /* le carrousel est toujours en plein cadre */
/* Un match : la liste des matchs ; un événement : titre, dates, lieu, pastille */
body:not([data-sujet="match"]) .sujet-match, body:not([data-sujet="evenement"]) .sujet-evenement { display: none; }
label .facultatif { font-size: inherit; }
.album-reglages .case { margin-top: 4px; }
.champ-etiq { display: grid; gap: 2px; min-width: 0; margin: 0; }   /* pas la marge de « label » : l'écart est celui de la grille */
.champ-etiq > span { font-size: var(--step--2); font-weight: 600; margin: 0; }
.photo-ligne .etiq-credit { display: none; }
.liste-photos.avec-album .photo-ligne .etiq-credit { display: grid; }
.photo-ligne .credit-photo { display: none; min-height: 44px; padding: 8px 10px; }
.liste-photos.avec-album .photo-ligne .credit-photo { display: block; }
/* À droite de chaque photo : ✕ (retirer), puis ▲ ▼ (changer l'ordre) */
.boutons-photo { display: flex; flex-direction: column; gap: 4px; }
.photo-ligne .ordre-photo { padding: 0; min-width: 48px; min-height: 44px; font-size: var(--step--1); }
.actions-photo { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }   /* « Cadrer la photo » et « Retirer », sous le nom */
.actions-photo button { margin: 0; }
.photo-ligne .retirer-photo, .ligne-fond .retirer-photo { min-height: 44px; padding: 8px 14px; }
.photo-ligne .ordre-photo:disabled { opacity: .3; cursor: default; }

/* Selon la source choisie, on affiche le calendrier ou la saisie manuelle */
body[data-source="calendrier"] .s-manuel { display: none; }
body[data-source="manuel"] .s-calendrier { display: none; }
/* Rappel « Saisie manuelle » en haut du formulaire : seulement hors calendrier, et pas pour Message / Photo (sans source) */
body[data-source="calendrier"] .rappel-source, body[data-mode="message"] .rappel-source, body[data-mode="photo"] .rappel-source { display: none; }
.rappel-source .lien { min-height: 44px; }

/* Ordinateur (> 800 px) : l'aperçu reste visible quand on fait défiler le formulaire.
   La zone ne s'étire plus sur toute la hauteur (align-self: start) : elle fait la hauteur de l'écran et se colle en haut (sticky).
   Le fond de l'aperçu passe sur .appli pour que la colonne de droite reste colorée sur toute la page.
   Réservé aux grands écrans : sur téléphone, l'aperçu est un écran à part (voir plus bas). */
@media (min-width: 801px) {
  .cadrer-photo { display: none !important; }   /* sur ordinateur, l'aperçu est déjà à côté du formulaire */
  .appli { background: var(--fond-apercu); }
  .zone { position: sticky; top: 0; align-self: start; height: 100vh; overflow: hidden; }
}

/* Téléphone : un écran à la fois (Créer, Aperçu, Texte ou Publier, choisi par le menu du bas), barre d'action sur toute la largeur */
.menu-onglets { display: none; }   /* ordinateur : l'aperçu est déjà à côté du formulaire */
@media (max-width: 800px) {
  .appli { grid-template-columns: minmax(0, 1fr); }
  .formulaire { border-right: 0; padding: 18px 16px calc(var(--barre-h, 170px) + 20px); }
  .barre-action { width: auto; right: 0; padding-bottom: 0; }   /* la marge de l'iPhone (barre d'accueil) passe sous le menu */
  .barre-action .actions { grid-template-columns: 1fr; }
  body[data-onglet="apercu"] .formulaire { display: none; }
  /* Menu du bas : le noir marque l'écran où l'on est (comme tout ce qui est choisi) ; zones tactiles de 56 px */
  .menu-onglets { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 8px -16px 0; border-top: 1px solid var(--filet);
    padding-bottom: env(safe-area-inset-bottom); }
  .menu-onglets button { display: grid; justify-items: center; align-content: center; gap: 2px; min-height: 56px; margin: 0; padding: 4px 0;
    border: 0; background: transparent; color: var(--noir); font-size: var(--step--2); font-weight: 700; cursor: pointer; }
  .menu-onglets svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 2; }
  .menu-onglets button[aria-current="page"] { background: var(--noir); color: var(--papier); }
  /* Écran Texte : seulement le texte du post (et l'en-tête). Écran Publier : seulement les images prêtes (ou « rien à publier »).
     Écran Créer : tout sauf ces deux-là. */
  body[data-onglet="texte"] .formulaire > :not(.s-texte-post):not(.entete) { display: none; }
  body[data-onglet="publier"] .formulaire > :not(#sortie):not(#publier-vide):not(.entete) { display: none; }
  body[data-onglet="formulaire"] .s-texte-post, body:not([data-onglet="publier"]) #sortie { display: none; }
  body[data-onglet="publier"] #publier-vide:not([hidden]) { display: block; }
  body[data-onglet="publier"] .conseil-publier:not([hidden]), body[data-onglet="publier"] .copier-publier:not([hidden]) { display: block; }
  #sortie { margin-top: 0; padding-top: 0; border-top: 0; }
  /* Sur téléphone, le conseil et la copie du texte sont sur l'écran Publier : plus dans la barre du bas (elle reste basse) */
  .barre-action .conseil, .barre-action .copier-barre { display: none !important; }
  /* Vignette de l'image, à gauche du résumé et du bouton principal (écrans Créer et Texte) : c'est la vraie scène, réduite à 52 px de large
     (--s écrase celui de l'aperçu pour ce seul bloc). Le bouton transparent par-dessus ouvre l'aperçu. */
  body[data-onglet="formulaire"] .cadre, body[data-onglet="texte"] .cadre {
    --s: calc(52 / 1080); position: fixed; left: 16px; bottom: calc(66px + env(safe-area-inset-bottom)); z-index: 21;
    box-shadow: none; outline: 2px solid var(--noir); pointer-events: none; }
  body[data-onglet="formulaire"] .vignette-apercu, body[data-onglet="texte"] .vignette-apercu {
    display: block; position: fixed; left: 16px; bottom: calc(66px + env(safe-area-inset-bottom)); z-index: 22;
    width: 52px; height: calc(52px * var(--h) / 1080); margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; }
  /* Le résumé et le bouton principal se décalent à droite de la vignette ; la ligne du résumé est assez haute pour que l'ensemble ait la hauteur de la vignette */
  body[data-onglet="formulaire"] .barre-action .infos, body[data-onglet="formulaire"] .barre-action .actions,
  body[data-onglet="texte"] .barre-action .infos, body[data-onglet="texte"] .barre-action .actions { margin-left: 64px; }
  body[data-onglet="formulaire"] .barre-action .infos, body[data-onglet="texte"] .barre-action .infos { min-height: calc(52px * var(--h) / 1080 - 58px); }
  /* Les écrans Créer et Texte : l'aperçu reste mis en page (indispensable pour l'export) mais hors de l'écran */
  body:not([data-onglet="apercu"]) .zone { position: absolute; top: 0; left: -200vw; width: 100vw; }
}

/* =====================================================================
   2. LA SCÈNE (le visuel 1080 de large, 1920 ou 1350 de haut selon le format : c'est ce qui est exporté)
   ===================================================================== */
/* Échelle des tailles de police de la scène : ratio 1,25 à partir de 40 px. Valeurs FIXES en px (jamais de vw ni de rem :
   l'image exportée doit être identique sur tous les appareils). app.js lit ces tailles dans le CSS (une seule source). */
:root {
  --t-2xs: 26px; --t-xs: 32px; --t-s: 40px; --t-m: 50px; --t-l: 63px; --t-xl: 78px; --t-2xl: 98px; --t-3xl: 122px;
  /* Chiffres en Grenat (heure, scores) : tailles propres à leur mise en page, hors échelle */
  --t-heure: 190px; --t-score: 360px; --t-deux-points: 240px;
}
/* --reduction (1 par défaut) : app.js la baisse jusqu'à 0,7 sur un visuel d'un seul match quand la place manque autour du bandeau (surtout en 4:5).
   Elle multiplie seulement les grands textes : heure, score, deux-points, verdict, noms de l'annonce (.equipe). */

.stage {
  position: absolute; top: 0; left: 0; width: 1080px; height: calc(var(--h) * 1px);
  transform: scale(var(--s)); transform-origin: top left;
  background: var(--noir); color: var(--blanc); overflow: hidden;
  isolation: isolate;   /* empilement propre à la scène, même sans transform (l'export le retire) : sinon le terrain en z-index -1 passe derrière le fond noir */
  font-family: 'AT Name Sans', system-ui, sans-serif; font-weight: 700;
  /* Name Sans : ss14 = 6 et 9 très lisibles (image réduite sur téléphone) ; ss02 = capitales rectangulaires (plus étroites, plus carrées
     comme les angles droits du club) ; case = trait d'union et tirets à la hauteur des capitales (« PULLY-LAUSANNE », « M15 – PROMOTION ») */
}
/* IMPORTANT : « .stage * » et non seulement « .stage ». À l'export, html-to-image ne recopie pas une fonction OpenType seulement HÉRITÉE d'un parent
   (le texte sortait sans ss02 alors que l'aperçu l'avait : plus large, donc coupé par la pastille). Déclarée sur chaque élément, elle passe. */
.stage, .stage * { font-feature-settings: "ss14", "ss02", "case"; }
/* Grenat a aussi des fonctions « ss02 » et « case » (autres dessins) : on les coupe pour garder l'heure, les scores et le grand « HBC MOUDON » comme avant */
.grenat, .grenat *, .heure, .heure *, .score, .score *, .deux-points, .couv-nombre, .couv-nombre *, .album-score, .album-score *,
.ligne .heure-l, .ligne .score-n { font-feature-settings: normal; }

/* Fond décoratif sans photo : lignes du terrain vues en perspective (6 % de blanc au plus près, plus pâles au loin). Derrière le contenu (.contenu vient après dans la scène). */
.terrain { position: absolute; inset: 0; pointer-events: none; }
.terrain svg { position: absolute; left: 0; top: 0; stroke: #fff; stroke-linecap: butt; }   /* la transparence de chaque trait est réglée par dessinerTerrain() */
#photo:not([hidden]):not(.en-cadre):not(.reduite) + .terrain { display: none; }   /* une photo remplit l'image : pas de décor (sauf si elle est dézoomée : .reduite, Album photos) */
#photo.reduite + .terrain { z-index: -1; }   /* photo dézoomée : le décor reste DERRIÈRE elle (la scène a son propre empilement : transform) */

/* Photo de fond (facultative) */
.photo { position: absolute; inset: 0; overflow: hidden; }
.photo .calque { position: absolute; inset: 0; }
/* La photo garde sa taille naturelle ; sa taille et sa position sont réglées par le JavaScript (zoom et glisser) */
.photo img { position: absolute; left: 50%; top: 50%; max-width: none; max-height: none; }
.cadre.avec-photo { touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; }   /* touch-action : le doigt déplace la photo au lieu de faire défiler la page */
.cadre.avec-photo:active { cursor: grabbing; }
/* Aspect des photos (choix « Aspect des photos », posé sur #scene par maj()). Le filtre est sur l'<img> seule :
   le voile, les textes et les logos ne changent pas. */
#scene[data-aspect="nb"] .photo img { filter: grayscale(1) contrast(1.15) brightness(1.05); }
/* Les vignettes du formulaire montrent l'effet tout de suite (sans passer par l'aperçu) */
body[data-aspect="nb"] #fond-vignette, body[data-aspect="nb"] .ligne-fond img, body[data-aspect="nb"] .photo-ligne .vignette img { filter: grayscale(1) contrast(1.15) brightness(1.05); }
/* « Album photos » : la vignette de la 1re photo montre la couverture, qui a son propre choix (« Photo de couverture ») */
body[data-mode="photo"][data-style-photo="carrousel"][data-couv-aspect="nb"] .photo-ligne:first-child .vignette img { filter: grayscale(1) contrast(1.15) brightness(1.05); }
body[data-mode="photo"][data-style-photo="carrousel"][data-couv-aspect="couleur"] .photo-ligne:first-child .vignette img { filter: none; }
.aspect-photo { margin: 14px 0; }
.photo::after { content: ""; position: absolute; inset: 0; opacity: var(--voile, 1);
  background: linear-gradient(to bottom, rgba(13,13,13,.60) 0%, rgba(13,13,13,.50) 30%, rgba(13,13,13,.82) 65%, rgba(13,13,13,.95) 100%); }

/* Bande de sponsors : en bas de l'image, à la distance --bas-base du bord (Story : dans la zone masquée par Instagram, voulu), alignée sur les marges du contenu.
   Chaque logo est sur une pastille blanc cassé (angles droits) : il reste lisible sur fond sombre, photo ou orange. */
.sponsors { position: absolute; left: 90px; right: 90px; bottom: var(--bas-base);
  display: flex; flex-wrap: wrap; align-items: stretch; align-content: flex-end; gap: 12px; }   /* plusieurs rangées si les logos ne tiennent pas sur une ligne */
.sponsor { background: var(--blanc); padding: 10px 18px; display: flex; align-items: center; }
.sponsor img { height: calc(var(--logo-h) * var(--taille, 1)); width: auto; display: block; }

/* Zone de sécurité : en Story, Instagram recouvre ~250px en haut et ~340px en bas (--haut et --bas changent avec le format) */
.contenu { position: absolute; left: 90px; right: 90px; top: var(--haut); bottom: var(--bas);
  display: flex; flex-direction: column; justify-content: space-between; }
.logo { width: 400px; color: var(--blanc); }
.logo svg { width: 100%; height: auto; display: block; }

.r { display: block; overflow: hidden; line-height: 1; padding: .06em 0; }
.r > span { display: block; }
.grenat { font-family: 'Grenat', 'AT Name Sans', sans-serif; font-weight: 900; }
.fit > span { display: inline-block; white-space: nowrap; }   /* permet de mesurer la largeur du texte */
/* Noms d'équipes longs : le texte peut passer sur 2 lignes (voir ajusterLignes() dans app.js) */
.fit.multi > span { display: block; white-space: normal; text-wrap: balance; }
.barre { height: 10px; background: var(--orange); }
/* Titre sur bandeau (liste, classement, message) : le même bandeau orange que le résultat et l'annonce (.bande, forme tirée par formeBandeau()),
   avec le titre (et le sous-titre) en noir dessus. Les marges de .entete-bande sont la place que le bandeau peut déborder en haut et en bas :
   plus elles sont grandes, plus les cassures sont marquées. Plus petites en Post, et encore plus dans un grand classement (.bande-compacte :
   le bandeau colle alors au texte, bord droit). */
.entete-bande { margin: 76px 0 44px; }   /* 76 = 20 px de garde sous le logo (BANDE_GARDE_LOGO) + 56 de place pour le bandeau */
body[data-format="post"] .entete-bande { margin: 58px 0 34px; }
.bande-compacte .entete-bande { margin: 40px 0 16px; }
.entete-bande .titre, .entete-bande .titre-classement, .entete-bande .titre-message { color: var(--noir); }
.entete-bande .sous-titre { color: rgba(13, 13, 13, .75); }
.orange { color: var(--orange); }
.gris { color: var(--gris); font-weight: 500; }
.salle.dom, .prochain-lieu.dom { color: var(--orange); }   /* la salle d'un match à domicile (gris à l'extérieur) */

.categorie { font-size: var(--t-m); margin-top: 14px; }
/* Rappel (« Ce soir », « Demain ») : en orange, plus grand */
.categorie.rappel { color: var(--orange); font-size: var(--t-l); font-weight: 800; text-transform: uppercase; }
/* Pastille orange : indique QUELLE équipe de Moudon joue (même style que "Victoire") */
.tag { font-size: var(--t-s); margin-top: 44px; }
/* Pastilles (en haut, et .tag-bande dans le bandeau de l'annonce et du résultat) : même style de texte ; seules les couleurs et les marges intérieures changent */
.tag > span, .tag-bande > span { font-weight: 800; text-transform: uppercase; }
.tag > span { background: var(--orange); color: var(--noir); padding: .08em .45em .14em; }
.date { font-size: var(--t-2xl); font-weight: 800; }
.heure { font-family: 'Grenat', sans-serif; font-weight: 900; font-size: calc(var(--t-heure) * var(--reduction, 1)); line-height: .95; color: var(--orange); }
/* Annonce : le titre (« Prochain match » / rappel) passe en capitales ; la pastille de l'équipe est dans le bandeau (.tag-bande), pas en haut */
body[data-mode="annonce"] .haut .tag { display: none; }
/* Résultat : idem, la pastille de l'équipe est dans le bandeau (.tag-bande), pas en haut (une liste de résultats montre l'équipe sur chaque ligne) */
body[data-mode="resultat"] .haut .tag { display: none; }
body[data-mode="annonce"] .categorie { margin-top: 32px; font-weight: 700; text-transform: uppercase; }
body[data-mode="annonce"] .categorie:not(.rappel) { color: var(--blanc); }   /* « Prochain match » : en blanc cassé (le gris le rendait plus faible que le bandeau), le rappel reste orange */
body[data-mode="annonce"] .categorie.rappel { font-weight: 800; }
/* La salle se rapproche des noms (marge négative) : l'espace libre de la colonne est réparti en 3 parts égales, et celle sous les noms ne sert à rien ;
   ainsi les deux autres, autour du bandeau, grandissent et ses cassures sont plus marquées */
body[data-mode="annonce"] .bas { margin-top: -50px; }
/* En Post, il y a moins de place : avec un nom d'adversaire sur 2 lignes (ex. « HC Yverdon Bas »), -50px collait la salle au nom.
   On garde donc une petite marge positive : environ 20 px entre les deux dans le pire cas mesuré (Post, rappel, pastille, sponsors) */
body[data-format="post"][data-mode="annonce"] .bas { margin-top: 10px; }
/* Bandeau de l'annonce (même .bande que le résultat, toujours orange) : tout est en noir dessus.
   .tag-bande (pastille noire, texte orange) : dans les deux bandeaux ; le noir se lit aussi sur le gris (défaite) et le blanc cassé (nul) */
.bloc-date .date, .bloc-date .heure { color: var(--noir); }
.tag-bande { font-size: var(--t-xl); margin-bottom: 8px; }
/* Post (1350 px de haut) : moins de place, donc heure, date et pastille plus petites (sinon le bandeau mord sur le titre et sur les noms) */
body[data-format="post"] { --t-heure: 150px; }
body[data-format="post"] .bloc-date .date { font-size: var(--t-xl); }
body[data-format="post"] .tag-bande { font-size: var(--t-l); }
.tag-bande > span { background: var(--noir); color: var(--orange); padding: .04em .3em .1em; }
.equipe { font-size: calc(var(--t-3xl) * var(--reduction, 1)); font-weight: 800; text-transform: uppercase; }
.contre { font-size: var(--t-m); }
.salle { font-size: var(--t-m); }
.titre { font-size: var(--t-2xl); font-weight: 800; }
.scores { display: flex; justify-content: space-between; align-items: flex-end; margin-top: 70px; }
.score { font-family: 'Grenat', sans-serif; font-weight: 900; font-size: calc(var(--t-score) * var(--reduction, 1)); line-height: .9; }
.deux-points { font-family: 'Grenat', sans-serif; font-weight: 900; font-size: calc(var(--t-deux-points) * var(--reduction, 1)); line-height: .9; color: var(--gris); padding-bottom: 20px; }
.noms { display: flex; justify-content: space-between; align-items: flex-start; margin-top: 30px; gap: 30px; }
/* align-items: flex-start : chaque nom garde la hauteur de son texte. Sans ça, un adversaire sur 2–3 lignes étirait la case de
   « HBC Moudon ». */
.nom { font-size: var(--t-l); font-weight: 800; text-transform: uppercase; }
.noms .r { flex: 1 1 0; min-width: 0; }   /* deux colonnes égales : un nom long passe sur 2 lignes au lieu de déborder */
.noms .r:last-child { text-align: right; }

/* Résultat : un bandeau de couleur (orange = victoire, gris = défaite, blanc cassé = nul) derrière le titre, le verdict, le score et les noms.
   Sa forme est un clip-path « anguleux » tiré au hasard par app.js (formeBandeau) : chaque visuel a ses propres cassures, comme les coupes du Grenat.
   app.js règle aussi data-issue (v / d / n) sur la scène et la marge --E que le bandeau déborde en haut et en bas. */
/* .avec-bande = bloc qui contient un bandeau (.m-resultat et .bloc-date de l'annonce) : le bandeau (z-index -1) passe derrière son texte, sans passer derrière la scène */
.avec-bande { position: relative; z-index: 0; }
.bande { position: absolute; left: -90px; right: -90px; z-index: -1; background: var(--orange); }
/* « Changer la forme du bandeau » : les cassures glissent vers leur nouvelle place au lieu de sauter (même nombre de sommets, donc animable) */
@media (prefers-reduced-motion: no-preference) { .bande { transition: clip-path .45s ease; } }   /* -90px = les marges de .contenu : le bandeau va d'un bord à l'autre de l'image */
/* Avec une photo de fond : le bandeau devient semi-transparent pour laisser voir la photo (app.js ajoute .avec-photo-fond sur la scène).
   L'opacité est sur le bandeau seul : le texte, lui, reste net. */
.stage.avec-photo-fond .bande { opacity: var(--bande-opacite, .8); }
.stage[data-issue="d"] .bande, .stage[data-issue="x"] .bande { background: var(--gris); }   /* d = défaite, x = match à l'extérieur (annonce) */
.stage[data-issue="n"] .bande { background: var(--blanc); }
.m-resultat .titre { font-size: var(--t-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: rgba(13,13,13,.7); }
.verdict-bloc { margin-top: 10px; }
.verdict { font-size: calc(var(--t-3xl) * var(--reduction, 1)); font-weight: 800; text-transform: uppercase; color: var(--noir); }
.m-resultat .scores { margin-top: 20px; }
.m-resultat .noms { margin-top: 24px; }
/* Sur le bandeau tout est en noir (l'orange disparaîtrait sur la victoire) ; le score de l'adversaire est atténué (victoire ou défaite) */
.m-resultat .score, .m-resultat .nom { color: var(--noir); }
.m-resultat .deux-points { color: rgba(13,13,13,.35); }
.m-resultat .score.attenue, .ligne .score-n.moins { opacity: .65; }   /* .65 : contraste du score adverse mesuré à 4,0:1 sur l'orange, 4,6:1 sur le gris, 5,9:1 sur le blanc cassé (avec .55 : 3,2:1 ; avec .35 : 2,1:1). Dans une liste (.score-n.moins), c'est le score le plus bas : l'orange atténué d'une défaite de Moudon tombe à ~3,7:1, assez pour un grand texte gras. */
/* Pas de petit titre « Résultat final » quand le score est connu : le grand mot (Victoire, Résultat, Match nul) suffit.
   Il ne reste que si le score manque (pas de grand mot). */
.stage[data-issue="v"] .m-resultat .titre, .stage[data-issue="d"] .m-resultat .titre, .stage[data-issue="n"] .m-resultat .titre { display: none; }

/* Liste de plusieurs matchs (une image avec 2 à 5 matchs) */
.liste { display: none; flex: 1; flex-direction: column; min-height: 0; }
body[data-vue="liste"] .liste { display: flex; }
body[data-vue="liste"] .m-annonce, body[data-vue="liste"] .m-resultat,
body[data-vue="liste"] .bas, body[data-vue="liste"] .categorie, body[data-vue="liste"] .tag { display: none; }

/* Couverture d'un carrousel (1re image) : titre et dates en haut, un grand chiffre au centre (victoires ou matchs),
   les pastilles des équipes, puis « Glissez pour voir les matchs → ». Le chiffre est en AT Name Sans très gras : en Grenat, un « 1 » géant
   ressemblait à un bloc illisible. */
.couverture { display: none; flex: 1; flex-direction: column; min-height: 0; margin-top: 50px; }
body[data-vue="couverture"] .couverture { display: flex; }
body[data-vue="couverture"] .m-annonce, body[data-vue="couverture"] .m-resultat,
body[data-vue="couverture"] .bas, body[data-vue="couverture"] .categorie, body[data-vue="couverture"] .tag { display: none; }
.couv-centre { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; }
.couv-centre > * { flex-shrink: 0; }   /* sinon le chiffre serait écrasé (et coupé) au lieu de déborder : le débordement doit se mesurer */
/* --couv-reduction (1 par défaut) : baissée par dessinerCouverture() si les pastilles ne laissent pas assez de place au chiffre */
.couv-nombre { font-family: 'Grenat', sans-serif; font-size: calc(var(--t-score) * var(--couv-reduction, 1)); font-weight: 900; line-height: .9; color: var(--orange); }   /* Grenat comme les scores (même un « 1 » seul : choix du club) */
.couv-unite { font-size: var(--t-2xl); font-weight: 800; text-transform: uppercase; }
.couv-bilan { font-size: var(--t-m); margin-top: 12px; }
.couv-equipes { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 30px; }
.pastille-couv { background: var(--orange); color: var(--noir); font-size: var(--t-s); font-weight: 800; text-transform: uppercase; line-height: 1; padding: .14em .45em .18em; }
.couv-suite { font-size: var(--t-m); font-weight: 800; color: var(--orange); margin-top: 36px; }

/* Classement : titre, tableau (une ligne par équipe du groupe), légende. La ligne de notre équipe est sur fond orange.
   Les lignes se partagent la place disponible : plus le groupe est grand, plus la police rétrécit (classes dense / serre). */
.classement { display: none; flex: 1; flex-direction: column; min-height: 0; }
.classement, .classement * { font-variant-numeric: tabular-nums; }   /* chiffres de même largeur : place, J et Pts s'alignent bien (sur chaque élément : voir « .stage * » plus haut) */
body[data-vue="classement"] .classement { display: flex; }
body[data-vue="classement"] .m-annonce, body[data-vue="classement"] .m-resultat,
body[data-vue="classement"] .bas, body[data-vue="classement"] .categorie { display: none; }
.titre-classement { font-size: var(--t-xl); font-weight: 800; }
/* 5 colonnes : repère montée / descente, place, équipe, matchs joués, points */
.entete-classement, .ligne-c > span { display: grid; grid-template-columns: var(--cols-c, 34px 56px minmax(0, 1fr) 84px 110px); align-items: center; }
/* Petit groupe (6 équipes en Story) : les lignes ont de la place, le texte passe de 40 à 50 px (--t-m) et les colonnes s'élargissent (voir dessinerClassement) */
.classement.ample { --cols-c: 40px 72px minmax(0, 1fr) 96px 130px; }
.classement.ample .lignes-c .ligne-c { font-size: var(--t-m); max-height: 112px; }
.classement.aere .lignes-c .ligne-c { max-height: 104px; }   /* texte normal, lignes plus hautes */
.classement.ample .tri { border-left-width: 12px; border-right-width: 12px; }
.classement.ample .tri.monte { border-bottom-width: 19px; }
.classement.ample .tri.descend { border-top-width: 19px; }
.entete-classement { margin-top: 0; padding: 0 16px 10px; font-size: var(--t-xs); font-weight: 500; color: var(--gris); }
.entete-classement span:nth-child(n+4), .c-j, .c-pts { text-align: right; }
.lignes-c { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.ligne-c { flex: 1 1 0; min-height: 0; max-height: 88px; display: flex; padding: 0 16px; font-size: var(--t-s); border-top: 3px solid rgba(189, 184, 174, .28); }
.ligne-c > span { flex: 1; min-width: 0; }
.lignes-c.dense .ligne-c { font-size: var(--t-xs); }
/* Très grand groupe (lignes de moins de 40 px, ex. 16 équipes en Post avec les sponsors) : le texte reste à 32 px, minimum lisible
   sur un téléphone ; on resserre seulement l'interligne et le trait de séparation pour que le texte tienne dans la ligne */
.lignes-c.serre .ligne-c { line-height: .95; border-top-width: 2px; }
.c-rang, .c-pts { font-weight: 800; }
.c-nom { font-weight: 700; min-width: 0; }
.c-nom > span { display: inline-block; white-space: nowrap; }
.c-j { color: var(--gris); font-weight: 500; }
.ligne-c.nous { background: var(--orange); color: var(--noir); }
.ligne-c.nous .c-j { color: var(--noir); }
/* Repères de zone : triangle vers le haut (montée) ou vers le bas (descente), de la couleur du texte */
.tri { display: block; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; }
.tri.monte { border-bottom: 16px solid currentColor; }
.tri.descend { border-top: 16px solid currentColor; }
.legende-classement { margin-top: 18px; font-size: var(--t-xs); }
.legende-classement > span { white-space: nowrap; }
.legende-classement .tri { display: inline-block; margin-right: 8px; vertical-align: middle; }
.legende-classement .tri.descend:not(:first-child) { margin-left: 34px; }   /* écart seulement s'il y a aussi la légende de la promotion */
/* Petits textes (classement, lignes des listes) : taille optique « Text » (opsz 14, plus ouverte et espacée), pensée pour le petit texte.
   Sur téléphone l'image est réduite à ~36 % : ces textes de 32-40 px s'y lisent comme du 12-15 pt. Les grands textes gardent l'opsz automatique.
   Le texte est un peu plus large : ajuster() mesure, donc les noms et salles longs se raccourcissent seuls (« … »). */
.ligne-c, .ligne-c *, .entete-classement, .entete-classement *, .legende-classement, .legende-classement *,
#lignes .ligne .info, #lignes .ligne .info * { font-variation-settings: "opsz" 14; }   /* « * » : à l'export, une valeur seulement héritée n'est pas recopiée */

/* Message libre : un titre, la barre orange, puis le texte (retours à la ligne conservés) */
.message { display: none; flex: 1; flex-direction: column; min-height: 0; }
body[data-vue="message"] .message { display: flex; }
body[data-vue="message"] .m-annonce, body[data-vue="message"] .m-resultat,
body[data-vue="message"] .bas, body[data-vue="message"] .categorie, body[data-vue="message"] .tag { display: none; }
body[data-mode="message"] .m-annonce, body[data-mode="message"] .m-resultat { display: none; }
.titre-message { font-size: var(--t-2xl); font-weight: 800; line-height: 1.05; }
.texte-message { flex: 1; min-height: 0; font-size: var(--t-m); font-weight: 500; line-height: 1.3; color: var(--blanc); }
.texte-message.grand { font-size: var(--t-l); }   /* message court : voir dessinerMessage() */
.texte-message .p { white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.3em; }   /* une ligne vide garde sa hauteur */
.texte-message .gras { font-weight: 800; }
.texte-message .italique, .titre-message .italique { font-style: italic; }
.titre-message .gras { font-weight: 1000; }
/* Puce : petit carré orange et retrait */
.texte-message .puce { position: relative; padding-left: .85em; }
.texte-message .puce::before { content: ""; position: absolute; left: 0; top: .55em; width: .34em; height: .34em; background: var(--orange); }

/* Type Photo : la photo est l'élément principal. Le logo passe en bas à gauche, sous une légende facultative.
   Deux dispositions : "plein" (la photo remplit l'image) et "cadre" (photo entière, sans recadrage, sur fond noir). */
.photo-legende { display: none; }
body[data-vue="photo"] .photo-legende { display: block; order: 1; }
body[data-vue="photo"] .haut { order: 2; }
body[data-vue="photo"] .logo { width: 320px; }
body[data-vue="photo"] .m-annonce, body[data-vue="photo"] .m-resultat,
body[data-vue="photo"] .bas, body[data-vue="photo"] .categorie, body[data-vue="photo"] .tag { display: none; }
/* Plein cadre : légende puis logo, empilés en bas de la zone de sécurité (même sans légende, le logo reste en bas) */
body[data-vue="photo"][data-disposition="plein"] .contenu { justify-content: flex-end; }
body[data-vue="photo"][data-disposition="plein"] .photo-legende { margin-bottom: 50px; }
/* Cadre : la légende suit la photo (sa marge du haut est réglée par le JavaScript), le logo reste en bas */
body[data-vue="photo"][data-disposition="cadre"] .contenu { justify-content: flex-start; }
body[data-vue="photo"][data-disposition="cadre"] .haut { margin-top: auto; }
.legende { font-size: var(--t-s); font-weight: 500; line-height: 1.3; color: var(--blanc); text-shadow: 0 2px 20px rgba(0, 0, 0, .55); }
.legende > span { display: block; }
.legende .p { white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.3em; }
.legende .gras { font-weight: 800; }
.legende .italique { font-style: italic; }
/* Style « À l'honneur » : motif (pastille orange), nom en grand, équipe, détail ; empilés au-dessus du logo */
.honneur { display: none; }
body[data-vue="photo"][data-honneur="oui"] .honneur { display: block; order: 1; margin-bottom: 50px; }
.pastille-honneur { font-size: var(--t-s); margin-bottom: 26px; }
.pastille-honneur > span { background: var(--orange); color: var(--noir); font-weight: 800; text-transform: uppercase; padding: .08em .45em .14em; }
.nom-honneur { font-size: var(--t-3xl); font-weight: 800; text-transform: uppercase; line-height: 1.02; color: var(--blanc); text-shadow: 0 2px 24px rgba(0, 0, 0, .55); }
.nom-honneur > span { display: block; overflow-wrap: anywhere; white-space: pre-line; }   /* pre-line : un duo ou un trio = un nom par ligne */
.equipe-honneur { font-size: var(--t-m); margin-top: 18px; text-shadow: 0 2px 20px rgba(0, 0, 0, .55); }
.detail-honneur { margin-top: 22px; }
/* Plus de place pour lire le texte : le dégradé du bas commence plus haut */
body[data-vue="photo"][data-honneur="oui"] .photo::after { background: linear-gradient(to bottom, rgba(13,13,13,0) 35%, rgba(13,13,13,.6) 60%, rgba(13,13,13,.92) 100%); }
/* Plein cadre : pas de voile sur toute la photo, seulement un dégradé en bas pour lire la légende */
body[data-vue="photo"] .photo::after { opacity: 1;
  background: linear-gradient(to bottom, rgba(13,13,13,0) 55%, rgba(13,13,13,.55) 75%, rgba(13,13,13,.9) 100%); }
/* « Album photos », images 2 et suivantes : le texte est tout petit, donc le fond sombre est plus court que celui de la couverture (voir plus haut) ;
   il commence à 62 % de la hauteur : plus court, le gris de « Photo : … » se lit mal sur une photo claire (essayé à 70 % et 80 %) */
body[data-vue="photo"][data-album="oui"] .photo::after {
  background: linear-gradient(to bottom, rgba(13,13,13,0) 62%, rgba(13,13,13,.6) 78%, rgba(13,13,13,.9) 100%); }
/* « Album photos », images 2 et suivantes : « HBC Moudon 27 – 22 HC Pully » (ou le titre de l'événement) et « Photo : … », tout petit au-dessus d'un logo réduit :
   le texte ne doit pas voler la place de la photo. Exception voulue par le club à la règle « 32 px au minimum » : 26 px (--t-2xs), car c'est une info secondaire */
.album-infos { display: none; text-shadow: 0 2px 20px rgba(0, 0, 0, .55); }
body[data-vue="photo"][data-album="oui"] .album-infos { display: block; order: 1; margin-bottom: 24px; }
body[data-vue="photo"][data-album="oui"] .logo { width: 130px; }   /* même taille dans les trois formats */
.album-ligne { font-size: var(--t-2xs); font-weight: 800; }
#c-album-credit { font-size: var(--t-2xs); margin-top: 2px; }
.album-credit { font-size: var(--t-xs); margin-top: 8px; }

/* « Album photos », couverture : la photo d'abord (plein cadre, glisser/pincer actifs), le logo en haut ;
   en bas : pastille de l'équipe, verdict, score (Grenat), noms, date, photographe et « Glissez pour voir les photos → » */
.album-couv { display: none; text-shadow: 0 2px 24px rgba(0, 0, 0, .55); }
body[data-vue="album"] .album-couv { display: block; }
body[data-vue="album"] .m-annonce, body[data-vue="album"] .m-resultat,
body[data-vue="album"] .bas, body[data-vue="album"] .categorie, body[data-vue="album"] .tag { display: none; }
/* Dégradé : léger en haut (le logo reste lisible), transparent au milieu (la photo), sombre en bas (le texte) */
body[data-vue="album"] .photo::after { opacity: 1;
  background: linear-gradient(to bottom, rgba(13,13,13,.45) 0%, rgba(13,13,13,0) 20%, rgba(13,13,13,0) 35%, rgba(13,13,13,.7) 58%, rgba(13,13,13,.94) 100%); }
.album-pastille { font-size: var(--t-s); margin-bottom: 20px; }
.album-pastille > span { background: var(--orange); color: var(--noir); font-weight: 800; text-transform: uppercase; padding: .08em .45em .14em; text-shadow: none; }
.album-verdict { font-size: var(--t-2xl); font-weight: 800; text-transform: uppercase; }
/* Titre d'un événement (« Tournoi de Noël ») : même style que le verdict, sur 2 lignes au plus (voir texteNom() dans app.js) */
.album-titre { font-size: var(--t-2xl); font-weight: 800; text-transform: uppercase; line-height: 1; }
.album-lieu { font-size: var(--t-s); margin-top: 6px; }
/* Légende libre d'un événement, juste sous le titre */
.album-legende { font-size: var(--t-m); font-weight: 800; line-height: 1.1; margin-top: 14px; }
.album-score { font-family: 'Grenat', sans-serif; font-weight: 900; font-size: var(--t-heure); line-height: .95; margin-top: 6px; }
.album-score .nous { color: var(--orange); }
.album-score .sep { color: var(--gris); margin: 0 .12em; }
.album-noms { font-size: var(--t-m); font-weight: 800; text-transform: uppercase; margin-top: 14px; }
.album-date { font-size: var(--t-s); margin-top: 18px; }
.album-suite { font-size: var(--t-s); font-weight: 800; color: var(--orange); margin-top: 30px; }
/* Couverture d'un match (body[data-couverture="images"], posé par dessinerAlbum()) : « En images » remplace le verdict,
   le score reste mais plus petit (elle doit faire « partage de photos », pas « résultat ») ; le photographe en blanc au lieu de gris. */
body[data-vue="album"][data-couverture="images"] .album-score { font-size: var(--t-xl); margin-top: 14px; }
body[data-vue="album"][data-couverture="images"] .album-noms { font-size: var(--t-s); margin-top: 10px; }
body[data-vue="album"][data-couverture="images"] #c-album-credit-couv { color: var(--blanc); font-weight: 800; margin-top: 10px; }

/* « Album photos », dernière image : « Merci à nos partenaires », les logos en grand (taille réglée par app.js : --logo-p),
   puis le prochain match de l'équipe, en bas */
.partenaires { display: none; flex: 1; flex-direction: column; min-height: 0; margin-top: 50px; }
body[data-vue="partenaires"] .partenaires { display: flex; }
body[data-vue="partenaires"] .m-annonce, body[data-vue="partenaires"] .m-resultat,
body[data-vue="partenaires"] .bas, body[data-vue="partenaires"] .categorie, body[data-vue="partenaires"] .tag { display: none; }
.partenaires > * { flex-shrink: 0; }   /* rien n'est écrasé : le débordement doit se mesurer (voir dessinerPartenaires) */
.titre-partenaires { font-size: var(--t-xl); font-weight: 800; line-height: 1.05; }
.partenaires .barre { margin: 34px 0 44px; }
.partenaires-logos { display: flex; flex-wrap: wrap; gap: 20px; }
.partenaires-logos .sponsor { padding: 18px 28px; }
.partenaires-logos .sponsor img { height: calc(var(--logo-p, 110px) * var(--taille, 1)); }
.prochain { margin-top: auto; padding-top: 40px; }
.prochain-titre { font-size: var(--t-s); }
.prochain .album-pastille { margin: 16px 0 0; }
.prochain-quand { font-size: var(--t-l); font-weight: 800; margin-top: 14px; }
.prochain-adv { font-size: var(--t-m); margin-top: 8px; }
.prochain-lieu { font-size: var(--t-xs); margin-top: 10px; }

/* Cadre : la boîte de la photo est placée et dimensionnée par le JavaScript (--cx, --cy, --cw, --ch) */
.photo.en-cadre { inset: auto; left: var(--cx); top: var(--cy); width: var(--cw); height: var(--ch); }
.photo.en-cadre::after { display: none; }

.sous-titre { font-size: var(--t-m); margin-top: 6px; }
.lignes { margin-top: 0; flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* Les lignes se partagent la place disponible (jusqu'à la zone masquée d'Instagram) ; 300px max pour les listes courtes */
.ligne { flex: 1 1 0; min-height: 0; max-height: 300px; display: flex; border-top: 3px solid rgba(189, 184, 174, .28); }
.ligne > span { flex: 1; min-width: 0; display: flex; align-items: center; gap: 30px; }   /* min-width:0 = peut rétrécir sous la largeur du texte */
.ligne .info { flex: 1; min-width: 0; }
.lignes > .ligne:first-child { border-top: 0; }   /* la première ligne touche le bandeau du titre : pas de filet en double */
.ligne .info > div { line-height: 1.15; }
.ligne .info span { display: inline-block; white-space: nowrap; }
.ligne .adv { font-size: var(--t-m); font-weight: 800; text-transform: uppercase; }
/* Pastille de l'équipe de Moudon (nom court) : exactement la taille du nom de l'adversaire (1em de .adv, que tailleCommuneAdversaires() règle).
   inline-block + line-height 1 : sa boîte (1,16 em) tient dans la ligne (1,2 em, voir .avec-pastille), donc elle ne mord jamais sur la ligne du dessus
   ou du dessous quand le nom passe sur 2 ou 3 lignes. */
.ligne .adv .tag-equipe { display: inline-block; background: var(--orange); color: var(--noir); font-weight: 800; font-size: 1em; line-height: 1; text-transform: uppercase; margin: 0; padding: .06em .4em .1em; vertical-align: baseline; white-space: nowrap; }
.ligne .adv.avec-pastille, .ligne .adv.avec-pastille.deux-lignes { line-height: 1.2; }
/* Nom trop long pour une ligne à la taille commune de la liste : 2 lignes (voir tailleCommuneAdversaires() dans app-match.js) */
.ligne .adv.deux-lignes { line-height: 1; }
.ligne .info .adv.deux-lignes span { display: block; white-space: normal; text-wrap: balance; overflow-wrap: anywhere; }
.ligne .lieu { font-size: var(--t-xs); color: var(--gris); font-weight: 500; }
.ligne .lieu .dom { color: var(--orange); font-weight: 500; }   /* la salle d'un match à domicile, en orange (le gris reste pour l'extérieur) ; même graisse que le reste de la ligne : une salle longue ne doit pas être plus large */
.ligne .heure-l { flex: 0 0 270px; font-family: 'Grenat', sans-serif; font-weight: 900; font-size: var(--t-xl); line-height: 1; color: var(--orange); }
.ligne .heure-l.exterieur { color: var(--gris); }   /* match à l'extérieur */
/* Résultat : scores en diagonale. .equipes-l = les deux lignes d'équipe ; chaque score est posé en absolu, plus grand que sa ligne (S = 2,1 × la taille du nom) :
   celui de l'équipe qui reçoit en haut à gauche, celui de la visiteuse en bas à droite. Les noms réservent la place des deux scores côte à côte :
   --score-l = largeur d'un score et --score-ecart = écart entre les deux, en fois S (donc 2 × --score-l + --score-ecart en tout). */
.ligne .equipes-l { --S: calc(var(--taille-equipe, 50px) * 2.1); --score-l: 1.3; --score-ecart: .15; position: relative; padding-right: calc(var(--S) * (var(--score-l) * 2 + var(--score-ecart))); }
.ligne .score-n { position: absolute; width: calc(var(--S) * var(--score-l)); text-align: right; font-family: 'Grenat', sans-serif; font-weight: 900; font-size: var(--S); line-height: .68; color: var(--blanc); }
.ligne .score-n.score-haut { top: 0; right: calc(var(--S) * (var(--score-l) + var(--score-ecart))); }
.ligne .score-n.score-bas { bottom: 0; right: 0; }
.ligne .score-n.nous { color: var(--orange); }
.lignes.dense .lieu { font-size: var(--t-xs); }   /* pas plus petit : 32 px est le minimum lisible sur un téléphone */
/* 260 px : « 09h00 » ou « 00h00 » en Grenat font 255 px à cette taille (mesuré). Plus étroit, la colonne s'élargit toute seule et les noms ne sont plus alignés d'une ligne à l'autre. */
.lignes.dense .heure-l { font-size: var(--t-xl); flex-basis: 260px; }

/* Très petit écran ou zoom à 400 % (moins de 280 px de large) : les types passent l'un sous l'autre, sans chevauchement */
@media (max-width: 280px) {
  .choix.choix-modes label, .choix.choix-modes label.mode-grand { grid-column: span 6; }
  .choix span { overflow-wrap: anywhere; }
}

/* Zoom à 400 % (écran de 320 px de large mais seulement ~256 px de haut) : la barre fixe et la vignette cacheraient presque tout le formulaire.
   Dans ce cas seulement, la barre se place à la fin de la page (non fixe) et la vignette disparaît. Un téléphone normal (plus de 520 px de haut) n'est pas touché. */
@media (max-width: 480px) and (max-height: 520px) {
  .barre-action { position: static; }
  .formulaire, body[data-onglet] .formulaire { padding-bottom: 20px; }
  body[data-onglet] .vignette-apercu { display: none; }
  body[data-onglet="formulaire"] .cadre, body[data-onglet="texte"] .cadre { position: static; outline: 0; }
  body[data-onglet] .barre-action .infos, body[data-onglet] .barre-action .actions { margin-left: 0; min-height: 0; }
}

/* Bouton « Changer le bandeau et le fond », au-dessus de l'aperçu : quand l'image a un bandeau (résultat, annonce d'un seul match, liste, classement, message) ou, sur la couverture d'un carrousel, un fond terrain */
.outils-apercu { display: none; margin-bottom: 14px; }
body[data-mode="resultat"][data-vue="match"] .outils-apercu, body[data-mode="annonce"][data-vue="match"] .outils-apercu,
body[data-vue="liste"] .outils-apercu, body[data-vue="classement"] .outils-apercu, body[data-vue="message"] .outils-apercu,
body[data-vue="couverture"] .outils-apercu { display: block; }   /* partout où l'image a un bandeau, et sur la couverture d'un carrousel (sans bandeau, mais avec le fond terrain) */
.outils-apercu .sans-bandeau, body[data-vue="couverture"] .outils-apercu .avec-bandeau { display: none; }
body[data-vue="couverture"] .outils-apercu .sans-bandeau { display: inline; }
/* Résultat dont le bandeau suit le match (forme-match.js) : seul le fond change, le bouton le dit */
body[data-forme-match="oui"] .outils-apercu .avec-bandeau { display: none; }
body[data-forme-match="oui"] .outils-apercu .sans-bandeau { display: inline; }
body[data-forme-match="oui"][data-photo-fond="oui"] .outils-apercu { display: none; }   /* photo de fond : le terrain est caché, le bouton ne changerait rien */
body[data-vue="couverture"][data-photo-fond="oui"] .outils-apercu { display: none; }   /* photo de fond : le terrain est caché et la couverture n'a pas de bandeau, le bouton ne changerait rien */   /* la couverture n'a pas de bandeau : le bouton ne change que le fond */

/* Affiche seulement ce qui correspond au type choisi (annonce ou résultat) */
body[data-mode="annonce"]  .m-resultat { display: none; }
body[data-mode="resultat"] .m-annonce  { display: none; }

/* ---------- Écran du code d'accès (interface, pas la scène) ---------- */
.acces { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: var(--space-s); background: var(--papier); }
.acces-boite { width: 100%; max-width: 380px; }
.acces-boite label { display: block; margin: var(--space-s) 0 6px; font-weight: 700; }
.acces-erreur { margin: 8px 0 0; color: var(--erreur); font-weight: 600; }
.acces-bouton { display: block; width: 100%; min-height: 56px; margin-top: var(--space-s); border: 0; background: var(--orange); color: var(--noir); font: inherit; font-weight: 800; font-size: var(--step-0); cursor: pointer; }

/* =====================================================================
   Format paysage 3:2 (1080 × 720) : seulement pour le « Album photos » (voir maj() dans app.js).
   Peu de hauteur, donc tout est plus petit. La grille du profil Instagram (3:4) ne montre que la bande centrale d'une image large
   (540 px sur 1080) : sur la couverture, le score, le verdict et les noms sont centrés dans cette bande.
   ===================================================================== */
body[data-format="paysage"] { --t-heure: 120px; }
body[data-format="paysage"] .contenu { left: 40px; right: 40px; }
body[data-format="paysage"] .logo { width: 220px; }
/* Images 2 et suivantes : le score et le photographe, en petit, juste au-dessus du logo */
body[data-format="paysage"][data-vue="photo"] .album-infos { margin-bottom: 10px; }/* Couverture : le logo en haut à gauche, hors de la bande centrale ; le texte centré, sur toute la hauteur */
body[data-format="paysage"][data-vue="album"] .contenu { justify-content: flex-start; }
body[data-format="paysage"][data-vue="album"] .album-couv { position: absolute; top: 0; bottom: 0; left: 50%; width: 540px; margin-left: -270px;
  display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
body[data-format="paysage"] .album-couv .r { max-width: 100%; }
body[data-format="paysage"] .album-pastille { font-size: var(--t-xs); margin-bottom: 8px; }
body[data-format="paysage"] .album-verdict { font-size: var(--t-xl); }
body[data-format="paysage"] .album-titre { font-size: var(--t-l); }
body[data-format="paysage"] .album-legende { font-size: var(--t-s); margin-top: 8px; }
body[data-format="paysage"] .album-lieu { font-size: var(--t-xs); margin-top: 4px; }
body[data-format="paysage"] .album-score { margin-top: 10px; }
body[data-format="paysage"] .album-noms { font-size: var(--t-m); margin-top: 12px; width: 100%; }
body[data-format="paysage"] .album-date { font-size: var(--t-s); margin-top: 12px; }
/* « Glissez pour voir les photos → » : dans le coin en bas à droite, hors de la bande centrale */
body[data-format="paysage"] .album-suite { position: absolute; right: -230px; bottom: 0; margin: 0; font-size: var(--t-xs); white-space: nowrap; }
/* Couverture d'un match en paysage : textes plus petits et posés en bas de la bande centrale, pour laisser voir la photo
   (720 px de haut seulement : centré et en grand, le texte en prenait presque toute la hauteur) */
/* « safe » : si le texte était plus haut que la place, il commencerait en haut au lieu de sortir de l'image par le haut
   (les navigateurs qui ne connaissent pas « safe » ignorent la 2e ligne et gardent la 1re) */
body[data-format="paysage"][data-vue="album"][data-couverture="images"] .album-couv { justify-content: flex-end; justify-content: safe flex-end; padding-bottom: 60px; }   /* au-dessus de « Glissez pour voir les photos → » */
body[data-format="paysage"][data-vue="album"][data-couverture="images"] .album-verdict { font-size: var(--t-m); }
body[data-format="paysage"][data-vue="album"][data-couverture="images"] .album-score { font-size: var(--t-m); margin-top: 6px; }
body[data-format="paysage"][data-vue="album"][data-couverture="images"] .album-noms { font-size: var(--t-xs); margin-top: 6px; }
body[data-format="paysage"][data-vue="album"][data-couverture="images"] .album-date,
body[data-format="paysage"][data-vue="album"][data-couverture="images"] #c-album-credit-couv { font-size: var(--t-xs); margin-top: 6px; }
/* Image des partenaires : le titre et les logos à gauche, le prochain match dans une colonne à droite */
body[data-format="paysage"][data-vue="partenaires"] .contenu { justify-content: flex-start; }
body[data-format="paysage"] .partenaires { display: none; margin-top: 0; }
body[data-format="paysage"][data-vue="partenaires"] .partenaires { display: grid; grid-template-columns: minmax(0, 1fr) 360px; grid-template-rows: auto auto minmax(0, 1fr); column-gap: 40px; }
body[data-format="paysage"] .partenaires.sans-prochain { grid-template-columns: minmax(0, 1fr); }
body[data-format="paysage"] .titre-partenaires { grid-column: 1; font-size: var(--t-m); }
body[data-format="paysage"] .partenaires .barre { grid-column: 1; margin: 12px 0 16px; }
body[data-format="paysage"] .partenaires-logos { grid-column: 1; gap: 12px; align-content: flex-start; }
body[data-format="paysage"] .partenaires-logos .sponsor { padding: 10px 16px; }
body[data-format="paysage"] .prochain { grid-column: 2; grid-row: 1 / span 3; margin-top: 0; padding-top: 0; align-self: center; }
/* Nom de l'équipe sur 2 lignes : la pastille orange garde la largeur de son texte (pas toute la colonne) */
body[data-format="paysage"] .prochain .album-pastille.multi > span { width: fit-content; max-width: 100%; line-height: 1.15; }
body[data-format="paysage"] .prochain-quand { font-size: var(--t-xs); }
body[data-format="paysage"] .prochain-adv { font-size: var(--t-xs); }

/* Exemple grisé d'un Message ou d'une Photo encore vide. Le dessin est celui de la VRAIE scène (mêmes polices, tailles et positions que le futur
   contenu, voir dessiner()) : il est seulement estompé. Il n'est jamais exporté : tant qu'il est là, le bouton principal est grisé (ceQuiManque()). */
#scene[data-exemple="message"] #entete-message, #scene[data-exemple="message"] .texte-message, #scene[data-exemple="photo"] .photo-legende { opacity: .4; }
.exemple-photo { display: none; position: absolute; align-items: center; justify-content: center; border: 4px dashed rgba(255, 255, 255, .4);
  background: rgba(255, 255, 255, .08); color: rgba(255, 255, 255, .5); font-size: var(--t-xl); }
#scene[data-exemple="photo"] .exemple-photo { display: flex; }
/* Étiquette « Exemple » (hors de la scène : --s = échelle de l'aperçu) */
.exemple-vide { position: absolute; inset: 0; z-index: 4; pointer-events: none; color: rgba(255, 255, 255, .5); font-weight: 700; }
.exemple-note { position: absolute; right: calc(24px * var(--s)); top: calc(24px * var(--s)); font-size: calc(var(--t-s) * var(--s)); letter-spacing: .08em; text-transform: uppercase; }

/* « 1er » : le « er » est en exposant (posé par exposantEr()). line-height: 0 pour ne pas agrandir la ligne ; taille en em pour suivre le texte. */
#scene sup { font-size: .6em; line-height: 0; vertical-align: super; font-feature-settings: normal; }

/* Texte du post en Story : une seule ligne courte (texteStory()), donc pas de choix de style */
.aide-story { display: none; }
body[data-format="story"] .s-texte-post .choix { display: none; }
body[data-format="story"] .aide-post { display: none; }
body[data-format="story"] .aide-story { display: block; }
