/* Squashix — page /paris (pronostics squash PSA, Mizoukis (MZK)). Scoped à cette
   page uniquement : tokens de tokens.css, composants de components.css, aucune
   couleur/taille en dur. */

/* Le correctif [hidden]{display:none!important} qui vivait ici est remonté dans
   style.css : le même piège avait été rustiné séparément sur trois pages, et le
   Studio en souffrait sans que personne l'ait rustiné. */

/* Petites animations : la création d'un tableau (chaque tour apparaît l'un
   après l'autre, voir le délai posé en ligne par public/paris.js), le choix
   d'un score (léger rebond) et la validation d'un pari (le toast, réutilisé
   du composant global mais avec un mouvement propre à cette page). */
/* opacity uniquement, jamais transform : public/paris.js pose un translateY
   sur les cases DANS ce tour pour les centrer verticalement sur les deux
   cases du tour précédent (voir centerBracketRounds). Une animation CSS sur
   `transform` gagne toujours face à un style inline pendant qu'elle tourne
   (règle du cascade propre aux animations) — les deux se disputaient
   auparavant translateY, et les positions mesurées PENDANT l'animation (donc
   fausses) restaient figées dans le tracé SVG une fois l'animation terminée. */
@keyframes sxBracketRoundIn{from{opacity:0;}to{opacity:1;}}
.sx-bracket-round{animation:sxBracketRoundIn .4s ease both;}
@keyframes parisBadgePop{0%{transform:scale(.82);}65%{transform:scale(1.08);}100%{transform:scale(1);}}
.btn-secondary.selected{animation:parisBadgePop .25s ease;}
@keyframes parisToastPop{0%{transform:translate(-50%,16px) scale(.92);}60%{transform:translate(-50%,-2px) scale(1.03);}100%{transform:translate(-50%,0) scale(1);}}
#toast.show{animation:parisToastPop .32s cubic-bezier(.22,.85,.3,1.2);}
@keyframes parisFocusPulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--accent, var(--blue)) 55%, transparent);}70%{box-shadow:0 0 0 10px color-mix(in srgb, var(--accent, var(--blue)) 0%, transparent);}100%{box-shadow:0 0 0 0 transparent;}}
.paris-focus-pulse{animation:parisFocusPulse 1s ease;border-radius:var(--radius-sm);}
/* Cadre "en cours d'édition" (choix de vainqueur/score/mise, voir plus bas
   dans le fichier, vue liste desktop d'un tournoi) — une seule couleur (déjà
   associée à "pas encore validé" ailleurs sur le site), en boucle tant que
   le formulaire reste ouvert : un repère fonctionnel, pas le cycle 4 accents
   des illustrations décoratives (réservé, voir sx-stroke-anim.css). */
@keyframes parisEditPulse{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mustard) 35%, transparent);}50%{box-shadow:0 0 0 5px color-mix(in srgb, var(--mustard) 0%, transparent);}}

.paris-main{width:min(1100px,calc(100% - 40px));margin:0 auto;padding:40px 0 130px;display:flex;flex-direction:column;gap:34px;}
/* #parisContent (public/paris.html) enveloppe tout le contenu réservé aux
   membres, caché par défaut (voir #parisGate, public/paris.js) pour ne
   jamais peindre une bribe de page avant que la vérification /api/auth/me
   n'ait répondu — sans ça, un visiteur non connecté voyait le fil d'Ariane
   et les onglets clignoter avant d'être remplacés par le portail.
   display:contents : le rendre visible ne doit RIEN changer à la mise en
   page — ses enfants redeviennent directement ceux de .paris-main, comme
   si cette div n'existait pas, et gardent donc le même gap:34px que s'ils
   étaient un niveau plus haut. */
#parisContent{display:contents;}
/* align-self:flex-start : sans ça, .breadcrumb (display:inline-flex mais ENFANT
   d'un flex column) s'étire à la largeur totale de .paris-main comme n'importe
   quel autre bloc de la page — une pilule compacte doit garder sa taille de
   contenu, pas devenir une barre pleine largeur. */
.paris-main > .breadcrumb{align-self:flex-start;}

.paris-intro h1{margin:0;font-size:clamp(24px,3vw,34px);letter-spacing:-1px;}
.paris-intro h1 em{font-style:normal;color:var(--blue);}
.paris-intro p{margin:10px 0 0;color:var(--ink-soft);font-size:13px;max-width:60ch;}

/* Pictogramme devant chaque libellé (Paris en cours/Nouveau pari/Paris
   passés/Classement) — juste un repère visuel rapide, jamais la seule façon
   de distinguer les onglets (le texte reste toujours affiché à côté). */
#parisTabs .sx-tab{display:inline-flex;align-items:center;gap:6px;}
#parisTabs .sx-tab svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none;}

/* 5 onglets ne tiennent plus à parts égales sur un écran de poche (essayé —
   "Paris en cours" s'écrasait sur 3 lignes) : la rangée défile
   horizontalement plutôt que de comprimer les libellés. flex:0 0 auto sur
   chaque onglet (jamais 1 1 0) garde sa largeur naturelle, une seule ligne ;
   c'est le conteneur qui déborde et scrolle, pas le texte qui se tasse.
   Barre de défilement masquée (scrollbar-width/::-webkit-scrollbar) : le
   dernier onglet visible est volontairement coupé à mi-largeur au repos,
   affordance suffisante pour signaler qu'il y a plus à droite. */
#parisTabs{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
#parisTabs::-webkit-scrollbar{display:none;}
#parisTabs .sx-tab{flex:0 0 auto;}
@media(max-width:640px){
  #parisTabs{display:flex;width:100%;}
}

/* Mobile : neuf blocs pleine largeur (fil d'Ariane, kicker, accordéon, 4
   onglets, recherche, catégorie, statut, bascule de vue) s'empilaient avec
   le même gap:34px qu'en desktop — le premier tournoi passait sous la ligne
   de flottaison. Espacements resserrés ; les onglets restent collés sous le
   topbar (70px sur mobile, voir style.css) au lieu de défiler avec le reste,
   pour ne jamais avoir à remonter tout en haut pour changer de vue. */
@media(max-width:640px){
  .paris-main{gap:16px;padding:20px 0 70px;}
  #parisTabs{position:sticky;top:70px;z-index:15;}
}

/* .paris-header-row (public/paris.html) enveloppe fil d'Ariane + kicker +
   accordéon "Comment ça marche ?" — display:contents en permanence : sur
   desktop, ses trois enfants redeviennent des enfants directs de
   .paris-main comme avant son introduction (même gap:34px, même rendu,
   AUCUNE régression), exactement le même principe que #parisContent
   ci-dessus et que .paris-filters-drawer plus bas. Seul le mobile (media
   query suivante) lui donne un vrai rôle de mise en page, pour placer le
   bouton "?" à côté du titre au lieu de sa propre rangée pleine largeur. */
.paris-header-row{display:contents;}
@media(max-width:640px){
  /* "crumb crumb" / "kicker btn" / "reveal reveal" : le fil d'Ariane occupe
     seule la première rangée, le titre (kicker) et le bouton "?" partagent
     la seconde — aligné à droite du titre, pas du fil d'Ariane, pour rester
     visuellement rattaché à ce qu'il explique — et le contenu déplié de
     l'accordéon (quand ouvert) la troisième — sans dépendre de l'ordre du
     DOM (resté fil d'Ariane / kicker / accordéon, inchangé, donc toujours
     lisible tel quel par un lecteur d'écran). */
  .paris-header-row{
    display:grid;grid-template-columns:1fr auto;
    grid-template-areas:"crumb crumb" "kicker btn" "reveal reveal";
    align-items:start;gap:10px;
  }
  /* justify-self:stretch (pas :start) : le fil doit pouvoir occuper toute la
     largeur de sa rangée pour que le nom du tournoi (dernier maillon, voir
     règles juste en dessous) ait de la place où se tronquer AVANT de
     déborder, plutôt que de forcer tout le reste à rétrécir en premier. */
  .paris-header-row>.breadcrumb{grid-area:crumb;justify-self:stretch;overflow:hidden;}
  /* Accueil / Coin Paris / Paris Pro (et les séparateurs "/") gardent la
     priorité : white-space:nowrap + flex:none les empêche de rétrécir ou de
     couper un mot en 2 lignes (comportement par défaut d'un flex-item, qui a
     sinon un min-width:auto = min-content) — seul #parisBreadcrumbTournament
     juste après cède la place. */
  .paris-header-row>.breadcrumb>a,
  .paris-header-row>.breadcrumb>button,
  .paris-header-row>.breadcrumb>span:not(#parisBreadcrumbTournament){white-space:nowrap;flex:none;}
  /* Nom du tournoi : seul maillon qui peut être long et venir d'ailleurs
     (titre du tournoi PSA) — flex:1 min-width:0 le laisse rétrécir sous sa
     taille de contenu (impossible par défaut sur un flex-item) puis
     text-overflow:ellipsis coupe proprement au lieu de repasser à la ligne. */
  .paris-header-row>.breadcrumb #parisBreadcrumbTournament{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .paris-header-row>.paris-intro{grid-area:kicker;}
  /* .paris-how repasse en display:contents ICI (mobile seulement, jamais sur
     desktop où elle garde sa carte pleine largeur habituelle) : son en-tête
     et son corps deviennent à leur tour des enfants directs de la grille
     ci-dessus, placés chacun dans leur propre zone nommée. */
  .paris-header-row .paris-how{display:contents;}
  .paris-header-row .paris-how .accordion-header{
    grid-area:btn;width:34px;height:34px;padding:0;flex:none;
    display:flex;align-items:center;justify-content:center;
    border:1.5px solid var(--line);border-radius:50%;background:var(--card);
  }
  .paris-header-row .paris-how .paris-how-title-text,
  .paris-header-row .paris-how .accordion-caret{display:none;}
  /* Fond rempli (pas juste la bordure) une fois ouvert : seul indice visuel
     disponible ici pour signaler "ouvert, retape pour refermer" — le texte
     du titre et le chevron ▾ (qui suffiraient seuls) sont cachés sur ce
     bouton rond, voir masquage juste au-dessus. Même langage que .paris-how-
     bnode.is-open plus bas dans ce fichier (fond --blue-soft = "actif"). */
  .paris-header-row .paris-how.open .accordion-header{border-color:var(--blue);background:var(--blue-soft);}
  .paris-header-row .paris-how .accordion-body{grid-area:reveal;}
  /* .accordion-item.paris-how n'a plus de boîte propre (display:contents
     ci-dessus) : la carte "Comment ça marche ?" ouverte doit donc porter sa
     propre bordure ICI, sur accordion-body-pad — même principe que
     .paris-filters-drawer .accordion-body-pad plus bas. */
  .paris-header-row .paris-how .accordion-body-pad{
    border:1.5px solid var(--blue);border-radius:var(--radius-sm);background:var(--card);
    margin-top:2px;padding:16px 16px 18px;
  }
}

/* Tournoi ouvert, desktop : même « ? » à côté du titre que sur mobile (bloc
   ci-dessus) — le corps de l'accordéon se déplie sous l'en-tête. Sur la liste
   des tournois, l'accordéon garde sa carte pleine largeur. */
@media(min-width:641px){
  body.paris-tournoi-ouvert .paris-main{gap:22px;}
  body.paris-tournoi-ouvert .paris-header-row{
    display:grid;grid-template-columns:1fr auto;
    grid-template-areas:"crumb crumb" "kicker btn" "reveal reveal";
    align-items:start;gap:22px 10px;
  }
  body.paris-tournoi-ouvert .paris-header-row>.breadcrumb{grid-area:crumb;justify-self:start;}
  body.paris-tournoi-ouvert .paris-header-row>.paris-intro{grid-area:kicker;}
  body.paris-tournoi-ouvert .paris-header-row .paris-how{display:contents;}
  body.paris-tournoi-ouvert .paris-header-row .paris-how .accordion-header{
    grid-area:btn;width:34px;height:34px;padding:0;flex:none;margin-top:4px;
    display:flex;align-items:center;justify-content:center;
    border:1.5px solid var(--line);border-radius:50%;background:var(--card);
  }
  body.paris-tournoi-ouvert .paris-header-row .paris-how .paris-how-title-text,
  body.paris-tournoi-ouvert .paris-header-row .paris-how .accordion-caret{display:none;}
  body.paris-tournoi-ouvert .paris-header-row .paris-how.open .accordion-header{border-color:var(--blue);background:var(--blue-soft);}
  body.paris-tournoi-ouvert .paris-header-row .paris-how .accordion-body{grid-area:reveal;}
  body.paris-tournoi-ouvert .paris-header-row .paris-how .accordion-body-pad{
    border:1.5px solid var(--blue);border-radius:var(--radius-sm);background:var(--card);margin-top:2px;
  }
}

/* "Comment ça marche ?" : réutilise l'accordéon global (.accordion-item,
   toggle .open géré par components.js) — fermé par défaut, replié pour ne
   pas alourdir la page à chaque visite. Padding élargi par rapport à
   l'accordéon de base (14px/18px) : le contenu ici a plusieurs blocs, un
   peu plus d'air autour évite l'effet "collé au cadre". */
.paris-how .accordion-header{padding:16px 22px;font-size:14px;}
.paris-how .accordion-body-pad{padding:20px 22px 22px;}
/* Bordure du CADRE de la section (au complet), pas celle des cartes "Tour
   par tour"/"Mode Oneshot" à l'intérieur (déjà bleues, voir .paris-how-
   tabpanel) — écrase le sage par défaut de .accordion-item.open
   (components.css), même spécificité (2 classes), gagne par ordre de
   cascade (paris.css chargé après). */
.paris-how.open{border-color:var(--blue);}
.paris-how-title{display:flex;align-items:center;gap:9px;}
.paris-how-title svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;color:var(--blue);flex:none;}

/* Toute la section en thème bleu (couleur du Coin Paris pro — voir
   .paris-how-title svg ci-dessus, déjà bleu) : un seul accent du début à la
   fin plutôt que les 3 couleurs des cartes de l'accueil (essayées puis
   écartées) — cette section n'est pas un sommaire de rubriques, juste un
   mode d'emploi, elle garde l'identité bleue de la page qui la contient.
   Seul le résultat du calculateur reste au mustard (convention "gain MZK"
   déjà en place partout ailleurs, voir .paris-potential-gain) : une couleur
   sémantique, pas un accent de thème. */
.paris-how-steps{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px;}
@media (max-width:640px){.paris-how-steps{grid-template-columns:1fr;}}
/* Titre+sous-titre à côté du picto (au lieu d'en dessous) : la carte gagne
   toute la hauteur qu'occupait l'empilement — picto réduit en conséquence
   (38px -> 30px) pour rester proportionné à une ligne de texte au lieu d'un
   bloc de 2. padding-right élargi : laisse la place au badge numéro
   (coin haut-droit, position:absolute) sans que le titre ne passe dessous. */
.paris-how-step-card{position:relative;display:flex;align-items:center;gap:12px;padding:14px 34px 14px 16px;border:1.5px solid var(--line);border-radius:var(--radius-sm);background:var(--card-2);}
.paris-how-step-index{position:absolute;top:12px;right:12px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--blue-soft);color:var(--blue);font-size:15px;font-weight:800;}
.paris-how-step-icon{width:30px;height:30px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:none;background:var(--blue-soft);border:1px solid color-mix(in srgb, var(--blue) 26%, var(--line));}
.paris-how-step-icon svg{width:15px;height:15px;stroke:var(--blue);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.paris-how-step-card>div{min-width:0;}
.paris-how-step-card h4{margin:0 0 2px;font-size:13.5px;font-weight:800;color:var(--ink);}
.paris-how-step-card p{margin:0;font-size:12px;color:var(--ink-soft);line-height:1.4;}

/* Calculateur : mise/cote sur les sliders du design system
   (docs/design-system/v5.html, section "Sliders" — .range-soft) plutôt que
   le slider natif nu (.sx-range) utilisé avant : piste + poignée dessinées
   à la main (::-webkit-slider-thumb), remplissage tenu par --val, recalculé
   à chaque `input` (voir updateHowRangeFill, public/paris.js). */
.paris-how-calc{padding:18px 20px;border:1.5px solid var(--line);border-radius:var(--radius-sm);background:var(--card-2);margin-bottom:18px;}
.paris-how-calc-row{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:14px;}
@media (max-width:480px){.paris-how-calc-row{grid-template-columns:1fr;}}
.paris-how-field span{display:flex;align-items:baseline;justify-content:space-between;font-size:12px;font-weight:800;color:var(--ink-soft);margin-bottom:10px;}
.paris-how-field b{color:var(--blue);font-family:var(--mono);font-size:20px;}
.paris-how-range{--val:50%;appearance:none;-webkit-appearance:none;width:100%;height:10px;border-radius:99px;background:linear-gradient(90deg, var(--blue) var(--val), var(--card) var(--val));outline:none;cursor:pointer;}
.paris-how-range::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:22px;height:22px;border-radius:50%;background:var(--card);border:3px solid var(--blue);box-shadow:0 4px 12px rgba(0,0,0,.18);cursor:pointer;}
/* Case à cocher "du design system" : le toggle .sx-toggle de
   docs/design-system/v5.html (piste + poignée animée), pas la case native —
   ré-accentué en bleu et remappé sur les vrais tokens (var(--line-2),
   var(--card)…) plutôt que ceux, indépendants, du fichier de design : ça lui
   donne le thème sombre gratuitement, comme tout le reste du site. */
.paris-how-check{position:relative;display:inline-flex;align-items:center;gap:9px;font-size:12.5px;color:var(--ink-soft);cursor:pointer;margin-bottom:14px;}
.paris-how-check input{position:absolute;opacity:0;pointer-events:none;}
.paris-how-check .toggle-ui{width:38px;height:22px;flex:none;border-radius:99px;background:var(--line-2);position:relative;box-shadow:inset 0 1px 2px rgba(0,0,0,.12);transition:background .2s ease;}
.paris-how-check .toggle-ui:before{content:"";position:absolute;width:16px;height:16px;border-radius:50%;left:3px;top:3px;background:var(--card);box-shadow:0 2px 8px rgba(0,0,0,.18);transition:transform .2s cubic-bezier(.2,.9,.2,1.14);}
.paris-how-check input:checked+.toggle-ui{background:var(--blue);box-shadow:0 0 0 3px var(--blue-soft);}
.paris-how-check input:checked+.toggle-ui:before{transform:translateX(16px);}
/* Zone de gain, en bleu comme le reste de la section (au lieu du mustard
   "gain MZK" habituel — demande explicite ici, cette carte reste dans le
   thème bleu même pour son résultat). */
.paris-how-result{padding:10px 14px;border-radius:var(--radius-sm);background:var(--blue-soft);border:1px solid color-mix(in srgb, var(--blue) 45%, transparent);display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;}
.paris-how-result-eq{font-family:var(--mono);font-size:20px;color:var(--ink-soft);}
.paris-how-result-gain{font-size:15px;font-weight:800;color:var(--blue);}
.paris-how-note{margin:12px 0 0;font-size:12.5px;color:var(--ink-soft);line-height:1.6;}

/* Onglets "Tour par tour"/"Mode Oneshot" : réutilise .sx-tabs/.sx-tab
   (components.css/js) — l'état actif des boutons est déjà géré par le clic
   délégué générique de components.js ; seul le panneau associé est basculé
   ici (voir #parisHowTabs, public/paris.js). Actif en bleu (au lieu du
   card/ink neutre de .sx-tab.active par défaut) : cohérent avec le reste de
   la section. Le panneau lui-même est une carte à bordure neutre (comme
   .paris-how-calc/.paris-how-step-card, pas .paris-how.open) — rattachée à
   l'onglet actif, pas juste du texte flottant sous les boutons ; le bleu
   reste réservé au cadre de LA SECTION, pas à celui de ses sous-panneaux. */
.paris-how-tabs-wrap{margin-bottom:18px;}
.paris-how-tabs-wrap .sx-tabs{margin-bottom:14px;}
/* Le bleu est la couleur d'identité du Coin Paris PSA sur cette page entière
   (cotes, décompte, vainqueur pronostiqué… — voir tout le fichier) : les
   onglets sélectionnés (.sx-tab.active, normalement neutre — components.css)
   la reprennent partout ici, pas seulement sur "Comment ça marche ?" comme
   avant. Portée à .paris-page (body de cette page uniquement) pour ne
   changer aucune autre page qui réutilise .sx-tab (atelier, amateur…). */
.paris-page .sx-tab.active{background:var(--blue-soft);color:var(--blue);}
/* Même logique pour le fil d'Ariane : var(--sage), la couleur de lien par
   défaut (components.css), tranche avec un fil de page entièrement bleu. */
.paris-page .breadcrumb a,.paris-page .breadcrumb button{color:var(--blue);}
/* Pagination de la grille de tournois : même repli sur le bleu du coin que le
   fil d'Ariane et les onglets ci-dessus (.page-item.active est sage par
   défaut, components.css). */
.paris-page .page-item.active{background:var(--blue);color:#fff;border-color:var(--blue);}
/* "Aucun pari en cours"/"Aucun historique"… (emptyState, public/paris.js) et
   son bouton "Nouveau pari" : sage par défaut (components.css, générique à
   tout le site) — repris en bleu ici pour la même raison que les onglets et
   la pagination ci-dessus. */
.paris-page .empty-state .icon-circle{background:var(--blue-soft);}
.paris-page .empty-state .icon-circle svg{stroke:var(--blue);}
.paris-page .btn-primary{background:var(--blue-solid);}
/* Circuit amateur (hub, page d'un tournoi, écran TV) : terracotta de bout en
   bout, comme le pro l'est en bleu. Les composants repris de ce fichier
   lisent --blue* en dur (classement, podium, compte à rebours…) : on
   redéfinit ces jetons sur le body plutôt que de réécrire chaque règle. Les
   -solid et -badge sont réécrits eux aussi, car un var() posé sur :root est
   résolu là et ne suivrait pas le body. */
:is(.paris-amateur-page,.tournoi-page,.ecran-page){--blue:var(--terracotta);--blue-soft:var(--terracotta-soft);--blue-solid:var(--terracotta-solid);--blue-badge:var(--terracotta-badge);}
/* Composants génériques de components.css / style.css qui sont sage ou bleu
   par défaut : onglets, fil d'Ariane, boutons, focus, pastille « Paris » du
   menu. */
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .breadcrumb a,
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .breadcrumb button{color:var(--terracotta);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .sx-tab.active{background:var(--terracotta-soft);color:var(--terracotta);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .btn-primary{background:var(--terracotta-solid);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .sx-input:focus,
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .sx-list-item:hover{border-color:var(--terracotta);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .badge--sage{background:var(--terracotta-badge);color:var(--terracotta-2);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .nav-pills a[href^="/paris"]:hover,
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .nav-pills a[href^="/paris"].active{color:var(--terracotta);background:var(--terracotta-soft);}
:is(.paris-amateur-page,.tournoi-page,.ecran-page) .page-item.active{background:var(--terracotta-solid);color:#fff;border-color:var(--terracotta-solid);}
.paris-how-tabpanel{padding:18px 20px;border:1.5px solid var(--line);border-radius:var(--radius-sm);background:var(--card-2);}

.paris-how-bracket{display:flex;align-items:center;justify-content:space-between;gap:8px;}
.paris-how-bnode{flex:1;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;}
.paris-how-bnode-dot{width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid var(--line);background:var(--card);color:var(--ink-muted);}
.paris-how-bnode-dot svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.paris-how-bnode.is-open .paris-how-bnode-dot{border-color:var(--blue);background:var(--blue-soft);color:var(--blue);}
.paris-how-bnode span:last-child{font-size:11px;font-weight:800;color:var(--ink-soft);}
.paris-how-bnode.is-open span:last-child{color:var(--blue);}

.paris-how-chain{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:2px;}
.paris-how-chip{padding:8px 12px;border-radius:var(--radius-sm);background:var(--card);border:1px solid var(--line);font-family:var(--mono);font-weight:800;font-size:12px;color:var(--ink-soft);}
.paris-how-chip.is-win{background:var(--blue-soft);border-color:color-mix(in srgb, var(--blue) 35%, transparent);color:var(--blue);}
.paris-how-arrow{color:var(--ink-muted);font-size:12px;}

/* Reconstruites : même gabarit d'icône que .paris-how-step-icon juste
   au-dessus (30px, coins 10px, fond bleu-soft, trait bleu) — un seul
   composant "picto rond bleu" pour toute la section plutôt que deux
   variantes qui devaient rester synchronisées à la main. */
.paris-how-strip{display:flex;gap:14px;flex-wrap:wrap;}
.paris-how-pill{flex:1;min-width:220px;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:var(--radius-sm);background:var(--card-2);border:1px solid var(--line);}
.paris-how-pill-icon{width:30px;height:30px;border-radius:10px;display:flex;align-items:center;justify-content:center;flex:none;background:var(--blue-soft);border:1px solid color-mix(in srgb, var(--blue) 26%, var(--line));}
.paris-how-pill-icon svg{width:15px;height:15px;stroke:var(--blue);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;}
.paris-how-pill strong{display:block;font-size:12.5px;margin-bottom:2px;color:var(--ink);}
.paris-how-pill span{display:block;font-size:11px;color:var(--ink-muted);}

/* Mobile : "Comment ça marche ?" prenait trop de hauteur d'écran une fois
   déplié (3 blocs + calculateur + onglets + strip) — paddings/marges/gaps
   resserrés, textes inchangés (plancher 12px du projet respecté). */
@media(max-width:640px){
  .paris-how .accordion-header{padding:13px 16px;}
  .paris-how .accordion-body-pad{padding:14px 16px 16px;}
  .paris-how-steps{gap:10px;margin-bottom:14px;}
  .paris-how-step-card{padding:10px 30px 10px 12px;}
  .paris-how-calc{padding:14px 16px;margin-bottom:12px;}
  .paris-how-calc-row{gap:14px;margin-bottom:10px;}
  .paris-how-check{margin-bottom:10px;}
  .paris-how-note{margin-top:8px;line-height:1.5;}
  .paris-how-tabs-wrap{margin-bottom:12px;}
  .paris-how-tabs-wrap .sx-tabs{margin-bottom:10px;}
  .paris-how-tabpanel{padding:14px 16px;}
  .paris-how-strip{gap:10px;}
  .paris-how-pill{padding:10px 12px;}
}

.paris-panel{display:none;flex-direction:column;gap:26px;}
.paris-panel.active{display:flex;}

.paris-section-title{margin:10px 0 -10px;font-size:15px;font-weight:800;}

.paris-summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:14px;}
/* Cartes KPI de "Paris passés" : le picto et la ligne d'évolution
   s'ajoutent PAR-DESSUS .kpi-card (components.css, partagé avec
   communauté/composants) sans toucher ses règles de base — les autres
   usages du site n'ont ni .kpi-icon ni .kpi-delta et restent inchangés. */
.kpi-card--icon{gap:4px;}
.kpi-icon{width:18px;height:18px;color:var(--ink-soft);margin-bottom:2px;}
.kpi-icon svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.kpi-delta{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:700;color:var(--ink-soft);margin-top:2px;}
.kpi-delta svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2.6;flex:none;}
.kpi-delta.is-positive{color:var(--sage);}
.kpi-delta.is-negative{color:var(--danger);}

/* Indicateurs de la vue tournoi (#tournamentSummary, public/paris.js#
   renderTournamentSummary) : picto + titre sur une même ligne, valeur en
   dessous — layout propre à ce bloc, jamais celui des autres .kpi-card
   (picto seul au-dessus du libellé, voir "Paris passés" ci-dessus). Le
   picto est bleu ici (identité du bandeau "COIN PARIS · CIRCUIT PSA",
   .section-kicker.blue) plutôt que --ink-soft. */
#tournamentSummary .kpi-head{display:flex;align-items:center;gap:6px;}
#tournamentSummary .kpi-icon{width:16px;height:16px;color:var(--blue);margin-bottom:0;}

.paris-filters{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
/* Les noms de tournois PSA réels sont parfois très longs ("... presented by
   Spanos Group of Raymond James") : un <select> natif sans largeur bornée
   s'élargit pour son option la plus longue et pousse la page en débordement
   horizontal. max-width + ellipsis gardent le sélecteur dans la mise en page,
   le nom complet reste lisible une fois le menu ouvert. */
#pastTournamentFilter{max-width:min(260px,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

.paris-list{display:flex;flex-direction:column;gap:16px;}
.paris-bet-net{font-family:inherit;font-size:13px;font-weight:700;color:var(--ink-soft);white-space:nowrap;}
.paris-bet-net.is-positive{color:var(--sage);}
.paris-bet-net.is-negative{color:var(--danger);}
/* Tableau en arbre : le montant d'un pari réglé est un badge plein à cheval
   sur la bordure haute de la carte, côté droit (matchCardHtml, edgeBadge).
   L'anneau de la couleur du fond le détache de la bordure. */
.match-card .paris-bet-net--bord{position:absolute;top:-13px;right:14px;z-index:1;padding:2px 11px;border-radius:var(--pill);border:2px solid var(--canvas, var(--bg));font-size:13px;line-height:1.4;font-variant-numeric:tabular-nums;}
.match-card .paris-bet-net--bord.is-positive{background:var(--badge-gain);color:var(--on-badge);}
.match-card .paris-bet-net--bord.is-negative{background:var(--badge-perte);color:var(--on-badge);}
.page-item.is-disabled{opacity:.4;cursor:default;pointer-events:none;}

/* Sélecteur de vue "Paris en cours" (cartes/liste) — persisté en
   localStorage (public/paris.js), affiché en haut à droite de la section. */
.paris-current-toolbar{display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap;}
.paris-view-toggle{display:inline-flex;gap:2px;padding:3px;border-radius:var(--pill);background:var(--card);border:1.5px solid var(--line);}
.paris-view-btn{display:flex;align-items:center;justify-content:center;width:34px;height:30px;border:0;border-radius:var(--pill);background:none;color:var(--ink-soft);cursor:pointer;transition:background .15s ease,color .15s ease;}
.paris-view-btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.paris-view-btn:hover{color:var(--ink);}
.paris-view-btn[aria-pressed="true"]{background:var(--blue);color:#fff;}
/* Filtre genre "Paris en cours" : deux onglets indépendants, glyphe ♂/♀
   directement dans le bouton — même pattern que les onglets cartes/liste
   juste à côté (.paris-view-btn[aria-pressed] porte déjà la couleur d'état).
   Pas la bulle .paris-gender-icon des cartes tournoi : fond + bordure +
   contour empilés dans 20px, trop chargé pour un simple filtre. */
.paris-gender-filter .paris-view-btn{font-size:15px;font-weight:800;}

/* Historique condensé (renderPastGroups/pastBetRowHtml, public/paris.js) :
   deux lignes par pari, pas de tableau — round+mise en petite ligne de
   contexte, match + résultat net (badge coloré) en dessous. Pas de date/
   heure : une fois le pari réglé, seul le round garde un repère utile. */
.paris-past-list{display:flex;flex-direction:column;padding:2px 2px 10px;}
.paris-past-row{padding:9px 4px;border-bottom:1.5px solid var(--line);display:flex;flex-direction:column;gap:5px;}
.paris-past-row:first-child{border-top:1.5px solid var(--line);}
.paris-past-eyebrow{font-size:11.5px;color:var(--ink-muted);display:flex;align-items:center;gap:6px;}
.paris-past-dot{width:3px;height:3px;border-radius:50%;background:var(--ink-muted);flex:none;}
.paris-past-main{display:flex;align-items:center;gap:8px;}
.paris-past-match{flex:1 1 auto;min-width:0;display:flex;align-items:baseline;gap:6px;font-size:14px;}
.paris-past-match>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.paris-past-winner{font-weight:800;}
.paris-past-loser{color:var(--ink-soft);}
.paris-past-score{font-family:var(--mono);font-weight:700;color:var(--ink-soft);font-size:12.5px;flex:0 0 auto;}
.paris-past-net{flex:0 0 auto;font-family:var(--mono);white-space:nowrap;}
.paris-list-match{display:flex;align-items:center;gap:6px;white-space:nowrap;}
.paris-list-match .is-winner{color:var(--blue);font-weight:800;}
.paris-list-vs{color:var(--ink-soft);font-size:12px;text-transform:uppercase;}
/* Repli sans données live (currentMatchRowFallbackHtml, public/paris.js) :
   les deux noms doivent pouvoir se tronquer plutôt que déborder — même
   piège que .match-card .side-name (voir plus bas), un long nom sur un
   petit écran sortait du cadre au lieu de finir en "…". */
.paris-matches-list-row .paris-list-match{width:100%;}
.paris-matches-list-row .paris-list-match>span:not(.paris-list-vs){flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.paris-matches-list-row .paris-list-match>span:last-child{text-align:right;}
/* Mise + gain potentiel (stakeRowHtml, public/paris.js) : posés à gauche
   dans .paris-bet-actions, sur la même ligne que Modifier/Annuler (à
   droite dans .paris-bet-actions-right) — pas sur la carte elle-même.
   Avant, la mise d'un pari en cours restait invisible tant qu'on n'ouvrait
   pas "Modifier". Mêmes classes que le résumé du tableau (.paris-predict-
   stat/--gain/--stake plus bas dans ce fichier) pour le picto + le badge,
   juste réduites ici (.paris-list-stake) pour tenir sur cette ligne. */
.paris-list-stake-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;}
/* .paris-predict-stat.paris-list-stake (2 classes) plutôt que .paris-list-
   stake seul : ça doit battre .paris-predict-stat--gain/--stake (1 classe
   chacune, définies PLUS BAS dans ce fichier) sur le padding — à
   spécificité égale, la règle la plus TARDIVE dans le fichier gagnerait
   sinon, quel que soit l'ordre voulu ici. */
.paris-predict-stat.paris-list-stake{padding:3px 8px;font-size:13px;gap:4px;}
.paris-predict-stat.paris-list-stake svg{width:13px;height:13px;}
.paris-bet-actions-right{display:flex;align-items:center;gap:10px;}
/* Seule cette liste écarte mise/gain (à gauche) des boutons (à droite,
   .paris-bet-actions-right) — .paris-oneshot-card réutilise .paris-bet-
   actions SANS mise à gauche, elle doit garder flex-end (ses boutons
   restent groupés à droite, seuls enfants du conteneur). */
.paris-matches-list-row .paris-bet-actions{justify-content:space-between;}
.paris-list-actions{display:flex;gap:6px;justify-content:flex-end;white-space:nowrap;}

.paris-bet-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;}
.paris-bet-tournament{font-size:12px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.04em;}
.paris-bet-meta{display:flex;align-items:center;gap:12px;font-size:12px;color:var(--ink-soft);flex-wrap:wrap;}
/* Une seule ligne, groupe collé à droite, bouton principal (Modifier) tout
   à droite — jamais de retour à la ligne, y compris sur mobile (voir la
   media query plus bas qui exclut désormais cette classe de l'empilement
   vertical appliqué au reste de la carte). */
.paris-bet-actions{display:flex;flex-direction:row;align-items:center;justify-content:flex-end;gap:10px;flex-wrap:nowrap;margin-top:2px;}

/* Cadran fixe (betRailHtml/betScoreZoneHtml, public/paris.js — "Paris en
   cours", proposition retenue) : deux lignes (mise+gain, puis solde+icônes)
   qui gardent la même boîte en lecture comme en édition — voir .paris-bet-
   cadran plus haut pour le mécanisme .only-read/.only-edit. */
.paris-bet-rail{display:flex;flex-direction:column;gap:6px;}
/* Hors édition, la ligne d'icônes (Annuler/Modifier) suit le badge gain
   potentiel d'un peu trop près (le même gap:6px que toutes les paires de
   lignes du cadran) — un peu plus d'air entre "ce que je gagnerais" et les
   actions, pour ne pas les lire comme un seul bloc. */
.paris-bet-cadran:not(.is-editing) .paris-bet-rail{gap:10px;}
.paris-bet-rail-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.paris-bet-rail-row--end{justify-content:flex-end;}
/* Ligne mise+gain : jamais de retour à la ligne dans les 240px du cadran —
   la mise/le stepper gardent leur taille (flex:none), c'est le gain
   potentiel seul qui cède la place et tronque en ellipse (voir .paris-
   potential-gain-text plus bas) si la plage est trop large pour tenir. */
.paris-bet-rail-row:has(.paris-potential-gain){flex-wrap:nowrap;}
.paris-bet-rail-row:has(.paris-potential-gain) > :not(.paris-potential-gain){flex:none;}
.paris-bet-rail-icons{display:flex;align-items:center;gap:8px;margin-left:auto;}
/* Croix "fermer l'édition" (rouge) / coche "Valider" (verte) — .icon-action-
   btn ne connaît nativement que .is-cancel/.is-edit (components.css, partagé
   avec le reste du site) ; is-discard/is-check sont propres à ce cadran. */
.icon-action-btn.is-discard{color:var(--danger);}
.icon-action-btn.is-discard:hover{background:var(--danger-soft);border-color:var(--danger);}
.icon-action-btn.is-check{color:var(--sage);}
.icon-action-btn.is-check:hover{background:var(--sage-soft);border-color:var(--sage);}
/* Même gabarit que le badge "mise" juste à côté (.paris-predict-stat.paris-
   list-stake, plus haut dans ce fichier) — .paris-potential-gain de base
   (plus bas dans ce fichier) est pensé pour une ligne pleine largeur avant un
   bouton Valider ailleurs sur le site, trop grand une fois réduit à un badge
   parmi d'autres dans ce cadran étroit. */
.paris-bet-rail .paris-potential-gain{padding:3px 8px;font-size:13px;gap:4px;border-radius:var(--pill);flex:1 1 auto;min-width:0;justify-content:center;}
.paris-bet-rail .paris-potential-gain svg{width:13px;height:13px;flex:none;}
.paris-bet-rail .paris-potential-gain-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
/* Mobile uniquement : sous 860px, .paris-bet-rail n'est plus retenu à 240px
   (cette largeur fixe est réservée au cadran desktop, plus bas dans ce
   fichier) — flex:1 1 auto ci-dessus y étirait donc le badge doré sur toute
   la largeur de la carte, un bandeau bien plus imposant que la pilule "mise"
   juste à côté. Repasse en badge de la taille de son contenu, comme les
   autres pilules du cadran. */
@media (max-width:859.98px) {
  .paris-bet-rail .paris-potential-gain{flex:0 1 auto;}
}
/* Édition : le stepper de mise (rond, .paris-stepper-btn en 32px par défaut)
   dépassait nettement la hauteur du badge gain potentiel juste à côté (padding
   3px seul) — c'est le stepper qui doit se réduire à la taille du badge
   existant, pas l'inverse (le badge garde son gabarit d'origine, inchangé
   depuis avant cette carte fixe). Scopé aux lignes qui contiennent
   effectivement un stepper : le stepper "plein format" (dépôt/retrait
   ailleurs sur le site) n'est pas concerné. */
.paris-bet-rail-row:has(.paris-stepper) .paris-stepper{padding:2px;gap:1px;}
.paris-bet-rail-row:has(.paris-stepper) .paris-stepper-btn{width:18px;height:18px;font-size:12px;}
.paris-bet-rail-row:has(.paris-stepper) .paris-stepper-value{min-width:58px;gap:2px;}
.paris-bet-rail-row:has(.paris-stepper) .paris-stepper-value input{width:32px;font-size:12px;}
.paris-bet-rail-row:has(.paris-stepper) .paris-stepper-value small{font-size:11px;}
/* Pilule de score en lecture : même gabarit que les 3 boutons de score de
   l'édition juste à côté (.paris-score-buttons .btn, définis plus bas dans
   ce fichier) — seule la couleur change (sauge, "déjà choisi", pas bleu
   "sélectionné"), pour que le score d'un pari déjà posé se lise comme le
   même composant que celui qu'on est en train de choisir. */
.paris-bet-score-pill{background:var(--accent-soft, var(--blue-badge));color:var(--accent, var(--blue));border-color:transparent;gap:5px;}
.paris-bet-score-pill svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2.2;}
.paris-bet-score-zone .btn-secondary.selected{background:var(--accent-solid, var(--blue-solid));color:#fff;border-color:var(--accent-solid, var(--blue-solid));}
.paris-countdown{font-family:var(--mono);font-size:12px;font-weight:700;color:var(--blue);}
.paris-bet-result{font-size:12.5px;font-weight:800;}
.paris-bet-result.won{color:var(--sage);}
.paris-bet-result.lost{color:var(--danger);}

/* Statistiques (onglet "Paris passés") : cartes indicateurs + graphe
   d'évolution du solde + top gains/pertes. Le graphe est du SVG dessiné à la
   main (viewBox fixe, preserveAspectRatio="none" pour occuper toute la
   largeur du conteneur) — même logique que les connecteurs du tableau,
   aucune dépendance de charting. Les libellés (axe Y, dates) sont posés en
   HTML à côté/en dessous plutôt qu'en <text> SVG : un <text> hérite du
   preserveAspectRatio="none" et se retrouve écrasé/étiré dès que le ratio
   largeur/hauteur réellement rendu diffère du viewBox — un texte HTML ne
   peut pas se déformer comme ça, quel que soit le conteneur. */
.paris-stats{display:flex;flex-direction:column;gap:16px;padding:18px 20px 22px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius);}

/* Cartes indicateurs : une ligne (picto + valeur/libellé empilés), une fine
   bordure gauche colorée comme seul accent — pas de pastille, pas de couleur
   par carte sauf le profit net (sens du signe). Même gabarit partout : les
   6 cartes restantes montrent toutes un nombre, aucune n'affiche plus de
   texte que les autres. */
.paris-stats-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;}
.paris-stat-card{display:flex;align-items:center;gap:9px;padding:9px 12px;background:var(--bg);border:1.5px solid var(--line);border-left:3px solid var(--line);border-radius:var(--radius-sm);min-width:0;}
.paris-stat-icon{flex:none;width:16px;height:16px;color:var(--ink-soft);}
.paris-stat-icon svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
.paris-stat-text{display:flex;flex-direction:column;gap:1px;min-width:0;}
.paris-stat-val{font-size:15px;font-weight:800;color:var(--ink);line-height:1.2;}
.paris-stat-lbl{font-size:12px;font-weight:700;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.paris-stat-card.is-positive{border-left-color:var(--sage);}
.paris-stat-card.is-positive .paris-stat-val,.paris-stat-card.is-positive .paris-stat-icon{color:var(--sage);}
.paris-stat-card.is-negative{border-left-color:var(--danger);}
.paris-stat-card.is-negative .paris-stat-val,.paris-stat-card.is-negative .paris-stat-icon{color:var(--danger);}

.paris-chart-card{display:flex;flex-direction:column;gap:8px;}
.paris-chart-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:8px;}
.paris-chart-head h3{margin:0;font-size:13px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.3px;}
.paris-top-col h3{margin:0 0 10px;font-size:13px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.3px;}
.paris-chart-row{display:flex;gap:6px;}
.paris-chart-y-labels{position:relative;flex:0 0 42px;height:190px;}
.paris-chart-y-labels span{position:absolute;left:0;right:2px;transform:translateY(-50%);font-size:12px;font-family:var(--mono);color:var(--ink-soft);text-align:right;white-space:nowrap;}
.paris-chart-plot{position:relative;flex:1;min-width:0;height:190px;}
.paris-chart-svg{width:100%;height:190px;display:block;color:var(--sage);}
.paris-chart-svg.is-down{color:var(--danger);}
.paris-chart-grid{stroke:var(--line);stroke-width:1;}
.paris-chart-end-dot{fill:currentColor;stroke:var(--card);stroke-width:2;}
.paris-chart-cursor-line{stroke:var(--ink-soft);stroke-width:1;stroke-dasharray:3 3;}
.paris-chart-cursor-dot{fill:currentColor;stroke:var(--card);stroke-width:2;}
.paris-chart-x-labels{display:flex;justify-content:space-between;padding-left:48px;font-size:12px;font-family:var(--mono);color:var(--ink-soft);}
.paris-chart-tooltip{position:absolute;transform:translate(-50%,-115%);pointer-events:none;background:var(--ink);color:var(--bg);border-radius:8px;padding:6px 10px;font-size:12px;display:flex;flex-direction:column;gap:1px;white-space:nowrap;box-shadow:var(--shadow-subtle);z-index:2;}
.paris-chart-tooltip strong{font-size:12.5px;}
.paris-chart-empty,.paris-top-empty{margin:0;padding:20px 0;text-align:center;font-size:12.5px;color:var(--ink-soft);}

.paris-top-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;}
.paris-top-col{min-width:0;}
.paris-top-list{display:flex;flex-direction:column;gap:8px;}
.paris-top-item{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
.paris-top-item-rank{flex:none;width:20px;text-align:center;font-size:12px;font-weight:800;color:var(--ink-soft);}
.paris-top-item-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
.paris-top-item-match{font-size:12.5px;font-weight:700;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.paris-top-item-vs{color:var(--ink-soft);font-weight:500;}
.paris-top-item-meta{font-size:12px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.paris-top-item-amount{flex:none;font-family:var(--mono);font-size:13px;font-weight:800;}
.paris-top-item--gain .paris-top-item-amount{color:var(--sage);}
.paris-top-item--loss .paris-top-item-amount{color:var(--danger);}

@media(max-width:640px){
  .paris-top-grid{grid-template-columns:1fr;}
  .paris-stats-kpis{grid-template-columns:1fr 1fr;}
  .paris-chart-y-labels,.paris-chart-plot,.paris-chart-svg{height:160px;}
}

/* Classement : une ligne par parieur, flex-wrap pour rester lisible sur
   mobile (rang+nom sur une ligne, solde/série/résultats passent à la ligne
   plutôt que déborder — même leçon que le tableau, voir components.css). */
.paris-leaderboard-controls{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:18px;}
/* Les 5 onglets de tri (Solde, Taux de réussite, Gains…) restent sur UNE
   ligne et défilent horizontalement plutôt que de se tasser sur 3-4 lignes
   (même mécanique que #parisTabs). min-width:0 + max-width:100% : sans eux la
   largeur naturelle des onglets pousserait la rangée hors du cadre. */
#leaderboardSortTabs{max-width:100%;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;}
#leaderboardSortTabs::-webkit-scrollbar{display:none;}
#leaderboardSortTabs .sx-tab{flex:0 0 auto;white-space:nowrap;}
/* Vue « Points » : trois groupes de choix (score, période, filtre) qui se
   combinent. Chacun reste sur une ligne et défile plutôt que de se tasser. */
.paris-points-controls{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;margin-bottom:10px;}
.paris-points-controls .sx-tabs{max-width:100%;min-width:0;overflow-x:auto;scrollbar-width:none;}
.paris-points-controls .sx-tabs::-webkit-scrollbar{display:none;}
.paris-points-controls .sx-tab{flex:0 0 auto;white-space:nowrap;}
.paris-points-note{margin:0 0 18px;max-width:640px;font-size:13px;line-height:1.6;color:var(--ink-soft);}
.paris-leaderboard{display:flex;flex-direction:column;gap:12px;}
.paris-leaderboard-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px 18px;padding:14px 18px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
/* Ligne du joueur connecté : même idée que .match-card.is-predicted-winner
   ailleurs sur cette page — un liseré + un fond teinté de la couleur du coin
   (bleu), pas une couleur de plus qui n'a pas de sens ici. */
.paris-leaderboard-row.is-me{border-color:var(--blue);background:var(--blue-soft);}
.paris-leaderboard-you{flex:none;margin-left:8px;padding:2px 8px;border-radius:var(--pill);background:var(--blue);color:#fff;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;vertical-align:middle;}
.paris-leaderboard-rank{flex:none;min-width:34px;font-size:16px;font-weight:800;text-align:center;}
.paris-leaderboard-trend{flex:none;display:inline-flex;align-items:center;justify-content:center;width:20px;font-size:12px;font-weight:800;}
.paris-leaderboard-trend--up{color:var(--sage);}
.paris-leaderboard-trend--down{color:var(--danger);}
.paris-leaderboard-trend--same{color:var(--ink-soft);}
.paris-leaderboard-trend--new{color:var(--blue);}
.paris-leaderboard-name{flex:1 1 140px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px;font-weight:800;color:var(--ink);}
.paris-leaderboard-balance{flex:none;font-family:var(--mono);font-size:12.5px;font-weight:800;color:var(--blue);}
.paris-leaderboard-wl{flex:none;font-size:12px;font-weight:700;color:var(--ink-soft);}
.paris-leaderboard-streak{flex:none;display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:800;color:var(--blue);}
.paris-leaderboard-streak svg{width:14px;height:14px;stroke:currentColor;fill:var(--blue-soft);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;}
.paris-leaderboard-streak.is-none{color:var(--ink-soft);font-weight:600;}
.paris-leaderboard-recent{flex:none;display:inline-flex;align-items:center;gap:4px;}
.paris-leaderboard-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:#fff;flex:none;}
.paris-leaderboard-dot--won{background:var(--sage);}
.paris-leaderboard-dot--lost{background:var(--danger);}
.paris-leaderboard-dot--refunded{background:var(--ink-soft);}
.paris-leaderboard-empty{flex:none;font-size:12px;color:var(--ink-soft);}
/* Podium (top 3) : rang 1 au centre et légèrement surélevé (ordre visuel via
   `order`, l'ordre du DOM reste 1-2-3 — accessible, sans rapport avec la
   position à l'écran). 3 colonnes égales sous ~640px de large : la carte du
   1er n'a plus la place d'être surélevée sans se chevaucher, elle redevient
   simplement la plus large des trois — voir le media query plus bas. */
.paris-leaderboard-podium{display:grid;grid-template-columns:1fr 1.15fr 1fr;align-items:end;gap:12px;margin-bottom:18px;}
.paris-podium-card{display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;padding:18px 10px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
.paris-podium-card--1{order:2;padding-top:26px;padding-bottom:26px;border-color:var(--blue);box-shadow:0 6px 18px color-mix(in srgb, var(--blue) 18%, transparent);}
.paris-podium-card--2{order:1;}
.paris-podium-card--3{order:3;}
.paris-podium-card.is-me{background:var(--blue-soft);}
.paris-podium-medal{font-size:26px;line-height:1;}
.paris-podium-card--1 .paris-podium-medal{font-size:32px;}
.paris-podium-name{font-size:13.5px;font-weight:800;color:var(--ink);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.paris-podium-metric{font-family:var(--mono);font-size:14px;font-weight:800;color:var(--blue);}
@media(max-width:640px){.paris-leaderboard-podium{grid-template-columns:1fr 1fr 1fr;align-items:stretch;}.paris-podium-card--1{padding-top:18px;padding-bottom:18px;}}
#leaderboardPagination .page-item.active{cursor:default;}

/* Missions de la semaine : trois cartes, la prime à droite, l'avancement en
   barre. Sauge = accomplie (même sens que « gagné » dans tout le Coin Paris). */
.paris-missions{display:flex;flex-direction:column;gap:12px;}
.paris-mission{display:flex;align-items:center;gap:8px 18px;flex-wrap:wrap;padding:16px 18px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
.paris-mission.is-done{border-color:var(--sage);background:var(--sage-soft);}
.paris-mission-corps{flex:1 1 260px;min-width:0;}
.paris-mission h3{margin:0 0 2px;font-size:14.5px;font-weight:800;color:var(--ink);}
.paris-mission p{margin:0 0 10px;font-size:13px;line-height:1.5;color:var(--ink-soft);}
.paris-mission-barre{height:8px;border-radius:var(--pill);background:var(--line);overflow:hidden;}
.paris-mission-barre i{display:block;height:100%;border-radius:var(--pill);background:var(--blue);transition:width .3s ease;}
.paris-mission.is-done .paris-mission-barre i{background:var(--sage);}
.paris-mission-etat{display:block;margin-top:6px;font-size:12px;font-weight:700;color:var(--ink-soft);}
.paris-mission.is-done .paris-mission-etat{color:var(--sage);}
.paris-mission-prime{flex:none;font-family:var(--mono);font-size:13px;font-weight:800;color:var(--blue);}
.paris-mission.is-done .paris-mission-prime{color:var(--sage);}

/* Carte "Paris en cours" allégée : date/heure à la place du badge de statut,
   score en badge entre les deux noms, mise/gain/compte à rebours réduits à
   des pictogrammes + valeur plutôt que des phrases complètes. */
.paris-bet-datetime{display:flex;align-items:center;gap:5px;font-size:12px;font-weight:700;color:var(--ink-soft);flex:none;}
.paris-bet-datetime svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.paris-bet-score-badge{flex:none;font-family:inherit;gap:5px;}
.paris-bet-score-badge svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;flex:none;}

.paris-tournament-filters{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:28px;}
/* Regroupe le filtre d'état du tableau (Tous/Tableau disponible/à venir) et
   la bascule grille/calendrier dans UNE unité : les deux doivent rester sur
   la même ligne l'un à côté de l'autre, jamais séparés par le retour à la
   ligne du flex parent (.paris-tournament-filters) — voir le mobile
   ci-dessous, où la bascule passe en picto seul pour tenir à côté. */
.paris-tournament-toolbar-row{display:flex;align-items:center;gap:12px;}
/* Bascule grille/calendrier : bouton carré à picto seul (texte retiré du
   HTML, gardé pour un lecteur d'écran via aria-label/title) plutôt que la
   pastille texte de .sx-tab — bien plus étroit, donc capable de tenir à
   côté du filtre d'état du tableau sur mobile (voir toolbar-row ci-dessus
   et le mobile ci-dessous). */
#tournamentViewToggle{flex:none;}
#tournamentViewToggle .sx-tab{display:flex;align-items:center;justify-content:center;width:34px;padding:8px;}
#tournamentViewToggle .sx-tab svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none;}
/* Mobile : la ligne toolbar (filtre d'état + bascule vue) occupe TOUTE la
   largeur à elle seule (flex-basis:100% la fait démarrer sur sa propre
   ligne dans .paris-tournament-filters, qui wrap déjà). min-width:0 sur la
   ligne ELLE-MÊME (pas seulement sur #tournamentDrawFilter à l'intérieur) :
   sans lui, la taille minimale automatique d'un item flex reste son
   min-content, qui remonte la largeur naturelle (non rétrécie) des pastilles
   à l'intérieur — la ligne débordait alors du cadre au lieu de tenir dans
   les 100% demandés. Le filtre d'état du tableau (3 pastilles texte,
   potentiellement plus large que l'écran) défile horizontalement au besoin ;
   la bascule grille/calendrier (picto seul, flex:none) reste elle toujours
   visible à droite, jamais poussée hors champ. */
@media(max-width:640px){
  .paris-tournament-toolbar-row{flex:1 1 100%;min-width:0;}
  #tournamentDrawFilter{flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none;}
  #tournamentDrawFilter::-webkit-scrollbar{display:none;}
  #tournamentDrawFilter .sx-tab{flex:0 0 auto;white-space:nowrap;}
}

/* Mobile : catégorie + statut de tableau + bascule grille/calendrier
   n'apparaissent plus d'office (3 rangées pleine largeur en plus de la
   recherche) — elles vivent derrière un bouton "Filtres" à côté du champ de
   recherche, sur le même accordéon générique que "Comment ça marche ?"
   (.accordion-item/-header/-body, components.css) : réutilise le même
   toggle délégué, pas de JS spécifique à écrire pour ouvrir/fermer.
   .paris-filters-drawer{display:contents} EN PERMANENCE (desktop ET mobile) :
   le conteneur .accordion-item lui-même ne doit jamais introduire de boîte —
   sur desktop, ses enfants (header caché, body forcé ouvert) doivent occuper
   EXACTEMENT la place qu'occupaient <select> et .paris-tournament-toolbar-row
   quand ils étaient enfants directs de .paris-tournament-filters (aucune
   régression du rendu desktop actuel). */
.paris-filters-drawer{display:contents;}
.paris-filters-drawer .accordion-header{display:none;}
@media(min-width:641px){
  /* Desktop : le corps de l'accordéon doit rester grand ouvert (l'utilisateur
     n'a pas de bouton pour le déplier, voir accordion-header caché ci-dessus)
     ET transparent à la mise en page — sans ce display:contents en cascade,
     le padding par défaut de .accordion-body-pad (components.css) décalerait
     le <select> et la toolbar-row par rapport à aujourd'hui. */
  .paris-filters-drawer .accordion-body,
  .paris-filters-drawer .accordion-body-inner,
  .paris-filters-drawer .accordion-body-pad{display:contents;}
}
@media(max-width:640px){
  .paris-tournament-filters{display:grid;grid-template-columns:1fr auto;grid-template-areas:"search filter" "body body";gap:10px;}
  .paris-tournament-filters>.paris-search-field{grid-area:search;}
  .paris-filters-drawer .accordion-header{
    display:flex;grid-area:filter;align-items:center;align-self:stretch;gap:6px;padding:0 14px;
    border:1.5px solid var(--line);border-radius:var(--radius-sm);background:var(--card);
    color:var(--ink);font-size:12px;font-weight:800;white-space:nowrap;cursor:pointer;
  }
  .paris-filters-drawer-label{display:flex;align-items:center;gap:6px;}
  .paris-filters-drawer-label svg{width:14px;height:14px;stroke:var(--ink-soft);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex:none;}
  .paris-filters-drawer .accordion-caret{display:none;}
  .paris-filters-drawer.open .accordion-header{border-color:var(--blue);color:var(--blue);}
  .paris-filters-drawer.open .accordion-header svg{stroke:var(--blue);}
  .badge-count{display:inline-flex;align-items:center;justify-content:center;min-width:15px;height:15px;padding:0 4px;border-radius:50%;background:var(--blue-solid);color:#fff;font-size:9px;font-weight:800;}
  .paris-filters-drawer .accordion-body{grid-area:body;}
  .paris-filters-drawer .accordion-body-pad{
    display:flex;flex-direction:column;gap:12px;padding:14px;margin-top:2px;
    border:1.5px solid var(--line);border-radius:var(--radius-sm);background:var(--card-2);
  }
  /* La liste des catégories (.paris-tier-menu, position:absolute) se
     déployait sous le bouton déclencheur puis se faisait couper net par
     l'overflow:hidden d'accordion-body-inner (components.css) — nécessaire
     pour l'animation de dépliage du tiroir, mais qui recadrait aussi tout
     menu qui déborderait ensuite de sa hauteur. Repassé en overflow:visible
     UNE FOIS le tiroir ouvert (via .open), avec un délai de transition égal
     à la durée de cette animation (.3s, voir .accordion-body dans
     components.css) : le repli garde son effet (overflow:hidden immédiat,
     aucun délai en sens inverse), et le menu catégories, lui, n'ouvre que
     bien après la fin du dépliage — jamais rogné. */
  .paris-filters-drawer.open .accordion-body-inner{overflow:visible;transition:overflow 0s .3s;}
  /* Sans ça : passer accordion-body-inner en overflow:visible ci-dessus lui
     retire aussi sa "taille minimale automatique" de conteneur de défilement
     (règle CSS Grid : un enfant de grille dont l'overflow n'est PAS visible a
     un min-width automatique de 0 ; visible, c'est sa taille min-content qui
     s'applique). Résultat mesuré : la rangée statut+vue (438px de contenu
     minimal, .paris-tournament-toolbar-row) imposait alors CETTE largeur à
     tout le tiroir au lieu de tenir dans les 350px alloués, et toute la page
     débordait horizontalement. min-width:0 explicite contourne le
     calcul automatique — inutile de choisir entre dropdown visible et
     largeur contenue, les deux marchent ensemble. */
  .paris-filters-drawer .accordion-body-inner{min-width:0;}
}
.paris-search-field{display:flex;align-items:center;gap:10px;flex:1;min-width:200px;padding:11px 16px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);transition:border-color .15s ease;}
.paris-search-field:focus-within{border-color:var(--sage);}
.paris-search-field svg{width:15px;height:15px;stroke:var(--ink-soft);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.paris-search-field input{border:0;outline:none;background:none;font:inherit;font-size:13px;font-weight:700;color:var(--ink);width:100%;}
.paris-search-field input::placeholder{color:var(--ink-soft);font-weight:500;}
.paris-search-field input::-webkit-search-cancel-button{cursor:pointer;}

.paris-tournament-empty-filter{grid-column:1/-1;padding:32px;text-align:center;color:var(--ink-soft);font-size:13px;}

/* Vue calendaire des tournois : frise groupée par mois, alternative à la
   grille de cartes (bascule #tournamentViewToggle). Une ligne compacte par
   tournoi plutôt qu'une grille de photos — pensé pour parcourir vite un
   grand nombre de dates, pas pour l'image d'illustration. */
.paris-timeline{display:flex;flex-direction:column;gap:28px;}
.paris-timeline-month-title{margin:0 0 12px;font-size:15px;font-weight:800;letter-spacing:-.1px;text-transform:capitalize;color:var(--ink);}
.paris-timeline-list{display:flex;flex-direction:column;gap:10px;}
.paris-timeline-row{
  display:flex;align-items:center;gap:16px;width:100%;padding:14px 16px;text-align:left;
  background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);
  font:inherit;color:var(--ink);cursor:pointer;transition:border-color .15s ease,transform .15s ease;
}
.paris-timeline-row:hover,.paris-timeline-row:focus-visible{border-color:var(--blue);transform:translateY(-2px);}
.paris-timeline-row-date{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:none;width:52px;padding:6px 0;border-radius:var(--radius-sm);background:var(--card-2);}
.paris-timeline-row-date .d{font-size:18px;font-weight:800;line-height:1.1;color:var(--ink);}
.paris-timeline-row-date .mo{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--ink-soft);}
.paris-timeline-row-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;}
.paris-timeline-row-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.paris-timeline-row-main h4{margin:0;font-size:15px;font-weight:800;letter-spacing:-.1px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.paris-timeline-row-main .meta{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--ink-soft);}
.paris-timeline-row-main .meta svg{width:13px;height:13px;stroke:var(--ink-soft);fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.paris-timeline-row-badges{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:6px;}
.paris-timeline-row-badges .badge{gap:5px;}
.paris-timeline-row-badges .badge svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex:none;}
/* Décompte des tournois majeurs (majorCountdownBadge, paris.js) : chiffres à
   largeur fixe pour que le badge ne "danse" pas à chaque seconde. */
.paris-countdown{gap:5px;font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;}
.paris-countdown svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex:none;}
@media (max-width:640px){
  .paris-timeline-row{flex-wrap:wrap;}
  .paris-timeline-row-badges{flex-direction:row;flex-wrap:wrap;align-items:center;}
}

/* Filtre catégories à cases à cocher (remplace l'ancien <select> à choix
   unique) : coquille .custom-select/.select-trigger/.select-menu réutilisée
   telle quelle (components.css #28) — ouverture/fermeture au clic, fermeture
   au clic extérieur/Échap déjà gérées par le délégué générique de
   components.js, qui ne cible QUE .select-trigger/.select-option. Les cases
   à cocher ci-dessous n'ont ni l'une ni l'autre classe : cocher une case ne
   ferme donc jamais le panneau, contrairement au select à option unique
   d'origine — exactement le comportement voulu pour cocher plusieurs
   catégories à la suite. */
.paris-tier-filter{min-width:220px;}
.paris-tier-menu{min-width:280px;max-width:min(340px,calc(100vw - 40px));padding:14px;}
.paris-tier-menu-actions{display:flex;justify-content:space-between;gap:10px;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid var(--line);}
.paris-tier-menu-link{background:none;border:0;padding:0;font-size:12px;font-weight:800;color:var(--blue);cursor:pointer;}
.paris-tier-menu-link:hover{text-decoration:underline;}
.paris-tier-menu-group{border:0;margin:0;padding:0;}
.paris-tier-menu-group+.paris-tier-menu-group{margin-top:10px;padding-top:10px;border-top:1px solid var(--line);}
.paris-tier-menu-group legend{padding:0 0 6px;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-soft);}
.paris-tier-option{display:flex;align-items:center;gap:9px;padding:7px 6px;border-radius:var(--radius-sm);font-size:12.5px;font-weight:600;color:var(--ink);cursor:pointer;}
.paris-tier-option:hover{background:var(--card-2);}
.paris-tier-option input{width:15px;height:15px;flex:none;accent-color:var(--sage);cursor:pointer;}

/* Cartes tournoi grand format, 2 par rangée (1 en dessous de 760px) : assez
   grandes pour porter l'image d'illustration du tournoi (fond de sa page PSA,
   voir psa-selectors.mjs#parseTournamentImageUrl) en pleine carte. */
.paris-tournament-grid{display:grid;grid-template-columns:1fr;gap:24px;}
@media (min-width:760px){.paris-tournament-grid{grid-template-columns:repeat(2,1fr);}}
/* Carte "photo" : un voile sombre fixe (pas les tokens de thème — comme le
   dessin du court dans studio.css, une photo ne s'adapte pas au clair/sombre)
   garantit un texte blanc lisible, que l'image PSA soit connue ou non. Sans
   image (tournoi pas encore synchronisé en détail), --tournament-card-image
   reste "none" et seul le dégradé de secours (dernière couche) est visible. */
.paris-tournament-card{
  --tournament-card-image:none;
  position:relative;display:flex;flex-direction:column;justify-content:space-between;gap:32px;
  text-align:left;padding:22px;min-height:240px;
  background:
    linear-gradient(180deg, rgba(10,14,8,.1) 0%, rgba(10,14,8,.55) 60%, rgba(10,14,8,.9) 100%),
    var(--tournament-card-image),
    linear-gradient(135deg, color-mix(in srgb, var(--sage) 55%, var(--card)), color-mix(in srgb, var(--blue) 45%, var(--card)));
  background-size:cover;background-position:center;background-repeat:no-repeat;
  border:1.5px solid var(--line);border-radius:var(--radius);cursor:pointer;overflow:hidden;
  font:inherit;color:#fff;
  transition:border-color .15s ease,transform .15s ease,box-shadow .15s ease;
}
.paris-tournament-card:hover,.paris-tournament-card:focus-visible{border-color:var(--blue);transform:translateY(-3px);box-shadow:var(--shadow-hover);}
/* Apparition en "pop" au rendu de la grille (même recette que .hp-section-card,
   public/home.css) — rejoue à chaque reconstruction de #tournamentGrid
   (filtre, page, bascule grille/calendrier), pas juste au premier chargement.
   "backwards", jamais "both" : la carte a déjà son propre transform au survol
   ci-dessus, un fill-mode qui le retiendrait après coup le verrouillerait pour
   de bon (l'origine "animation" gagne toujours sur une règle :hover normale).
   Décalage borné aux 8 premières cartes : au-delà, la cascade ne se verrait
   plus (page de 10 max, voir TOURNAMENT_PAGE_SIZE) et ferait juste patienter
   inutilement les dernières. */
@keyframes parisTournamentCardIn{from{opacity:0;transform:translateY(10px) scale(.95);}to{opacity:1;transform:translateY(0) scale(1);}}
.paris-tournament-card{animation:parisTournamentCardIn .55s cubic-bezier(.33,1,.68,1) backwards;}
.paris-tournament-grid .paris-tournament-card:nth-child(1){animation-delay:0s;}
.paris-tournament-grid .paris-tournament-card:nth-child(2){animation-delay:.05s;}
.paris-tournament-grid .paris-tournament-card:nth-child(3){animation-delay:.1s;}
.paris-tournament-grid .paris-tournament-card:nth-child(4){animation-delay:.15s;}
.paris-tournament-grid .paris-tournament-card:nth-child(5){animation-delay:.2s;}
.paris-tournament-grid .paris-tournament-card:nth-child(6){animation-delay:.25s;}
.paris-tournament-grid .paris-tournament-card:nth-child(7){animation-delay:.3s;}
.paris-tournament-grid .paris-tournament-card:nth-child(n+8){animation-delay:.35s;}
@media (prefers-reduced-motion:reduce){.paris-tournament-card{animation:none;}}
.paris-tournament-card-body{display:flex;flex-direction:column;gap:4px;}
.paris-tournament-badges{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;}
.paris-tournament-card-body .badge{align-self:flex-start;gap:5px;}
.paris-tournament-card-body .badge svg{width:11px;height:11px;stroke:currentColor;fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.paris-tournament-card h3{margin:0 0 2px;font-size:21px;letter-spacing:-.3px;}
.paris-tournament-card .meta{display:flex;align-items:center;gap:6px;font-size:12.5px;color:rgba(255,255,255,.85);}
.paris-tournament-card .meta svg{width:13px;height:13px;stroke:rgba(255,255,255,.85);fill:none;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.paris-tournament-card-head{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;}
.paris-tournament-location{display:flex;align-items:center;gap:6px;}

.paris-gender-badges{display:flex;align-items:center;gap:4px;}
/* Pas de couleur par genre (♂ bleu / ♀ prune) : en thème sombre certains
   tokens de couleur perdent leur contraste et les deux pastilles se
   confondent. Distinguées par leur seul symbole, pas par une teinte. */
.paris-gender-icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:12px;font-weight:800;line-height:1;background:var(--card);color:var(--ink-soft);border:1.5px solid var(--line);}

/* Badges de catégorie PSA — dégradé métallique par palier (tokens -from/-to/
   -ink dans tokens.css, voir le commentaire là-bas pour le pourquoi). Les
   .badge génériques (5 couleurs de marque) ne suffisaient pas à distinguer
   8+ paliers officiels — ici la hiérarchie doit se lire même sans lire le
   texte : le badge Majeur est le seul en majuscules + halo, tout en bas
   Challenger reste volontairement plat/neutre (circuit à part, pas "moins
   bien" qu'un palier régulier bas de gamme). inset shadow blanche = reflet
   métallique, même trucage CSS sur les 6 paliers réguliers. */
.paris-tier-badge{display:inline-flex;align-items:center;gap:5px;padding:5px 12px;border-radius:var(--pill);font-size:12px;font-weight:800;letter-spacing:.02em;border:1px solid rgba(255,255,255,.25);box-shadow:inset 0 1px 0 rgba(255,255,255,.35),0 1px 2px rgba(0,0,0,.15);white-space:nowrap;}
.paris-tier-badge--major{padding:6px 14px;font-size:12px;text-transform:uppercase;background:linear-gradient(135deg,var(--tier-major-from),var(--tier-major-to));color:var(--tier-major-ink);box-shadow:inset 0 1px 0 rgba(255,255,255,.4),0 2px 10px color-mix(in srgb, var(--tier-major-to) 45%, transparent);}
.paris-tier-badge--major svg{width:12px;height:12px;stroke:currentColor;fill:var(--tier-major-ink);flex:none;}
.paris-tier-badge--diamond{background:linear-gradient(135deg,var(--tier-diamond-from),var(--tier-diamond-to));color:var(--tier-diamond-ink);}
.paris-tier-badge--platinum{background:linear-gradient(135deg,var(--tier-platinum-from),var(--tier-platinum-to));color:var(--tier-platinum-ink);}
.paris-tier-badge--gold{background:linear-gradient(135deg,var(--tier-gold-from),var(--tier-gold-to));color:var(--tier-gold-ink);}
.paris-tier-badge--silver{background:linear-gradient(135deg,var(--tier-silver-from),var(--tier-silver-to));color:var(--tier-silver-ink);}
.paris-tier-badge--bronze{background:linear-gradient(135deg,var(--tier-bronze-from),var(--tier-bronze-to));color:var(--tier-bronze-ink);}
.paris-tier-badge--copper{background:linear-gradient(135deg,var(--tier-copper-from),var(--tier-copper-to));color:var(--tier-copper-ink);}
.paris-tier-badge--challenger{background:var(--card-2);color:var(--ink-soft);border-color:var(--line);box-shadow:none;font-weight:700;}
/* Dans l'en-tête d'un tournoi ouvert (#parisIntro) : un peu plus grand que
   sur une carte, pour porter le regard au même titre que le nom du tournoi.
   .paris-intro-badges regroupe le(s) badge(s) hommes/femmes quand ils
   diffèrent — remplace l'ancien kicker texte simple. */
.paris-intro-titre{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;}
.paris-intro-badges{display:flex;flex-wrap:wrap;gap:8px;}
.paris-intro-badges:empty{display:none;}
.paris-intro .paris-tier-badge{font-size:12.5px;padding:6px 14px;}
/* Sous-titre : version longue sur desktop, abrégée (mois courts, « $ » seul)
   sur mobile pour tenir sur une ligne. */
.paris-intro-court{display:none;}
@media(max-width:640px){.paris-intro-long{display:none;}.paris-intro-court{display:inline;}}

/* Pleine largeur (demande explicite) : .sx-tabs est inline-flex par défaut
   (largeur au contenu, voir components.css) — ici les 2 boutons se
   partagent TOUTE la largeur disponible à parts égales plutôt que de rester
   collés à gauche. */
#bracketGenderTabs{display:flex;width:100%;margin-bottom:4px;}
#bracketGenderTabs .sx-tab{flex:1;text-align:center;}
/* Sous-onglets de mode (match par match / prédiction complète) rattachés
   sous les onglets de genre — ligne dédiée, pour lire comme un second niveau
   plutôt que fusionner avec la ligne des onglets de genre. */
/* Onglets Hommes/Femmes et mode de pronostic sur une ligne ; la bascule
   Tableau/Liste vit dans la barre des tours (.paris-round-nav). Toute la barre
   disparaît quand aucun de ses onglets n'est affiché (cas courant : un seul
   genre, oneshot éteint) plutôt que de laisser une marge vide. */
.paris-bracket-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px;}
.paris-bracket-toolbar:not(:has(> :not([hidden]))){display:none;}
#oneshotPanel{display:flex;flex-direction:column;gap:18px;}
#oneshotSummary:empty{display:none;}
.paris-oneshot-card{display:flex;flex-direction:column;gap:8px;padding:18px 20px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
.paris-oneshot-rules{margin:0;font-size:12px;color:var(--ink-soft);max-width:56ch;}
/* La mise se choisit AVANT de parcourir le tableau, pas après — posée en
   haut, elle reste visible pendant tous les pronostics. */
#oneshotStakeBar{background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);padding:18px 20px;}
.paris-oneshot-submit-row{display:flex;justify-content:center;}
.sx-bracket-match[data-oneshot-round] .match-card .side{cursor:pointer;}
/* Oneshot : pas de stepper à droite (pas de mise par match, une seule mise
   globale en haut du tableau) — les 3 scores restent groupés à gauche plutôt
   que de se répartir sur toute la largeur via le space-between hérité de
   .paris-score-options (pensé pour score+stepper côte à côte ailleurs). */
.sx-bracket-match[data-oneshot-round] .paris-score-options{justify-content:flex-start;}
/* Le badge "Gain potentiel : min–max MZK" par phase peut être plus long que
   le label du tour ("À venir" etc.) — laisser passer à la ligne plutôt que
   déborder d'une colonne de 240px, sans toucher aux têtes de tour du VRAI
   tableau (#bracket), qui n'ont qu'un court badge "À venir". */
#oneshotBracketRounds .sx-bracket-round-head{flex-wrap:wrap;row-gap:4px;}

/* Vue liste du mode "Match par match" (#bracketViewToggle) : des lignes
   fines qui prennent toute la largeur disponible (contrairement aux
   colonnes de 240px de .sx-bracket-rounds) — tour + horaire dans une colonne
   fixe à gauche, carte de match compacte à droite. Choisir un vainqueur y
   déplie le score puis la mise SOUS la carte (même mécanique déjà en place
   pour le tableau, voir le clic délégué sur #bracket) : la ligne grandit le
   temps de la saisie plutôt que d'ouvrir un second panneau. */
/* Seul enfant direct de #bracketRounds (voir renderMatchesListHtml,
   public/paris.js) — .sx-bracket-rounds est display:flex EN LIGNE, ce wrapper
   empile la liste des matchs à venir et l'accordéon "Matchs terminés"
   verticalement plutôt que de les laisser se partager la largeur comme deux
   colonnes du tableau. */
/* Groupe par tournoi dans "Paris en cours" (renderCurrentListView,
   public/paris.js) : PAS d'accordéon — une carte (le groupe) enveloppant
   des cartes (chaque match) donnait un aspect "container dans container"
   et son padding mangeait la largeur déjà comptée en vue mobile (décision
   actée). Juste un intitulé cliquable suivi directement des lignes de
   match, sans boîte/bordure autour du groupe. */
.paris-tournament-group{width:100%;}
.paris-tournament-group+.paris-tournament-group{margin-top:18px;border-top:1.5px solid var(--line);padding-top:10px;}
.paris-tournament-group-head{display:flex;align-items:center;gap:8px;padding:4px 4px 0;}
.paris-tournament-group-link{border:0;background:none;padding:0;font:inherit;font-size:13.5px;font-weight:800;color:inherit;cursor:pointer;text-align:left;}
.paris-tournament-group-link:hover{color:var(--blue);text-decoration:underline;}
.paris-tournament-group-link:focus-visible{outline:2px solid var(--blue);outline-offset:2px;border-radius:3px;}
/* Bilan perso du tournoi (pastGroupStatsHtml, public/paris.js) — 4 mêmes
   indicateurs que #tournamentSummary (.kpi-card, un peu plus haut dans ce
   fichier) mais en ligne compacte sous l'intitulé du groupe : "Paris passés"
   liste potentiellement plusieurs tournois d'affilée, une carte KPI complète
   par tournoi aurait pris toute la largeur. */
.paris-tournament-group-stats{display:flex;flex-wrap:wrap;gap:4px 18px;padding:6px 4px 2px;}
.paris-tg-stat{display:flex;align-items:baseline;gap:5px;font-size:12px;}
.paris-tg-stat-lbl{color:var(--ink-soft);}
.paris-tg-stat-val{font-weight:800;color:var(--ink);}
.paris-tg-stat-val.is-positive{color:var(--sage);}
.paris-tg-stat-val.is-negative{color:var(--danger);}

.paris-matches-list-wrap{display:flex;flex-direction:column;width:100%;}
.paris-matches-list{display:flex;flex-direction:column;gap:2px;padding:4px 2px 12px;width:100%;}
/* L'horaire empilé au-dessus de la carte plutôt qu'à côté (colonne fixe
   qu'il fallait autrefois large de 150px pour loger le round — "Seizièmes
   de finale", le plus long intitulé — avant qu'il ne devienne l'en-tête de
   groupe .paris-round-head ci-dessous) : une seule mise en page, valable à
   toute largeur d'écran, plus de variante mobile séparée à maintenir. */
.paris-matches-list-row{display:flex;flex-direction:column;align-items:stretch;gap:6px;width:100%;padding:12px 4px;border-bottom:1.5px solid var(--line);}
.paris-matches-list-row:first-child{border-top:1.5px solid var(--line);}
.paris-matches-list-info{display:flex;align-items:center;gap:6px;padding:0 2px;font-size:12px;color:var(--ink-soft);}
.paris-matches-list-time{font-weight:700;color:var(--ink);white-space:nowrap;}
.paris-matches-list-row .match-card{padding:10px 12px;}
.paris-matches-list-row .match-card.is-open,.paris-matches-list-row .match-card.is-placed,.paris-matches-list-row .match-card.is-locked,.paris-matches-list-row .match-card.is-won,.paris-matches-list-row .match-card.is-lost{padding-left:40px;}
/* "Paris en cours" (currentMatchRowHtml, [data-bet]) ET le mode liste du
   tableau UNE FOIS le résultat connu (matchesListRowHtml, [data-result] —
   voir plus bas) : même cadran de droite (mise/net/coche-croix), donc même
   traitement. Un match encore ouvert au pari ([data-match], pas de résultat)
   garde lui son coin d'état (horloge/ticket/cadenas) — rien d'autre n'y
   indique l'état tant qu'il n'y a pas de résultat à afficher à droite. Ici
   ce badge part dans le cadran de droite à la place (voir .paris-bet-
   status-icon plus bas) : plus de réservation de marge à gauche pour lui, ni
   de pavé de fond sur le camp vainqueur (voir .is-real-winner ci-dessous) —
   juste le nom mis en avant par la couleur, la carte reste à fond plat comme
   le reste de la ligne. */
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .mc-corner-icon,
.paris-matches-list-row .paris-predict-wrap[data-result] .match-card .mc-corner-icon{display:none;}
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card.is-open,
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card.is-placed,
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card.is-locked,
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card.is-won,
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card.is-lost,
.paris-matches-list-row .paris-predict-wrap[data-result] .match-card.is-won,
.paris-matches-list-row .paris-predict-wrap[data-result] .match-card.is-lost{padding-left:12px;}
/* Carte à fond plat dans cette liste — pas de pavé/contour coloré par état
   (is-open/is-placed/is-locked/is-won/is-lost, components.css) : seuls le
   badge score et, pour un résultat connu, la coche/croix du cadran de droite
   portent la couleur. Le pavé vert du VRAI vainqueur (.is-real-winner), lui,
   reste — [data-result] (tableau, matchesListRowHtml) n'annule plus ce
   pavé : un vainqueur doit toujours être entouré en vert, qu'un pari existe
   dessus ou non, pas seulement son nom en couleur. */
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card,
.paris-matches-list-row .paris-predict-wrap[data-result] .match-card{background:transparent;border-color:transparent;box-shadow:none;}
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card:hover,
.paris-matches-list-row .paris-predict-wrap[data-result] .match-card:hover{transform:none;box-shadow:none;}
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .side.is-real-winner .side-highlight{background:transparent;border-color:transparent;}
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .side.is-real-winner .side-name{color:var(--sage-2);}
/* Le pronostic (pas encore réglé) se distingue du vrai vainqueur (.is-real-
   winner juste au-dessus, resté sage/texte seul) par un pavé bleu PLEIN —
   couleur du choix actif, cohérente avec .selected en édition juste plus
   bas — plutôt qu'un simple texte coloré : plus visible dans une liste dense.
   Nom ET cote (celle-ci bleue par défaut, components.css) repassent en blanc
   pour rester lisibles sur ce fond. */
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .side.is-predicted-winner .side-highlight{background:var(--blue-solid);border-color:transparent;}
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .side.is-predicted-winner .side-name,
.paris-matches-list-row .paris-predict-wrap[data-bet] .match-card .side.is-predicted-winner .paris-odds{color:#fff;}
/* Coche/croix de résultat, cadran de droite (currentMatchRowHtml) — même
   gabarit rond que .icon-action-btn (components.css), fond plein plutôt que
   contour : c'est un état, pas une action cliquable. */
.paris-bet-status-icon{width:22px;height:22px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;flex:none;color:#fff;}
.paris-bet-status-icon svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;}
.paris-bet-status-icon.is-won{background:var(--sage);}
.paris-bet-status-icon.is-lost{background:var(--danger);}
/* Cadenas (pari verrouillé, match pas encore joué) : neutre, pas gagné/perdu
   — mêmes tons que .badge--neutral, pas de couleur d'accent (réservée aux
   illustrations, voir CLAUDE.md). */
.paris-bet-status-icon.is-locked{background:var(--card-2);color:var(--ink-soft);}
/* Round mutualisé (voir groupRowsByRound, public/paris.js) : un seul
   intitulé au-dessus d'un groupe de lignes consécutives qui le partagent,
   au lieu de le répéter dans .paris-matches-list-info sur chaque ligne —
   qui ne garde donc plus que l'horaire. Même logique de section que
   .paris-tournament-group-head, un ton plus discret (sous-groupe DANS un
   groupe tournoi côté "Paris en cours"). */
.paris-round-head{padding:4px 4px 0;margin-top:16px;font-size:11.5px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.04em;}
.paris-matches-list>.paris-round-head:first-child{margin-top:2px;}
/* Photo + drapeau centrés au-dessus du nom : on NE surcharge plus le
   .side à plat (ancienne variante "carte à plat", ci-dessous) — on repart
   de la mise en page verticale par défaut de .match-card .side
   (components.css), juste réduite pour tenir dans une liste, et affichée à
   TOUTE largeur d'écran (fini la variante mobile sans photo/drapeau —
   décision actée après retour utilisateur : "un peu trop compact" visait le
   blanc entre les lignes, pas la présence de la photo). */
.paris-matches-list-row .match-card .side{position:relative;padding:5px;}
/* Cadran fixe ("Paris en cours" + pari existant repris en vue liste tournoi,
   voir .paris-bet-cadran) : sans largeur imposée, .side reste shrink-wrap
   (flex-item par défaut) et le pavé bleu du camp sélectionné/pronostiqué ne
   fait que la largeur de son nom — deux pavés de tailles différentes selon
   le côté, jamais alignés d'une ligne à l'autre de la liste. Les deux camps
   se partagent maintenant la largeur dispo à parts égales (le score garde la
   sienne, flex:none) ; .side-highlight, déjà centré horizontalement en
   interne (components.css), DEVRAIT remplir ce bloc plus large — mais
   .side-highlight/.side-text, comme .side lui-même (components.css), sont
   des flex en colonne avec align-items:center : sur l'axe transversal
   (horizontal ici), ça centre SANS étirer, seul align-self:stretch en cède
   la largeur à l'enfant. .side-name l'avait déjà (ligne juste en dessous)
   mais s'étirer dans un .side-text lui-même pas étiré ne change rien — la
   chaîne complète (highlight → text → name/odds) est nécessaire, sinon le
   pavé bleu ne fait toujours que la largeur du texte. Perd face à
   .side{flex:0 0 240px} (plus haut dans ce fichier, desktop large) là où
   cette autre disposition s'applique déjà — rien à exclure explicitement,
   la spécificité tranche seule. */
.paris-bet-cadran .match-card .side{flex:1 1 0;min-width:0;}
.paris-matches-list-row .match-card .side .side-highlight{align-self:stretch;}
.paris-matches-list-row .match-card .side .side-text{align-self:stretch;min-width:0;}
.paris-matches-list-row .match-card .side .av{width:30px;height:30px;font-size:12px;}
.paris-matches-list-row .match-card .side .side-name{align-self:stretch;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;}
.paris-matches-list-row .match-card .side .paris-odds{align-self:stretch;min-width:0;font-size:13px;}
/* text-align:center uniquement en dessous de 860px : au-delà, la vue liste
   condensée (plus bas dans ce fichier) passe .side-highlight en ligne
   (avatar à gauche, nom/cote empilés à droite, alignés à gauche contre la
   photo — .side-text{align-items:flex-start}) — centrer le TEXTE y aurait
   détaché nom/cote de l'avatar au lieu de rester collés contre lui. */
@media (max-width:859.98px) {
  .paris-matches-list-row .match-card .side .side-name,
  .paris-matches-list-row .match-card .side .paris-odds{text-align:center;}
}

/* Vue liste, TOUTES largeurs (mobile compris) : une fois les 3 boutons de
   score visibles, le "VS" qu'ils remplacent devient redondant juste au-
   dessus/à côté d'eux — vu aussi bien sur la carte verticale mobile que sur
   la ligne dense desktop plus bas. Côté "Paris en cours" ([data-bet]), le
   score suit la même logique mais via .only-read/.only-edit — voir
   .paris-bet-cadran juste en dessous. */
.paris-matches-list-row .paris-predict-wrap[data-match]:has(.paris-score-options:not([hidden])) .match-card-score .vs{display:none;}
/* "Cadran fixe" (currentMatchRowHtml/currentMatchRowFallbackHtml, public/
   paris.js) : la carte ne change JAMAIS de gabarit entre lecture et édition —
   seul .is-editing sur .paris-bet-cadran bascule quel contenu se montre à
   l'intérieur (pilule de score ou 3 boutons, mise en texte ou stepper,
   crayon+croix rouge ou croix neutre+coche), jamais quel bloc existe. */
.paris-bet-cadran:not(.is-editing) .only-edit{display:none;}
.paris-bet-cadran.is-editing .only-read{display:none;}
/* Le contenu qui réapparaît (passage de display:none à visible) se fond au
   lieu de sauter sec — l'animation CSS ne "tourne" pas tant que l'élément est
   display:none, elle se redéclenche donc naturellement à chaque bascule
   lecture ⇄ édition, sans JS dédié. */
@keyframes parisRailReveal{from{opacity:0;transform:translateY(-3px);}to{opacity:1;transform:translateY(0);}}
.paris-bet-cadran .only-read,
.paris-bet-cadran .only-edit{animation:parisRailReveal .16s ease;}
/* Pendant l'édition, le pavé "vainqueur déjà parié" (bleu, posé par
   matchCardHtml même en lecture — voir .is-predicted-winner plus haut)
   s'efface pour laisser place au choix EN COURS (.selected, même bleu plein)
   — sinon les deux se superposaient dès qu'on changeait de camp sans avoir
   encore validé. Sélecteurs alourdis à dessein (.paris-matches-list-row en
   préfixe + [data-edit-side] sur .side) : à spécificité égale ou moindre,
   ces règles perdaient contre .side.is-predicted-winner (ligne ~923, posée
   par le MÊME contexte .paris-matches-list-row .paris-predict-wrap[data-bet])
   — le camp initialement parié restait bleu avec son nom en blanc même après
   avoir cliqué l'autre joueur, la bascule ne se voyait jamais. */
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side] .side-highlight{border-color:transparent;background:transparent;}
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side] .side-name{color:var(--ink);}
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side] .paris-odds{color:var(--accent, var(--blue));}
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side].selected .side-highlight{border-color:transparent;background:var(--blue-solid);}
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side].selected .side-name,
.paris-matches-list-row .paris-bet-cadran.is-editing .match-card .side[data-edit-side].selected .paris-odds{color:#fff;}
/* Boutons de score (3-0/3-1/3-2) : encore un peu gros une fois un nom long
   tronqué à côté, sous 860px — réduits pour laisser le maximum de place aux
   deux camps sans que le centre n'impose sa largeur. */
@media (max-width:859.98px) {
  .paris-bet-cadran.is-editing .paris-score-buttons .btn{padding:0 6px;font-size:11px;min-height:26px;}
  .paris-bet-cadran.is-editing .paris-score-buttons{gap:3px;}
}
/* Bordure "en cours d'édition" du cadran fixe lui-même — seul repère qui dit
   "ceci n'est plus une carte de lecture, c'est modifiable maintenant", sur
   "Paris en cours" (currentMatchRowHtml) comme sur un pari existant repris en
   vue liste tournoi (matchesListRowHtml). outline, pas border : ne déplace
   rien dans le flex du wrap (card + .paris-bet-rail), contrairement à un
   border+padding. Statique, sans pulsation : contrairement à .paris-score-
   options (édition d'un NOUVEAU pari, pas encore engagé), ce cadran modifie
   un pari DÉJÀ posé — une animation continue y attirerait l'œil en permanence
   sur une carte qu'on regarde souvent sans vouloir y toucher. */
.paris-bet-cadran.is-editing .match-card{
  outline:1.5px dashed color-mix(in srgb, var(--mustard) 60%, var(--line));
  outline-offset:6px;
  border-radius:var(--radius-sm);
}
/* Annulation d'un pari ("Paris en cours", croix rouge only-read ci-dessus) :
   la ligne entière se rétracte (hauteur, fondu, marges) avant de disparaître
   du DOM au rechargement — voir animateRowRemoval, paris.js. max-height posé
   en JS juste avant (ne peut pas partir de "auto" en CSS, sinon rien ne se
   voit transiter). */
.paris-row-removing{
  transition:max-height .22s ease, opacity .22s ease, margin .22s ease, padding .22s ease, transform .22s ease;
  opacity:0;
  transform:scale(.97);
  margin-top:0!important;
  margin-bottom:0!important;
  padding-top:0!important;
  padding-bottom:0!important;
}
/* Validation d'un pari (nouveau ou modifié) : un bref halo bleu (accent de la page) sur le
   cadran, posé par paris.js juste après la réponse serveur — confirme
   visuellement "enregistré" avant que la liste ne se recharge autour, plutôt
   qu'un montant qui change sans transition perceptible. */
@keyframes parisBetSuccessFlash{
  0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--accent, var(--blue)) 55%, transparent);}
  40%{box-shadow:0 0 0 6px color-mix(in srgb, var(--accent, var(--blue)) 22%, transparent);}
  100%{box-shadow:0 0 0 0 transparent;}
}
.paris-bet-success-flash{animation:parisBetSuccessFlash .5s ease;border-radius:var(--radius-sm);}
/* Une case de tableau (matchWrapContentHtml, voir updateMatchWrap) dont le
   contenu vient d'être remplacé d'un coup (innerHTML) — après validation,
   modification ou annulation d'un pari sur CETTE case précise : un fondu
   d'entrée bref évite le saut sec entre l'ancien et le nouveau contenu. */
@keyframes parisWrapRefresh{from{opacity:.35;}to{opacity:1;}}
.paris-wrap-refresh{animation:parisWrapRefresh .22s ease;}

/* Vue liste, desktop large (≥860px) : au-delà de cette largeur, la carte
   verticale ci-dessus laisse le reste de l'écran vide et force un long
   défilement pour voir tous les matchs d'un tour — voir maquettes discutées
   avec l'équipe. À la différence de l'ancienne variante "carte à plat"
   abandonnée (mise en page verticale reprise à TOUTE largeur d'écran,
   décision actée plus haut) : ici la disposition dense ne s'applique qu'À
   PARTIR de cette largeur, mobile et tablette gardent la carte verticale
   inchangée — deux gabarits à maintenir, assumé pour ce gain d'espace-là.
   Tout passe par CSS sur le DOM déjà généré par matchCardHtml/matchPredictForm :
   aucun changement JS.
   Le "VS" devient les 3 boutons de score (matchPredictForm les rend déjà,
   simplement [hidden] jusqu'au clic sur un joueur) — mais
   .paris-score-options est un SIBLING du .match-card porteur du "VS", pas un
   enfant de son .match-card-score : impossible de le repositionner pile à sa
   place sans le sortir de son parent. `display:contents` sur .match-card
   (seulement pendant l'édition) aplatit sa boîte : .side et .match-card-score
   deviennent alors des items flex directs du même conteneur que
   .paris-score-options, qu'un simple `order` peut superposer au VS. */
@media (min-width: 860px) {
  .paris-matches-list-row{flex-direction:row;align-items:center;gap:14px;padding:10px 8px;}
  .paris-matches-list-row .paris-matches-list-info{flex:0 0 100px;padding:0;}
  .paris-matches-list-row .match-card{flex:1 1 auto;min-width:0;padding:8px 12px;gap:10px;}
  .paris-matches-list-row .match-card .side{padding:3px 6px;}
  .paris-matches-list-row .match-card .side .av{width:32px;height:32px;}
  .paris-matches-list-row .paris-bet-actions{flex:0 0 auto;margin-top:0;margin-left:auto;}
  /* Toute la liste (les 3 gabarits : [data-bet] "Paris en cours", [data-match]
     "Nouveau pari" pas encore joué, [data-result] résultat connu au tableau —
     voir plus bas) : c'est LE WRAP qui doit occuper toute la largeur laissée
     par la colonne horaire — sans ça, un flex-item sans flex-grow explicite
     garde sa largeur de contenu, et .match-card avait beau demander
     flex:1 1 auto, rien ne lui était distribué (le wrap ne s'étirait
     jamais). Le bloc joueurs, lui, reste à largeur de contenu (flex:0 1 auto
     plus bas) : c'est l'espace ENTRE ce bloc et le cadran mise/résultat (ou
     le formulaire de pari) qui doit absorber le vide, pas les 3 éléments
     internes de .match-card qui s'écarteraient sinon les uns des autres
     (space-between hérité de components.css). */
  .paris-matches-list-row .paris-predict-wrap{flex:1 1 auto;min-width:0;}
  /* .match-card à taille FIXE (flex:0 0 auto, width:auto écrasant le
     width:100% de base) — PAS flex-grow : une carte qui s'étire pour
     occuper la largeur restante du wrap donne une taille différente à
     chaque ligne selon ce qu'il y a À CÔTÉ (mise+gain+boutons pour un pari
     en cours, juste "Sans pronostic" pour un match sans pari, rien du tout
     pour une carte nue) — déjà essayé, corrigé après retour : la carte doit
     être un gabarit unique, répété identique sur toute la liste, pas une
     grandeur variable. Un match terminé sur lequel rien n'a été parié n'a
     pas de wrap du tout dans certains cas (.match-card enfant DIRECT de la
     ligne, voir matchesListRowHtml) — même règle là aussi, par le sélecteur
     enfant direct juste après. */
  .paris-matches-list-row .paris-predict-wrap .match-card,
  .paris-matches-list-row > .match-card{flex:0 1 auto;width:auto;}
  /* Alignement "table" (voir la maquette) : chaque camp occupe une colonne
     de largeur FIXE plutôt que la largeur de son propre contenu — sinon le
     badge score se décale ligne à ligne selon la longueur du nom du joueur.
     Un nom court laisse juste du vide entre la colonne horaire et l'avatar
     (justify-content pousse le contenu contre le score, jamais l'inverse) ;
     un nom trop long tronque (.side-name, ellipsis) plutôt que d'élargir la
     colonne et de redécaler tout le monde. Le contenu (avatar, nom, cote)
     garde l'ordre du DOM des deux côtés — c'est `order` par enfant, pas
     flex-direction, qui inverse la lecture côté 2 (cote/nom/avatar) sans
     retoucher le sens de empilement de la colonne elle-même. Sur TOUTE la
     liste, pas seulement "Paris en cours" — un match encore ouvert au pari
     ([data-match]), terminé sans pari (badge "Sans pronostic" à droite) ou
     avec pari réglé ([data-result]) doit s'aligner PAREIL, avec la même
     largeur de carte : flex:0 0 240px (largeur figée, ni grow ni shrink) —
     pas de flex-grow ici non plus, pour la même raison qu'au-dessus. 240px
     (pas plus) : le cadran de droite le plus large (mise+gain+2 boutons,
     "Nouveau pari") doit tenir sur la MÊME ligne que la carte à la largeur
     de page réelle (.paris-main, 1100px) — une carte plus large faisait
     déborder la ligne et le cadran retombait, seul, sur une 2e ligne. */
  .paris-matches-list-row .match-card .side{flex:0 1 240px;min-width:0;}
  .paris-matches-list-row .match-card .side .side-highlight{min-width:0;}
  /* Tout ce qui suit (avatar référence de hauteur + nom/cote empilés à côté,
     pavé sans bordure, police neutre du score) : la vue liste condensée,
     désormais partagée par TOUTE la liste — tournoi ([data-match] pas encore
     joué, pari existant compris, [data-result] réglé ou sans pronostic, +
     la carte nue sans wrap) ET "Paris en cours" ([data-bet] seul). Les deux
     utilisaient jusqu'ici deux gabarits différents (celui-ci horizontal et
     compact, "Paris en cours" vertical et plus haut, hérité de la mise en
     page mobile par défaut de .match-card .side, components.css) — demande
     explicite : les condenser au même gabarit, plus dense. */
  .paris-matches-list-row .paris-predict-wrap .match-card .side,
  .paris-matches-list-row > .match-card .side{display:flex;align-items:center;}
  .paris-matches-list-row .paris-predict-wrap .match-card .side[data-side="1"],
  .paris-matches-list-row > .match-card .side[data-side="1"]{justify-content:flex-end;}
  .paris-matches-list-row .paris-predict-wrap .match-card .side[data-side="2"],
  .paris-matches-list-row > .match-card .side[data-side="2"]{justify-content:flex-start;}
  /* Photo à gauche, nom+cote empilés à droite : la photo (32px) sert de
     référence de hauteur pour ces 2 lignes de texte, plutôt que la cote sur
     sa propre ligne pleine largeur sous tout le bloc. Avatar TOUJOURS avant
     le nom, sur les deux côtés — plus de lecture inversée (cote/nom/avatar)
     côté 2, une seule règle pour les deux plutôt que deux jeux de
     order/flex-direction à maintenir. */
  .paris-matches-list-row .paris-predict-wrap .match-card .side .side-highlight,
  .paris-matches-list-row > .match-card .side .side-highlight{flex-direction:row;align-items:center;gap:8px;}
  /* Nom au-dessus de la cote, empilés l'un sous l'autre, alignés à gauche
     (contre la photo) quel que soit le côté du match. */
  .paris-matches-list-row .paris-predict-wrap .match-card .side .side-text,
  .paris-matches-list-row > .match-card .side .side-text{align-items:flex-start;gap:1px;}
  /* Seul le nom cède de la place (ellipsis, déjà géré) : l'avatar ne doit
     jamais rétrécir. Largeur réduite (165 -> 150) : la cote n'a plus besoin
     de sa propre ligne pleine largeur, mais partage maintenant la colonne
     avec l'avatar. */
  .paris-matches-list-row .paris-predict-wrap .match-card .side .side-name,
  .paris-matches-list-row > .match-card .side .side-name{max-width:150px;}
  .paris-matches-list-row .paris-predict-wrap .match-card .side .av-wrap,
  .paris-matches-list-row > .match-card .side .av-wrap{flex:none;}
  /* Sélection/vainqueur : un fond coloré suffit à se voir dans cette liste
     dense, une bordure en plus alourdissait — border-color reste transparent
     ici (le fond seul porte l'état), que ce soit .selected (choix en cours)
     ou is-predicted/real-winner : les trois portent désormais leur pavé sur
     .side-highlight (photo+nom+cote), voir components.css/paris.css. */
  .paris-matches-list-row .paris-predict-wrap .match-card .side.selected .side-highlight,
  .paris-matches-list-row .paris-predict-wrap .match-card .side.is-predicted-winner .side-highlight,
  .paris-matches-list-row .paris-predict-wrap .match-card .side.is-real-winner .side-highlight,
  .paris-matches-list-row > .match-card .side.is-real-winner .side-highlight{border-color:transparent;}
  /* Score et cote en Roboto Mono plutôt que --mono (SF Mono/Menlo/Consolas) :
     ce dernier a un genre trop "console/rétro" pour un chiffre lu au premier
     coup d'œil — Roboto Mono reste neutre, chiffres alignés (utile pour les
     cotes en colonne) et zéro plein, jamais barré/pointé. Voir le <link>
     Google Fonts dans paris.html. */
  .paris-matches-list-row .paris-predict-wrap .match-card-score .vs,
  .paris-matches-list-row > .match-card .match-card-score .vs,
  .paris-matches-list-row .paris-predict-wrap .match-card .paris-odds,
  .paris-matches-list-row > .match-card .paris-odds{font-family:inherit;}

  /* Onglet "Nouveau pari" (tableau ouvert) : matchPredictForm. Le sélecteur
     [data-match] cible spécifiquement CE wrap (posé par matchesListRowHtml) —
     le wrap de "Paris en cours" ci-dessous porte [data-bet], jamais
     [data-match], et son score (.paris-bet-score-zone, betScoreZoneHtml) ne
     porte de toute façon pas la classe .paris-score-options : ce :has() ne
     peut donc pas s'y déclencher par erreur. */
  /* nowrap : carte, score et rail de mise restent sur UNE seule rangée (2 lignes
     de texte max : nom/cote à gauche, mise+gain / solde+boutons à droite). Quand
     la place manque, ce sont les noms (ellipsis) qui cèdent, jamais le rail qui
     retombe sous la carte. Le détail des parieurs, lui, a besoin du retour à
     la ligne : il le retrouve tant qu'il est ouvert. */
  .paris-matches-list-row .paris-predict-wrap{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;gap:10px;}
  .paris-matches-list-row .paris-predict-wrap:has(.paris-parieurs-panel:not([hidden])){flex-wrap:wrap;}
  .paris-matches-list-row .paris-predict-wrap[data-match]:has(.paris-score-options:not([hidden])) .match-card{display:contents;}
  .paris-matches-list-row .paris-predict-wrap[data-match]:has(.paris-score-options:not([hidden])) .mc-corner-icon{display:none;}
  .paris-matches-list-row .paris-predict-wrap .side[data-side="1"]{order:1;}
  .paris-matches-list-row .paris-predict-wrap .match-card-score{order:2;}
  .paris-matches-list-row .paris-predict-wrap .paris-score-options{order:2;margin:0;}
  .paris-matches-list-row .paris-predict-wrap .side[data-side="2"]{order:3;}
  .paris-matches-list-row .paris-predict-wrap .paris-predict{display:contents;}
  .paris-matches-list-row .paris-predict-wrap .paris-predict-summary{order:4;flex:0 0 auto;margin-left:auto;}
  /* "Pas encore de pari" (newBetListFormHtml) : même rail à droite que le
     cadran fixe ci-dessous (.paris-bet-rail, filet pointillé) — .paris-stake-
     row reste le sélecteur que le clic délégué #bracket masque/démasque,
     .paris-bet-rail n'apporte que l'habillage. Sélecteur à 4 classes pour
     battre .paris-stake-row seul (base, plus haut dans ce fichier — colonne
     pleine largeur du tableau) sans dépendre de l'ordre des règles. */
  .paris-matches-list-row .paris-predict-wrap .paris-stake-row.paris-bet-rail{order:5;flex:0 0 240px;display:flex;flex-direction:column;align-items:stretch;gap:6px;width:auto;margin-left:auto;padding-left:14px;padding-top:0;border-top:0;border-left:1px dashed var(--line);margin-top:0;}
  /* Cadre "en cours d'édition" (score-options dépliés) : .match-card passe
     en display:contents juste au-dessus, il ne porte donc plus lui-même de
     bordure visible — le cadre se pose ici, sur le wrap, seul élément qui
     reste un vrai bloc pendant l'édition (voir parisEditPulse en haut du
     fichier pour le choix de la pulsation). outline, pas border/padding :
     un vrai border+padding réduisait la largeur dispo pour les enfants en
     flex-wrap (score-options/stake-row ne se repliaient plus au même
     endroit qu'avant, tout se retrouvait tassé à droite) — outline se
     dessine PAR-DESSUS, hors du modèle de boîte, sans toucher à la mise en
     page existante en dessous. */
  .paris-matches-list-row .paris-predict-wrap[data-match]:has(.paris-score-options:not([hidden])){
    outline:1.5px dashed color-mix(in srgb, var(--mustard) 60%, var(--line));
    outline-offset:8px;
    border-radius:var(--radius-sm);
  }
  @media (prefers-reduced-motion: reduce) {
    .paris-matches-list-row .paris-predict-wrap[data-match]:has(.paris-score-options:not([hidden])){animation:none;}
  }

  /* Onglet "Paris en cours" (tous tournois) : cadran fixe (betRailHtml,
     public/paris.js). Largeur constante, séparée par un filet pointillé — ni
     la carte ni le cadran ne changent de largeur entre lecture et édition
     (voir .paris-bet-cadran plus haut), à la différence de l'ancien
     formulaire qui remplaçait toute la ligne. */
  .paris-matches-list-row .paris-bet-rail{flex:0 0 240px;margin-left:auto;padding-left:14px;border-left:1px dashed var(--line);}
  .paris-matches-list-row .paris-bet-rail-row{justify-content:space-between;}
}

/* Section "Matchs terminés" : accordéon générique (components.css), replié
   par défaut — la vue liste sert à repérer le PROCHAIN match, les matchs
   déjà commencés n'ont plus leur place au milieu de ceux à venir. */
.paris-matches-list-past{margin-top:14px;}
.paris-matches-list-past .accordion-body-pad{padding:0;}
.paris-matches-list-past .paris-matches-list{padding:4px 14px 8px;}

.paris-predict-wrap{display:flex;flex-direction:column;gap:10px;}
.paris-predict{display:flex;flex-direction:column;gap:12px;padding-top:10px;border-top:1px solid var(--line);margin-top:6px;}
/* Match déjà pronostiqué (non verrouillé) : plus de ligne de texte ni de
   seconde carte — juste la mise et Modifier, collés sous la carte déjà
   affichée au-dessus (voir matchCardHtml). Pas de séparateur ici : cette
   ligne doit lire comme la suite de la carte, pas comme un nouveau bloc. */
.paris-predict:has(.paris-predict-summary){padding-top:0;border-top:0;margin-top:-4px;}
.paris-predict-summary{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;}
/* Mise + gain potentiel avec pictogrammes (pari déjà posé) — inspiré du
   document "Arborescence Horizontale" reçu le 19/08/2026 : le gain se
   distingue par une pastille teintée sage, la mise reste en texte neutre. */
.paris-predict-stats{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.paris-predict-stat{display:inline-flex;align-items:center;gap:6px;font-family:inherit;font-size:13px;font-weight:700;color:var(--ink-soft);}
.paris-predict-stat svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;}
.paris-predict-stat--gain{color:var(--sage-2);background:var(--sage-badge);padding:4px 11px;border-radius:var(--pill);border:1px solid color-mix(in srgb, var(--sage) 25%, transparent);}
/* Mise : même gabarit de badge que le gain (--gain juste au-dessus), teinte
   neutre plutôt que sage — la mise n'est ni un gain ni une perte, juste une
   donnée. */
.paris-predict-stat--stake{color:var(--ink-soft);background:var(--card-2);padding:4px 11px;border-radius:var(--pill);border:1px solid var(--line);}
.paris-predict-summary-actions{display:flex;align-items:center;gap:8px;flex:none;}
/* Valider/Annuler LA MODIFICATION à la même taille — sans ça Valider (texte
   plus long, ex-.btn-full) écrasait visuellement Annuler à côté. */
.paris-predict-actions{display:flex;align-items:stretch;gap:10px;}
.paris-predict-actions .btn{flex:1;}
.paris-predict-locked-badge{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:800;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.03em;}
.paris-predict-locked-badge svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
/* Score et mise sur la même ligne — même mécanisme partout où un pari se
   pose ou se modifie (matchPredictForm dans le tableau ET betScoreZoneHtml/
   betRailHtml dans "Paris en cours") : le stepper occupe l'espace restant à
   droite des 3
   scores, JAMAIS de retour à la ligne — inspiré du document "Arborescence
   Horizontale" reçu le 19/08/2026. flex-wrap:nowrap est inconditionnel (pas
   seulement sous un certain viewport) car les colonnes du tableau restent
   étroites (~240px, voir .sx-bracket-round) même sur un grand écran : c'est
   la largeur de la CARTE qui compte, pas celle de la fenêtre.
   Le bandeau de mise "coup d'un coup" (#oneshotStakeBar) garde lui sa propre
   mise en page, pleine largeur (.paris-stake-input-row plus bas) : il n'a
   pas de 3 scores à côté de son stepper, pas besoin de compacter. */
.paris-score-options{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:nowrap;}
.paris-score-buttons{display:flex;gap:4px;flex:none;}
.paris-score-buttons .btn{padding:0 8px;font-size:12px;min-height:30px;}
/* flex-basis (pas min-width) : le stepper VISE au moins la moitié de la
   ligne, mais peut encore céder du terrain si la colonne est trop étroite
   pour tenir les 3 scores + cette moitié sans déborder (voir le cran de
   compaction mobile plus bas, qui reste le vrai filet de sécurité). */
.paris-score-options .paris-stake-control{flex:1 1 50%;justify-content:flex-end;min-width:0;}
.paris-stake-row{display:flex;flex-direction:column;align-items:flex-start;gap:12px;padding-top:6px;width:100%;}
.paris-available{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--ink-soft);white-space:nowrap;}
.paris-available svg{width:14px;height:14px;flex:none;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
/* Gain potentiel + solde disponible sur la même ligne (matchPredictForm,
   tableau) — demande explicite d'après une capture d'écran, le solde suit
   le gain plutôt que d'occuper sa propre ligne au-dessus. Les colonnes du
   tableau restent étroites (~370-400px) aussi bien sur desktop que mobile
   (voir .sx-bracket-round) : le pari doré, verbeux par défaut, est compacté
   ICI pour tenir sur cette ligne dans le cas courant — flex-wrap reste en
   filet de sécurité pour un écran vraiment minuscule, pas le mécanisme
   principal. */
.paris-gain-solde-row{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;width:100%;}
.paris-gain-solde-row .paris-potential-gain{padding:7px 10px;font-size:12px;gap:5px;flex:none;}
.paris-gain-solde-row .paris-potential-gain svg{width:13px;height:13px;}
.paris-gain-solde-row .paris-available{font-size:12px;white-space:nowrap;}

/* Mise + bouton Valider sur la même ligne (#oneshotStakeBar) ; le solde
   disponible reste sous ce bloc. */
.paris-stake-input-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.paris-stake-control{display:flex;align-items:center;gap:8px;}

/* Stepper −/valeur/+ sur la mise (public/paris.js#stakeStepperHtml) :
   l'input[type=number] réel reste au centre (toute la logique de lecture/
   bornage existante le cible), habillé pour ressembler à un simple nombre —
   flèches natives masquées, pas de bordure propre. */
.paris-stepper{display:inline-flex;align-items:center;gap:2px;padding:3px;border-radius:var(--pill);background:var(--bg);border:1px solid var(--line);}
.paris-stepper-btn{width:32px;height:32px;flex:none;border:0;border-radius:50%;background:var(--card);color:var(--accent, var(--blue));font-size:17px;font-weight:800;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:var(--shadow-subtle);transition:transform .15s ease,box-shadow .15s ease;}
.paris-stepper-btn:hover{transform:translateY(-1px);box-shadow:var(--shadow-hover);}
.paris-stepper-btn:active{transform:translateY(0) scale(.95);box-shadow:var(--shadow-subtle);}
.paris-stepper-value{display:flex;align-items:baseline;gap:4px;min-width:82px;justify-content:center;}
.paris-stepper-value input{width:44px;border:0;background:none;padding:0;text-align:right;font:inherit;font-size:13px;font-weight:800;color:var(--ink);-moz-appearance:textfield;}
.paris-stepper-value input::-webkit-inner-spin-button,.paris-stepper-value input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0;}
.paris-stepper-value input:focus{outline:none;}
.paris-stepper-value small{font-size:12px;font-weight:800;color:var(--ink-soft);}

/* Stepper compact, uniquement niché dans .paris-score-options (tableau et
   "Paris en cours") : ces colonnes n'ont jamais la place pour un stepper
   pleine taille à côté de 3 boutons de score. Celui du bandeau "coup d'un
   coup" (.paris-stake-input-row) garde sa taille normale ci-dessus. */
.paris-score-options .paris-stepper{padding:2px;gap:1px;width:100%;justify-content:space-between;}
.paris-score-options .paris-stepper-btn{width:26px;height:26px;font-size:14px;flex:none;}
.paris-score-options .paris-stepper-value{min-width:54px;gap:2px;flex:1;}
.paris-score-options .paris-stepper-value input{width:30px;font-size:12px;}
.paris-score-options .paris-stepper-value small{font-size:12px;}

/* Sur téléphone (vérifié sur appareil réel), une mise à 3 chiffres (jusqu'à
   500 MZK) serrait encore les 3 scores contre le stepper — un cran de
   compaction supplémentaire, au-delà de celui déjà permanent ci-dessus. */
@media (max-width:480px){
  .paris-score-options{gap:6px;}
  .paris-score-buttons{gap:3px;}
  .paris-score-buttons .btn{padding:0 7px;font-size:12px;min-height:29px;}
  .paris-score-options .paris-stepper{padding:1px;gap:1px;}
  .paris-score-options .paris-stepper-btn{width:25px;height:25px;font-size:13.5px;}
  .paris-score-options .paris-stepper-value{min-width:50px;gap:2px;}
  .paris-score-options .paris-stepper-value input{width:26px;font-size:12px;}
  .paris-score-options .paris-stepper-value small{font-size:12px;}
}

/* Gain potentiel : mis en avant dans un encart doré plutôt qu'en simple
   ligne de texte, pour rester visible juste avant le bouton Valider. */
.paris-potential-gain{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink);padding:10px 14px;border-radius:var(--radius-sm);background:var(--mustard-badge);border:1px solid color-mix(in srgb, var(--mustard) 45%, transparent);}
.paris-potential-gain strong{color:var(--mustard-2);font-weight:800;}
.paris-potential-gain svg{width:16px;height:16px;flex:none;stroke:var(--mustard-2);fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;}
.paris-potential-gain:empty{display:none;padding:0;border:0;}

/* Score choisi (3-0/3-1/3-2, pari classique comme oneshot) : sans cette règle,
   la classe .selected posée par public/paris.js n'avait aucun effet visuel —
   impossible de voir quel score on avait cliqué. */
.paris-score-options .btn-secondary.selected{background:var(--accent-solid, var(--blue-solid));color:#fff;border-color:var(--accent-solid, var(--blue-solid));}

/* Sélection du vainqueur par clic direct sur le joueur (carte match) : plus de
   boutons "Joueur 1"/"Joueur 2" séparés, voir public/paris.js matchCardHtml. */
.match-card .side{border-radius:var(--radius-sm);padding:6px;transition:border-color .15s ease,background-color .15s ease;border:1.5px solid transparent;}
.match-card .side[role="button"]{cursor:pointer;}
.match-card .side[role="button"]:hover{background:var(--bg);}
.match-card .side[role="button"]:focus-visible{outline:2px solid var(--accent, var(--blue));outline-offset:1px;}
.match-card .side.selected .side-highlight{border-color:var(--accent, var(--blue));background:var(--accent-soft, var(--blue-badge));}
/* Vainqueur pronostiqué (match déjà validé, lecture seule) ET vainqueur RÉEL
   (match déjà joué) : même sauge pour les deux — distincte de .selected
   (couleur d'accent de la page — bleu en pro, terracotta en amateur, voir
   --accent) qui sert à la sélection EN COURS côté formulaire. Les deux états
   ne s'affichent jamais en même temps sur une même carte (voir
   matchCardHtml#sideClass : bascule de l'un à l'autre selon que le résultat
   est connu ou non, jamais les deux), donc rien à distinguer entre eux.
   Le pavé (bordure+fond) porte sur .side-highlight (photo+nom+cote), PAS sur
   .side entier : .side ne reste qu'une zone de clic neutre autour du pavé. */
.match-card .side-highlight{border-radius:var(--radius-sm);padding:4px 8px;border:1.5px solid transparent;transition:border-color .15s ease,background-color .15s ease;}
/* Vainqueur PRONOSTIQUÉ (pas encore joué) : pavé bleu plein, comme dans la
   liste (voir plus haut) — le vert reste réservé au vrai vainqueur. Nom, cote
   et club repassent en blanc pour rester lisibles sur ce fond. */
.match-card .side.is-predicted-winner .side-highlight{border-color:transparent;background:var(--blue-solid);}
.match-card .side.is-predicted-winner .side-name,
.match-card .side.is-predicted-winner .paris-odds{color:#fff;}
.match-card .side.is-predicted-winner .paris-side-club{color:#fff;opacity:.85;}
.match-card .side.is-real-winner .side-highlight{border-color:var(--sage);background:var(--sage-badge);}
.match-card .side.is-real-winner .side-name{color:var(--sage-2);}
.match-card .side .av-wrap{position:relative;display:inline-flex;flex:none;}
.match-card .side .av{overflow:hidden;}
.match-card .side .av img{width:100%;height:100%;border-radius:50%;object-fit:cover;}
/* Tête de série : badge sur le coin de l'avatar (convention des tableaux de
   tournoi) — remplace l'ancien "(N)" collé au nom, qui finissait sur la 2e
   ligne dès que le nom passait à la ligne, donc invisible au premier regard. */
.match-card .side .paris-seed-badge{position:absolute;top:-4px;left:-4px;min-width:16px;height:16px;padding:0 3px;border-radius:50%;background:var(--card-2);color:var(--ink);font-size:12px;font-weight:800;display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 1.5px var(--line-2),0 0 0 3px var(--card-pop);}
/* Drapeau accroché au coin bas-droit de l'avatar (même principe que
   .paris-seed-badge, coin haut-gauche) — DANS .av-wrap plutôt qu'à côté
   (voir playerSideHtml), pour rester positionné pareil quelle que soit la
   disposition (colonne au tableau, ligne dense en vue liste) sans media
   query dédiée par gabarit. */
.match-card .side .flag{position:absolute;bottom:-3px;right:-3px;width:14px;height:10px;object-fit:cover;border-radius:2px;box-shadow:0 0 0 2px var(--card-pop);}
.match-card .side .side-name{display:flex;justify-content:center;gap:.3em;max-width:100%;min-width:0;overflow:hidden;white-space:nowrap;}
.match-card .side .paris-odds{display:block;font-family:inherit;font-size:12px;font-weight:600;color:var(--ink-soft);margin-top:2px;}
/* Club du joueur (circuit amateur, playerSideHtml) : sous le nom, discret. */
.match-card .side .paris-side-club{display:block;font-size:12px;font-weight:600;color:var(--ink-muted);line-height:1.2;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* Case "bye" (playerSideHtml, public/paris.js) : le mot BYE en gris, atténué
   par rapport au joueur réel en face — la silhouette de repli reste la même
   que "À déterminer" (cohérence visuelle), seul le texte se distingue. */
.match-card .side-name.paris-bye-slot{color:var(--ink-soft);font-weight:700;letter-spacing:.04em;}
/* Score pronostiqué : un vrai badge qui attire l'œil au centre de la carte,
   pas une pastille discrète — plus grand, fond contrasté, bordure nette. */
.match-card .vs.paris-score-inline{font-family:inherit;font-weight:700;font-size:13px;padding:0 10px;border-radius:var(--pill);background:var(--sage-badge);color:var(--sage);}
.match-card .vs.paris-score-inline.is-final{background:var(--sage-badge);color:var(--sage);}
/* Conteneur du score réel + (une fois le match réglé) du score parié en
   dessous — .vs garde sa taille/couleur d'origine, seul l'empilement change. */
.match-card-score{display:flex;flex-direction:column;align-items:center;gap:5px;flex:none;}
.match-card-predicted{display:flex;flex-direction:column;align-items:center;gap:4px;}
/* Le score parié lui-même (voir matchResultLineHtml) reprend .paris-bet-score-badge
   (badge + picto ticket, déjà utilisé pour "Paris en cours"/"Paris passés") —
   juste l'épaisseur du barré à ajuster ici, propre au contexte compact de la carte. */
.match-card-predicted .paris-bet-score-badge s{text-decoration-thickness:1.5px;}
/* Aperçu avant règlement admin (voir resolvedBetOutcome/matchResultLineHtml) :
   même badge, juste atténué — le montant peut encore bouger si l'admin
   annule le match au lieu de le régler normalement. */
.match-card-predicted.is-pending-settlement{opacity:.8;}

@media (max-width:760px){
  .paris-bet-head,.paris-bet-meta{flex-direction:column;align-items:flex-start;}
}

/* ==========================================================================
   Refonte du tableau pro (29/09/2026) — voir docs/paris.md, « Le tableau ».
   Une couleur = un sens : accent de la page (bleu en pro, terracotta en
   amateur) = mes choix (joueur sélectionné, pari posé, score pronostiqué,
   animation de validation) ; moutarde = « à parier » ; sauge = gagné ;
   danger = perdu ; tout le reste (cotes, têtes de série, « VS », « À venir »)
   est neutre. Une seule police (celle de la date et de l'heure).
   ========================================================================== */

/* Nom sur UNE ligne : le milieu (2e prénom) se tronque d'abord, puis le 1er
   prénom, jamais le nom de famille (dernier mot). Les poids de rétrécissement
   (1 / 1 000 / 100 000 000) rendent l'ordre strict : un poids plus grand
   absorbe TOUT le manque de place avant qu'un plus petit ne bouge. */
.match-card .side .side-highlight,.match-card .side .side-text{min-width:0;max-width:100%;}
.side-name .nm-1{flex:0 1000 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;}
.side-name .nm-2{flex:0 100000000 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;}
.side-name .nm-last{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;}

/* Score toujours centré : trois colonnes, celle du milieu à la largeur de son contenu. */
.sx-bracket-match .match-card{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:8px;}

/* Badges de score, de montant, de mise et de gain : même hauteur, même corps. */
.match-card .vs.paris-score-inline,
.match-card-predicted .badge,
.match-card-predicted .paris-bet-net,
.sx-bracket-match .paris-predict-stat--stake,
.sx-bracket-match .paris-predict-stat--gain{display:inline-flex;align-items:center;justify-content:center;gap:5px;min-height:26px;padding:0 10px;border-radius:var(--pill);font-family:inherit;font-size:13px;font-weight:700;line-height:1;letter-spacing:0;white-space:nowrap;border:0;}
.match-card .vs.paris-score-inline svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;flex:none;}
.match-card .vs.paris-score-inline.is-predicted{background:var(--accent-soft, var(--blue-badge));color:var(--accent, var(--blue));}
.match-card-predicted .paris-bet-net.is-positive{background:var(--sage-badge);color:var(--sage-2);}
.match-card-predicted .paris-bet-net.is-negative{background:var(--danger-badge);color:var(--danger);}
/* Mise et gain : deux pastilles neutres, seul le picto porte la couleur d'accent. */
.sx-bracket-match .paris-predict-stat--stake,.sx-bracket-match .paris-predict-stat--gain{background:var(--card-2);color:var(--ink);}
.sx-bracket-match .paris-predict-stat svg{color:var(--accent, var(--blue));}
/* « Sans pronostic » : un ticket barré, le libellé reste pour les lecteurs d'écran. */
.paris-no-bet-badge{padding:0 9px;min-height:26px;}
.paris-no-bet-badge svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;}

/* Mes pronostics qui avancent dans le tableau : le joueur choisi apparaît en
   pointillé dans la case suivante, avec la mention « ton choix ». */
.match-card .side-highlight.is-ghost{border:1.5px dashed var(--accent, var(--blue));background:transparent;}
.match-card .side.is-ghost-side .side-name{font-style:italic;color:var(--ink-soft);}
.match-card .side.is-ghost-side .av{opacity:.75;}
.paris-ghost-label{display:block;margin-top:2px;font-size:12px;font-weight:600;color:var(--accent, var(--blue));}
/* Survol d'un joueur : son nom s'allume dans tous les tours. */
.match-card .side.is-hl .side-highlight{border-color:var(--accent, var(--blue));background:var(--accent-soft, var(--blue-badge));}

/* Pastille « ferme dans 1 h 24 » à côté de l'horaire ; moutarde sous 2 h. */
.paris-lock-chip{display:inline-flex;align-items:center;min-height:22px;padding:0 9px;border-radius:var(--pill);background:var(--card-2);color:var(--ink-soft);font-size:12px;font-weight:600;white-space:nowrap;}
.paris-lock-chip.is-urgent{background:var(--mustard-badge);color:var(--mustard-2);}
.paris-matches-list-info .paris-lock-chip{margin-top:4px;}

/* Barre de tours collée sous l'en-tête du site (hors de .sx-bracket : un
   overflow-x:auto ferait coller un élément intérieur dans SA boîte, pas dans la page). */
.paris-round-nav{position:sticky;top:90px;z-index:12;display:flex;gap:6px;flex-wrap:wrap;align-items:center;padding:8px 10px;margin:0 0 12px;background:color-mix(in srgb, var(--card) 92%, transparent);backdrop-filter:blur(10px);border:1.5px solid var(--line);border-radius:var(--radius-sm);}
.paris-round-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1.5px solid var(--line);border-radius:var(--pill);background:var(--card);color:var(--ink);font:inherit;font-size:13px;font-weight:700;cursor:pointer;}
.paris-round-chip small{font-size:12px;font-weight:600;color:var(--ink-soft);}
.paris-round-chip:hover{border-color:var(--line-2);}
.paris-round-chip.active{background:var(--ink);border-color:var(--ink);color:var(--bg);}
.paris-round-chip.active small{color:inherit;opacity:.75;}
.paris-round-chip:focus-visible{outline:2px solid var(--accent, var(--blue));outline-offset:2px;}
@media (max-width:760px){
  .paris-round-nav{top:74px;}
  .sx-bracket{scroll-snap-type:x mandatory;}
  .sx-bracket-round{scroll-snap-align:start;}
  .sx-bracket.is-list-mode{scroll-snap-type:none;}
}

/* Onglets Hommes/Femmes compacts ; bascule Tableau/Liste (avec libellés) à
   droite de la barre des tours. */
#bracketGenderTabs{width:auto;margin-bottom:0;}
#bracketGenderTabs .sx-tab{flex:none;padding-inline:20px;}
.paris-round-chips{display:flex;flex-wrap:wrap;gap:6px;flex:1 1 auto;min-width:0;}
#bracketViewToggle{margin-left:auto;}
#bracketViewToggle .paris-view-btn{width:auto;padding:0 12px;gap:6px;font:inherit;font-size:13px;font-weight:700;}
@media (max-width:480px){#bracketViewToggle .paris-view-label{display:none;}#bracketViewToggle .paris-view-btn{padding:0 10px;}}

/* Résultat qui vient d'arriver (relecture en direct) : halo d'accent. */
@keyframes parisResultFlash{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--accent, var(--blue)) 60%, transparent);}30%{box-shadow:0 0 0 6px color-mix(in srgb, var(--accent, var(--blue)) 25%, transparent);}100%{box-shadow:0 0 0 0 transparent;}}
.paris-result-flash{animation:parisResultFlash 1.6s ease;}
@media (prefers-reduced-motion:reduce){.paris-focus-pulse,.paris-bet-success-flash,.paris-result-flash{animation:none;}}

/* Carte du tableau : moins de marges intérieures pour laisser la place aux noms
   (le nom de famille ne doit jamais être tronqué, voir .side-name). */
.sx-bracket-match .match-card{padding-right:8px;gap:6px;}
.sx-bracket-match .match-card .side{padding:2px;}
.sx-bracket-match .match-card .side-highlight{padding:4px 5px;}
/* Gain potentiel du formulaire : encart neutre, seul le picto tirelire porte l'accent. */
.paris-potential-gain{background:var(--card-2);border-color:var(--line);}
.paris-potential-gain strong{color:var(--ink);}
.paris-potential-gain svg{stroke:var(--accent, var(--blue));}

/* ==========================================================================
   Répartition des parieurs (pastille + détail au toucher) — voir
   parieursChipHtml/parieursPanelHtml dans paris-carte-match.js. La pastille
   (picto + nombre) se range sur la ligne d'infos du match, à droite : méta de
   la case au tableau, colonne d'horaire en vue liste. Le détail, lui, se
   déplie sous la carte.
   ========================================================================== */
.paris-parieurs-chip{display:inline-flex;align-items:center;gap:5px;min-height:22px;padding:0 9px 0 7px;border:0;border-radius:var(--pill);background:var(--card-2);color:var(--ink-soft);font-family:inherit;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer;margin-left:auto;transition:background-color .15s ease,color .15s ease;}
.paris-parieurs-chip svg{width:13px;height:13px;flex:none;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;}
.paris-parieurs-chip:hover{color:var(--ink);}
.paris-parieurs-chip:focus-visible{outline:2px solid var(--accent, var(--blue));outline-offset:2px;}
.paris-parieurs-chip[aria-expanded="true"]{background:var(--blue-badge);color:var(--blue);}
.paris-parieurs-chip.is-vide{opacity:.7;}
/* Même ligne que l'horaire sur mobile : la règle « méta en colonne » (≤760px)
   ne s'applique pas quand la pastille est là, sinon elle passerait seule en
   dessous. */
.paris-bet-meta:has(.paris-parieurs-chip){flex-direction:row;align-items:center;flex-wrap:wrap;gap:6px 12px;}
.paris-matches-list-info{flex-wrap:wrap;}
.paris-matches-list-info .paris-parieurs-chip{margin-left:0;margin-top:4px;}
.paris-parieurs-panel{display:flex;flex-direction:column;gap:10px;padding:10px 12px;background:var(--card);border:1.5px solid var(--line);border-radius:var(--radius-sm);animation:parisRailReveal .16s ease;}
.paris-parieurs-ligne{display:grid;grid-template-columns:minmax(64px,auto) minmax(0,1fr) auto;align-items:center;gap:10px;font-size:12px;}
.paris-parieurs-nom{min-width:0;max-width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:700;color:var(--ink);}
.paris-parieurs-nom i{display:block;font-style:normal;font-weight:700;color:var(--blue);}
.paris-parieurs-piste{height:10px;border-radius:var(--pill);background:var(--card-2);overflow:hidden;}
.paris-parieurs-piste span{display:block;height:100%;border-radius:var(--pill);background:var(--ink-muted);}
.paris-parieurs-ligne.is-mine .paris-parieurs-piste span{background:var(--blue-solid);}
.paris-parieurs-val{color:var(--ink-soft);white-space:nowrap;font-variant-numeric:tabular-nums;}
.paris-parieurs-val b{color:var(--ink);font-weight:800;}
.paris-parieurs-vide{margin:0;font-size:12px;color:var(--ink-muted);}
/* Vue liste, grand écran : le wrap est une rangée (carte + cadran) — le détail
   passe à la ligne, pleine largeur, sous les deux. */
@media (min-width: 860px) {
  .paris-matches-list-row .paris-predict-wrap .paris-parieurs-panel{flex:0 0 100%;order:9;}
}
@media (prefers-reduced-motion: reduce) {
  .paris-parieurs-panel{animation:none;}
}

/* Transition entre pages depuis le hub /paris (voir paris-hub.css) : fondu au
   lieu du flash, l'en-tête reste en place. Ignoré sans prise en charge. */
@view-transition{navigation:auto;}
.app-topbar{view-transition-name:entete-app;}
@media(prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important;}}

/* ==========================================================================
   Carte amateur : pas de photo, donc pas d'avatar (playerSideHtml, option
   sansAvatar ; classe .is-sans-avatar posée par matchCardHtml). La tête de série
   devient un petit badge devant le nom ; le nom s'abrège en « Nom P. »
   (nomAmateurCourt) et la cote passe dessous, en plus petit : chaque joueur garde
   sa moitié de carte, mais le nom y tient en entier.
   ========================================================================== */
.match-card .side .paris-seed-badge--ligne{position:static;flex:none;align-self:center;box-shadow:none;border:1.5px solid var(--line-2);}
.match-card .side-highlight.is-sans-avatar{max-width:100%;}
