Les scores à la mi-temps de la pro D2

Les scores à la mi-temps de la Pro D2

Retrouvez les scores à la mi-temps de la Pro D2 de cette journée ( attention certains matchs sont peut encore en cours ou ont commencé en différé )

Suivez la fin des matchs à la radio et/ou score live du multiplex Pro D2, pour cela cliquez sur le club ou le match de votre choix pour suivre la rencontre en direct/live, voir ou revoir les actions du match, les joueurs qui ont marqué, ainsi que la composition des équipes. Vous pouvez également conserver cette page afin de suivre l’évolution des scores de chaque équipe.







.comp-wrap { /* Brand tokens */ –navy: #003f7c; –navy-dark: #002a5d; –red: #d62242; –red-dark: #b91c3a; –gold: #f59e0b; –gray: #676767; –gray-light: #e9ecef; –gray-lighter: #f8f9fa; –white: #ffffff; –text: #0f141a; –radius: 8px; –radius-lg: 12px; –radius-pill: 9999px; –shadow-sm: 0 1px 3px rgba(0,0,0,.05); –shadow-md: 0 4px 12px rgba(0, 63, 124, 0.12); –shadow-lg: 0 10px 30px rgba(0, 42, 93, 0.18); –shadow-red: 0 4px 16px rgba(214, 34, 66, 0.22); –font-display: ‘Barlow Semi Condensed’, ‘Poppins’, -apple-system, sans-serif; –font-body: ‘Poppins’, -apple-system, BlinkMacSystemFont, ‘Segoe UI’, sans-serif; –ease: cubic-bezier(.4, 0, .2, 1); }/* Scoped reset */ .comp-wrap *, .comp-wrap *::before, .comp-wrap *::after { box-sizing: border-box; }.comp-wrap { max-width: 1240px; margin: 0 auto; padding: 20px; font-family: var(–font-body); background: var(–gray-lighter); color: var(–text); }/* ============================================== HEADER COMPÉTITION ============================================== */ .comp-title { position: relative; background: linear-gradient(135deg, var(–navy) 0%, var(–navy-dark) 100%); color: white; padding: 18px 24px 16px; border-radius: var(–radius-lg); margin-bottom: 18px; overflow: hidden; box-shadow: var(–shadow-lg); } .comp-title::before { content:  »; position: absolute; inset: 0; background: repeating-linear-gradient( -45deg, transparent 0 20px, rgba(255,255,255,0.04) 20px 21px ); pointer-events: none; } .comp-title::after { content:  »; position: absolute; right: -60px; top: 50%; transform: translateY(-50%); width: 220px; height: 220px; background: radial-gradient(circle, var(–red) 0%, transparent 65%); opacity: 0.28; pointer-events: none; } .comp-title-inner { position: relative; } .comp-title h1 { font-family: var(–font-display); font-size: 1.55rem; line-height: 1.05; margin: 0; font-weight: 700; color: white; text-transform: uppercase; letter-spacing: 0.02em; } .comp-kicker { display: inline-block; font-family: var(–font-body); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.14em; color: rgba(255,255,255,0.7); margin-bottom: 5px; } .comp-meta { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; } .comp-live-count { display: inline-flex; align-items: center; gap: 7px; background: var(–red); padding: 5px 12px; border-radius: var(–radius-pill); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; box-shadow: 0 2px 10px rgba(214,34,66,0.4); } .comp-live-count::before { content:  »; width: 7px; height: 7px; background: white; border-radius: 50%; animation: blink 1.5s infinite; } .comp-year-pill { display: inline-block; padding: 4px 10px; background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.18); border-radius: var(–radius-pill); font-size: 11px; font-weight: 600; letter-spacing: 0.06em; } @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} }/* ============================================== SECTION LABELS ============================================== */ .comp-section-label { font-family: var(–font-display); margin: 22px 0 10px; font-size: 0.85rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(–gray); display: flex; align-items: center; gap: 12px; } .comp-section-label::before { content:  »; display: inline-block; width: 3px; height: 16px; background: var(–red); border-radius: var(–radius-pill); } .comp-section-label::after { content:  »; flex: 1; height: 1px; background: linear-gradient(to right, var(–gray-light), transparent); } .comp-section-label:first-of-type { margin-top: 14px; }/* ============================================== POULE SELECTOR (sticky) ============================================== */ .poule-selector { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; padding: 10px 12px; background: rgba(255,255,255,0.95); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border: 1px solid var(–gray-light); border-radius: var(–radius); box-shadow: var(–shadow-sm); position: sticky; top: var(–comp-sticky-top, 0px); z-index: 20; } .poule-btn { padding: 8px 16px; background: white; border: 1.5px solid var(–gray-light); border-radius: var(–radius-pill); font-family: var(–font-display); font-size: 13px; font-weight: 600; letter-spacing: 0.05em; color: var(–navy); cursor: pointer; transition: all 0.2s var(–ease); text-transform: uppercase; font-family: inherit; } .poule-btn { font-family: var(–font-display); } .poule-btn:hover { border-color: var(–red); color: var(–red); transform: translateY(-1px); } .poule-btn.active { background: var(–navy); color: white; border-color: var(–navy); box-shadow: 0 2px 10px rgba(0,63,124,0.3); }/* ============================================== PHASE CONTAINER ============================================== */ .phase-container { margin-bottom: 28px; } .phase-container.group { display: none; } .phase-container.group.active { display: block; animation: fadeIn 0.3s var(–ease); } @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }/* Phase header (standalone card) */ .phase-header { position: relative; color: white; padding: 12px 20px; border-radius: var(–radius-lg); margin-bottom: 10px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; overflow: hidden; box-shadow: 0 4px 16px rgba(0, 42, 93, 0.1); } .phase-container.group .phase-header { background: linear-gradient(135deg, var(–navy) 0%, var(–navy-dark) 100%); } .phase-container.knockout .phase-header { background: linear-gradient(135deg, var(–red) 0%, var(–red-dark) 100%); } .phase-header::before { content:  »; position: absolute; inset: 0; background: repeating-linear-gradient( -45deg, transparent 0 18px, rgba(255,255,255,0.04) 18px 19px ); pointer-events: none; } .phase-title { position: relative; font-family: var(–font-display); font-size: 1rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; } .phase-badge { position: relative; margin-left: auto; display: inline-flex; align-items: center; gap: 6px; background: rgba(0,0,0,0.25); padding: 4px 10px; border-radius: var(–radius-pill); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; } .phase-badge.live { background: rgba(255,255,255,0.2); } .phase-badge.live::before { content:  »; width: 6px; height: 6px; background: white; border-radius: 50%; animation: blink 1.5s infinite; }/* Phase subtabs (journées) — standalone card */ .phase-subtabs { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 14px; background: white; border: 1px solid var(–gray-light); border-radius: var(–radius); margin-bottom: 10px; box-shadow: var(–shadow-sm); } .phase-subtab { position: relative; padding: 6px 13px; background: white; border: 1.5px solid var(–gray-light); border-radius: var(–radius-pill); font-family: var(–font-body); font-size: 12px; font-weight: 600; color: var(–navy); cursor: pointer; transition: all 0.2s var(–ease); white-space: nowrap; } .phase-subtab:hover { border-color: var(–navy); transform: translateY(-1px); } .phase-subtab.active { background: var(–navy); color: white; border-color: var(–navy); } .phase-subtab.has-live::after { content:  »; position: absolute; top: -3px; right: -3px; width: 9px; height: 9px; background: var(–red); border: 2px solid white; border-radius: 50%; animation: blink 1.5s infinite; } .phase-subtab.current .dot-current { display: inline-block; width: 5px; height: 5px; background: var(–red); border-radius: 50%; margin-right: 5px; vertical-align: middle; } .phase-subtab.active .dot-current { background: white; }/* Phase content — standalone card */ .phase-content { background: white; border: 1px solid var(–gray-light); border-radius: var(–radius); padding: 16px; box-shadow: var(–shadow-sm); }/* Phase body: sidebar (standings) + main (matches) */ .phase-body { display: block; } .phase-main { min-width: 0; } .phase-sidebar { min-width: 0; }/* Phases finales : grille 2 colonnes sur desktop */ .knockouts-grid { display: flex; flex-direction: column; gap: 16px; } .knockouts-grid .phase-container { margin-bottom: 0; min-width: 0; } .phase-journee-panel { display: none; } .phase-journee-panel.active { display: block; animation: fadeIn 0.2s var(–ease); }/* ============================================== KNOCKOUT — onglets Aller/Retour + rangées compactes ============================================== */ .ko-leg-tabs { display: flex; gap: 8px; margin-bottom: 12px; padding: 4px; background: var(–gray-lighter); border-radius: var(–radius); } .ko-leg-tab { flex: 1; position: relative; padding: 8px 14px; background: transparent; border: none; border-radius: 6px; font-family: var(–font-display); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(–gray); cursor: pointer; transition: all 0.18s var(–ease); display: inline-flex; align-items: center; justify-content: center; gap: 6px; } .ko-leg-tab:hover { color: var(–navy); } .ko-leg-tab.active { background: white; color: var(–navy-dark); box-shadow: var(–shadow-sm); } .ko-leg-tab .ko-leg-count { display: inline-block; padding: 1px 7px; background: var(–gray-light); color: var(–navy); border-radius: var(–radius-pill); font-size: 10px; font-weight: 700; line-height: 1.4; } .ko-leg-tab.active .ko-leg-count { background: var(–navy); color: white; } .ko-leg-tab .ko-leg-live { display: inline-block; width: 7px; height: 7px; background: var(–red); border-radius: 50%; animation: blink 1.5s infinite; } .ko-leg-panel { display: none; } .ko-leg-panel.active { display: block; animation: fadeIn 0.18s var(–ease); }/* Rangées compactes — une ligne par match */ .match-compact { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto minmax(0, 1fr) 22px; column-gap: 10px; align-items: center; padding: 6px 12px; background: white; border: 1px solid var(–gray-light); border-radius: 6px; margin-bottom: 4px; text-decoration: none; color: var(–navy); font-size: 12.5px; line-height: 1.3; position: relative; transition: border-color 0.15s var(–ease), background 0.15s var(–ease), transform 0.15s var(–ease); } .match-compact:last-child { margin-bottom: 0; } .match-compact:hover { border-color: var(–red); background: white; transform: translateX(2px); text-decoration: none; color: var(–navy); } .match-compact.live { border-color: rgba(214,34,66,0.4); background: rgba(214,34,66,0.04); } .match-compact.live::before { content:  »; position: absolute; left: -1px; top: -1px; bottom: -1px; width: 3px; background: var(–red); border-radius: 6px 0 0 6px; } .match-compact.finished { background: var(–gray-lighter); } .match-compact .team-logo-mini { width: 22px; height: 22px; flex-shrink: 0; } .match-compact .team-logo-mini img { width: 100%; height: 100%; object-fit: contain; display: block; } .match-compact .team-name-mini { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } .match-compact .team-name-mini.visitor { text-align: right; } .match-compact.finished.local-loser .team-name-mini.local, .match-compact.finished.visitor-loser .team-name-mini.visitor { color: var(–gray); font-weight: 500; } .match-compact.finished.local-winner .team-name-mini.local, .match-compact.finished.visitor-winner .team-name-mini.visitor { color: var(–navy-dark); font-weight: 700; } .match-compact .match-mid { display: flex; align-items: center; justify-content: center; min-width: 90px; } .match-compact .score-pill { display: inline-flex; align-items: baseline; gap: 3px; padding: 4px 9px; background: var(–navy); color: white; border-radius: 4px; font-family: var(–font-display); font-weight: 700; font-size: 13px; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; } .match-compact .score-pill.live { background: var(–red); } .match-compact .score-pill .score-dash { font-weight: 400; opacity: 0.5; } .match-compact .live-mini { margin-left: 6px; padding: 2px 6px; background: var(–red); color: white; border-radius: var(–radius-pill); font-family: var(–font-display); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; animation: blink 1.5s infinite; } .match-compact .match-time-compact { font-size: 11.5px; color: var(–gray); font-variant-numeric: tabular-nums; white-space: nowrap; } .match-compact .match-time-compact strong { color: var(–navy); font-weight: 700; margin-left: 4px; } /* Badge « Cumul aller-retour » sur le retour quand les 2 manches sont jouées */ .match-compact .cumul-badge { position: absolute; top: -7px; right: 10px; padding: 2px 7px; background: var(–gold); color: white; border-radius: var(–radius-pill); font-family: var(–font-display); font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; box-shadow: 0 2px 4px rgba(245,158,11,0.35); pointer-events: none; white-space: nowrap; } .match-compact .cumul-badge .cumul-dash { opacity: 0.7; margin: 0 2px; } .match-compact .cumul-badge.draw { background: var(–gray); }/* ============================================== MATCH ROW ============================================== */ .match-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: 16px; padding: 14px 18px 14px 21px; background: white; border: 1.5px solid var(–gray-light); border-radius: var(–radius); margin-bottom: 8px; cursor: pointer; transition: all 0.25s var(–ease); align-items: center; text-decoration: none; color: inherit; position: relative; overflow: hidden; } .match-row:last-child { margin-bottom: 0; } .match-row:hover { border-color: var(–red); transform: translateY(-2px); box-shadow: var(–shadow-red); text-decoration: none; color: inherit; } .match-row.live { background: linear-gradient(90deg, rgba(214,34,66,0.05) 0%, transparent 60%); border-color: rgba(214,34,66,0.35); } .match-row::after { content:  »; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: transparent; transition: background 0.25s var(–ease); } .match-row.live::after { background: var(–red); } .match-row.finished::after { background: var(–navy); }/* Today pill */ .today-pill { display: inline-block; padding: 2px 7px; background: var(–gold); color: white; border-radius: var(–radius-pill); font-family: var(–font-display); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; }.team-info { display: flex; align-items: center; gap: 12px; min-width: 0; } .team-info.away { flex-direction: row-reverse; text-align: right; } .team-name { font-family: var(–font-body); font-size: 14px; font-weight: 600; color: var(–navy); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .team-info.away .team-name { color: var(–gray); } .team-logo { width: 40px; height: 40px; object-fit: contain; flex-shrink: 0; transition: transform 0.25s var(–ease); } .match-row:hover .team-logo { transform: scale(1.08); }.match-center { text-align: center; min-width: 84px; } .score-box { display: inline-flex; align-items: center; justify-content: center; gap: 4px; background: var(–navy); color: white; padding: 7px 12px; border-radius: var(–radius); font-family: var(–font-display); font-weight: 700; font-size: 1.15rem; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; min-width: 68px; } .score-box .score-dash { font-weight: 400; opacity: 0.5; margin: 0 2px; } .score-box.live { background: var(–red); animation: pulse 2s infinite; } @keyframes pulse { 0%,100% { box-shadow: 0 0 0 0 rgba(214,34,66,.4); } 50% { box-shadow: 0 0 0 10px rgba(214,34,66,0); } } .live-pill { display: block; font-family: var(–font-body); font-size: 9px; font-weight: 700; letter-spacing: 0.12em; color: var(–red); margin-top: 4px; text-transform: uppercase; } .live-pill::before { content:  »; display: inline-block; width: 5px; height: 5px; background: var(–red); border-radius: 50%; margin-right: 4px; animation: blink 1.5s infinite; vertical-align: middle; } .match-datetime { font-size: 12px; color: var(–gray); line-height: 1.3; } .match-datetime .match-date { display: block; font-weight: 500; } .match-time { display: block; font-family: var(–font-display); font-weight: 700; color: var(–navy); font-size: 1.05rem; margin-top: 3px; letter-spacing: 0.02em; font-variant-numeric: tabular-nums; }/* ============================================== STANDINGS ============================================== */ .standings-wrap { background: white; border: 1px solid var(–gray-light); border-radius: var(–radius); overflow: hidden; margin: 0 0 16px; } .comp-standings { width: 100%; border-collapse: collapse; font-size: 13px; } .comp-standings thead tr { background: var(–navy); color: white; } .comp-standings th { padding: 13px 8px; text-align: center; font-family: var(–font-display); font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; } .comp-standings th:nth-child(2) { text-align: left; padding-left: 16px; } .comp-standings td { padding: 11px 8px; text-align: center; border-bottom: 1px solid var(–gray-light); font-variant-numeric: tabular-nums; } .comp-standings td:nth-child(2) { text-align: left; padding-left: 16px; } .comp-standings tbody tr:last-child td { border-bottom: 0; } .comp-standings tbody tr { transition: background 0.15s ease; } .comp-standings tbody tr:hover { background: #fafbfd; }.rank { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; font-family: var(–font-display); font-weight: 700; font-size: 14px; color: var(–navy); background: var(–gray-lighter); border-radius: 6px; } .comp-standings tbody tr:first-child .rank { background: linear-gradient(135deg, var(–gold), #d97706); color: white; box-shadow: 0 2px 6px rgba(245,158,11,0.4); }.team-cell { display: flex; align-items: center; gap: 10px; } .team-cell img { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; } .team-cell span { font-family: var(–font-body); font-weight: 600; color: var(–navy); } .comp-standings td.pts strong { display: inline-block; background: var(–navy); color: white; padding: 4px 11px; border-radius: var(–radius); font-family: var(–font-display); font-size: 14px; font-weight: 700; min-width: 36px; font-variant-numeric: tabular-nums; }/* Forme column */ .forme-cell { display: inline-flex; gap: 3px; justify-content: center; } .forme-dot { width: 18px; height: 18px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; font-family: var(–font-display); font-size: 10px; font-weight: 700; color: white; } .forme-dot.V { background: #16a34a; } .forme-dot.N { background: var(–gray); } .forme-dot.D { background: var(–red); }/* ============================================== TEAM SEARCH ============================================== */ .team-search { position: relative; margin-bottom: 14px; } .team-search input { width: 100%; padding: 11px 38px 11px 40px; background: white; border: 1.5px solid var(–gray-light); border-radius: var(–radius); font-family: var(–font-body); font-size: 14px; color: var(–text); outline: none; transition: all 0.2s var(–ease); } .team-search input::placeholder { color: #9aa3af; font-family: var(–font-body); } .team-search input:focus { border-color: var(–navy); box-shadow: 0 0 0 3px rgba(0, 63, 124, 0.12); } .team-search .ts-icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(–gray); pointer-events: none; width: 16px; height: 16px; } .team-search .ts-clear { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 22px; height: 22px; border: 0; background: var(–gray-light); border-radius: 50%; color: var(–gray); cursor: pointer; display: none; align-items: center; justify-content: center; font-family: inherit; font-size: 14px; line-height: 1; } .team-search .ts-clear:hover { background: var(–red); color: white; } .team-search.has-value .ts-clear { display: inline-flex; } .ts-hint { display: none; margin-top: 6px; font-size: 12px; color: var(–gray); padding: 0 4px; } .team-search.has-value + .ts-hint { display: block; }/* Highlight matching rows/matches */ .comp-standings tbody tr.is-match { background: rgba(245, 158, 11, 0.12); box-shadow: inset 3px 0 0 var(–gold); } .match-row.is-dim { opacity: 0.35; filter: saturate(0.4); } .match-row.is-match { border-color: var(–gold); box-shadow: 0 2px 10px rgba(245, 158, 11, 0.2); } .poule-btn.is-match { border-color: var(–gold); color: var(–gold); } .poule-btn.is-match.active { background: var(–gold); color: white; border-color: var(–gold); box-shadow: 0 2px 10px rgba(245, 158, 11, 0.35); }/* ============================================== DAY GROUPING (inside a journée) ============================================== */ .day-group + .day-group { margin-top: 14px; } .day-header { display: flex; align-items: center; gap: 10px; margin: 4px 0 10px; font-family: var(–font-display); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; color: var(–gray); } .day-header::before { content:  »; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(–navy); } .day-header.is-today::before { background: var(–red); animation: blink 1.5s infinite; } .day-header.is-today { color: var(–red); } .day-header::after { content:  »; flex: 1; height: 1px; background: var(–gray-light); }/* ============================================== EMPTY / LOADING / ERROR ============================================== */ .empty-state { text-align: center; padding: 36px 20px; color: var(–gray); font-size: 14px; } .empty-state::before { content:  »; display: block; width: 44px; height: 44px; margin: 0 auto 12px; background: var(–gray-lighter); border-radius: 50%; background-image: radial-gradient(circle, var(–gray-light) 30%, var(–gray-lighter) 31%); }.comp-loading { padding: 8px 0; } .skel { background: linear-gradient(90deg, #eef1f5 0%, #f7f9fb 50%, #eef1f5 100%); background-size: 200% 100%; animation: shimmer 1.4s infinite; border-radius: var(–radius); } .skel-title { height: 110px; margin-bottom: 20px; border-radius: var(–radius-lg); } .skel-row { height: 66px; margin-bottom: 8px; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }.comp-error { padding: 20px; background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; border-radius: var(–radius); }/* ============================================== RESPONSIVE ============================================== */ @media (max-width: 768px) { .comp-wrap { padding: 12px; } .comp-title { padding: 14px 16px; } .comp-title h1 { font-size: 1.25rem; } .comp-kicker { font-size: 9px; } .poule-selector { padding: 8px 10px; gap: 6px; } .poule-btn { padding: 6px 12px; font-size: 12px; } .match-row { padding: 12px 12px 12px 16px; gap: 10px; } .team-info { gap: 8px; } .team-name { font-size: 13px; } .team-logo { width: 32px; height: 32px; } .match-center { min-width: 88px; } .score-box { font-size: 1rem; padding: 6px 11px; min-width: 62px; } .match-time { font-size: 0.95rem; } .match-datetime { font-size: 11px; } .comp-standings { font-size: 12px; } .comp-standings th, .comp-standings td { padding: 9px 4px; } .comp-standings th:nth-child(2), .comp-standings td:nth-child(2) { padding-left: 10px; } .rank { width: 22px; height: 22px; font-size: 12px; } .team-cell { gap: 6px; } .team-cell img { width: 18px; height: 18px; } .comp-standings td.pts strong { padding: 3px 9px; font-size: 12px; min-width: 30px; } .phase-header { padding: 10px 14px; } .phase-title { font-size: 0.95rem; } .phase-content { padding: 12px; } .phase-subtabs { padding: 10px 12px; } .phase-subtab { font-size: 11px; padding: 5px 10px; } .forme-dot { width: 16px; height: 16px; font-size: 9px; } .hide-mobile { display: none; } } @media (max-width: 480px) { .comp-standings th, .comp-standings td { padding: 7px 3px; font-size: 11px; } .team-cell span { font-size: 11px; } }/* ============================================== DESKTOP (≥1024px) — 2-column split ============================================== */ @media (min-width: 1024px) { .comp-title { padding: 24px 28px 22px; } .comp-title h1 { font-size: 1.75rem; }.phase-container.group .phase-body:has(.phase-sidebar) { display: grid; grid-template-columns: minmax(460px, 560px) 1fr; gap: 20px; align-items: start; } .phase-container.group .phase-sidebar { position: sticky; top: calc(var(–comp-sticky-top, 0px) + 80px); z-index: 5; } .phase-container.group .phase-sidebar .standings-wrap { margin: 0; max-height: calc(100vh – 110px); overflow-y: auto; scrollbar-width: thin; } /* Sidebar standings : on masque seulement PM/PE, on garde N, +/-, Forme */ .phase-container.group .phase-sidebar .comp-standings .col-hide-narrow { display: none; } .phase-container.group .phase-sidebar .comp-standings th, .phase-container.group .phase-sidebar .comp-standings td { padding: 9px 6px; } .phase-container.group .phase-sidebar .comp-standings th:nth-child(2), .phase-container.group .phase-sidebar .comp-standings td:nth-child(2) { padding-left: 12px; } .phase-container.group .phase-sidebar .team-cell span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 160px; display: inline-block; vertical-align: middle; } .phase-container.group .phase-sidebar .forme-dot { width: 16px; height: 16px; font-size: 9px; }/* More generous match row on desktop */ .match-row { padding: 16px 20px 16px 24px; } .team-logo { width: 44px; height: 44px; } .team-name { font-size: 15px; } .score-box { font-size: 1.25rem; padding: 8px 14px; min-width: 72px; } .match-time { font-size: 1.15rem; }/* Phases finales : 2 colonnes */ .knockouts-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; } /* Match rows en phase finale (single-leg) repassent compacts */ .knockouts-grid .match-row { padding: 14px 16px 14px 18px; } .knockouts-grid .team-logo { width: 36px; height: 36px; } .knockouts-grid .team-name { font-size: 13.5px; } .knockouts-grid .score-box { font-size: 1.05rem; padding: 6px 11px; min-width: 62px; } .knockouts-grid .match-time { font-size: 1rem; } /* Finale plein écran (D) */ .knockouts-grid .phase-container.is-final { grid-column: 1 / -1; } .knockouts-grid .phase-container.is-final .phase-title { font-size: 1.4rem; letter-spacing: 0.06em; } .knockouts-grid .phase-container.is-final .match-compact { padding: 12px 18px; font-size: 14.5px; } .knockouts-grid .phase-container.is-final .match-compact .team-logo-mini { width: 32px; height: 32px; } .knockouts-grid .phase-container.is-final .match-compact .score-pill { font-size: 1.15rem; padding: 7px 14px; } }/* Reduced motion — respect user preference */ @media (prefers-reduced-motion: reduce) { .comp-wrap *, .comp-wrap *::before, .comp-wrap *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }
(function () { const API_BASE = ‘https://rugby-app-4ebacff10fda.herokuapp.com’; const root = document.getElementById(‘comp-root’);// Slug URL → slug API quand ils diffèrent const SLUG_ALIASES = { ‘rugby-nationale’: ‘nationale’, ‘six-nations’: ‘tournoi-des-6-nations’, ‘six-nations-u20’: ‘tournoi-des-6-nations-u20’, ‘six-nations-feminin’: ‘tournoi-des-6-nations-f’, ‘champions-cup’: ‘coupe-des-champions’, ‘challenge-cup’: ‘challenge’, ‘rugby-feminine’: ‘coupe-du-monde-f’, ‘rugby-u20’: ‘coupe-du-monde-u20’, ‘coupe-du-monde-de-rugby-feminine’: ‘coupe-du-monde-f’, ‘coupe-du-monde-de-rugby-u20’: ‘coupe-du-monde-u20’, ‘tournee-dete’: ‘test-matches-automne’, // Élite féminines : aliases de secours si la page WP utilise elite-1/elite-2 ‘elite-1’: ‘elite-1-feminine’, ‘elite-2’: ‘elite-2-feminine’, ‘elite-1-f’: ‘elite-1-feminine’, ‘elite-2-f’: ‘elite-2-feminine’ };// Ordre : ?competition=, data-competition, URL path const slugFromQuery = new URLSearchParams(window.location.search).get(‘competition’); const slugFromUrl = (() => { const path = window.location.pathname.replace(/\/+$/,  »); const m = path.match(/\/(?:resultats-|competitions?\/)([a-z0-9-]+)/i); return m ? m[1] : null; })(); let slug = slugFromQuery || root.dataset.competition || slugFromUrl; if (slug && SLUG_ALIASES[slug]) slug = SLUG_ALIASES[slug];if (!slug) { root.innerHTML = ‘
Compétition non spécifiée. Ajoute data-competition="federale-3" sur #comp-root.
‘; return; }// — Utils — const esc = (s) => String(s ??  »).replace(/[& »‘]/g, (c) => ({‘&’:’&’, »:’>’,’ »‘:’"’, »‘ »:’'’}[c])); const MATCH_URL = (id) => `/live-en-direct/?id=${id}`;const isToday = (iso) => { if (!iso) return false; const d = new Date(iso); const n = new Date(); return d.getFullYear() === n.getFullYear() && d.getMonth() === n.getMonth() && d.getDate() === n.getDate(); }; const formatDate = (iso) => { if (!iso) return  »; if (isToday(iso)) return « Aujourd’hui »; return new Date(iso).toLocaleDateString(‘fr-FR’, { day: ‘numeric’, month: ‘short’ }); }; const formatDayHeader = (iso) => { if (!iso) return  »; if (isToday(iso)) return « Aujourd’hui »; return new Date(iso).toLocaleDateString(‘fr-FR’, { weekday: ‘long’, day: ‘numeric’, month: ‘long’ }); }; const dayKey = (iso) => iso ? new Date(iso).toDateString() : ‘__none__’; const groupMatchesByDay = (matches) => { const order = []; const map = new Map(); (matches || []).forEach((m) => { const k = dayKey(m.date); if (!map.has(k)) { map.set(k, []); order.push(k); } map.get(k).push(m); }); return order.map((k) => ({ key: k, iso: (map.get(k)[0] || {}).date, matches: map.get(k) })); }; const formatTime = (iso) => { if (!iso) return  »; return new Date(iso).toLocaleTimeString(‘fr-FR’, { hour: ‘2-digit’, minute: ‘2-digit’ }); }; const isLiveState = (s) => s === ‘endirect’ || s === ‘mi-temps’;// — Rendu d’un match — const renderMatch = (m) => { const live = isLiveState(m.etat); const finished = m.etat === ‘termine’; const hasScore = live || finished; const stateClass = live ? ‘live’ : (finished ? ‘finished’ :  »);let center; if (hasScore) { const score = `
${esc(m.score_locale ?? 0)} ${esc(m.score_visiteuse ?? 0)}
`; center = live ? `${score}En direct` : score; } else { const dateLabel = formatDate(m.date); const dateHtml = isToday(m.date) ? `${esc(dateLabel)}` : `${esc(dateLabel)}`; center = `
${dateHtml}${esc(formatTime(m.date))}
`; }return `
${m.local_team.img_url ? `` :  »} ${esc(m.local_team.name)}
${center}
${m.visitor_team.img_url ? `` :  »} ${esc(m.visitor_team.name)}
`; };// — Rendu du classement — const renderStandings = (standings) => { if (!standings || !standings.length) return  »; const hasForme = standings.some((t) => Array.isArray(t.forme) && t.forme.length); return `
${hasForme ? ‘‘ :  »} ${standings.map((t, i) => ` ${hasForme ? `` :  »} `).join( »)}
PosÉquipeJGNPPMPE+/-FormePts
${esc(t.rank ?? i + 1)}
${t.team_img_url ? `Résultats PRO D2` :  »} ${esc(t.team_name)}
${esc(t.matchs_joues ?? 0)}${esc(t.matchs_gagnes ?? 0)}${esc(t.matchs_nuls ?? 0)}${esc(t.matchs_perdus ?? 0)}${esc(t.buts_marques ?? 0)}${esc(t.buts_encaisses ?? 0)}${esc(t.diff_buts ?? 0)}${ (Array.isArray(t.forme) ? t.forme.slice(-5) : []).map((f) => { const c = String(f).toUpperCase(); return `${esc(c)}`; }).join( ») }${esc(t.points ?? 0)}
`; };// — Détection du round en cours — const findCurrentRoundIndex = (rounds) => { const now = Date.now(); let lastWithFinished = -1; let firstUpcoming = -1; rounds.forEach((r, i) => { const matches = r.matches || []; if (matches.some((m) => [‘termine’, ‘endirect’, ‘mi-temps’].includes(m.etat))) { lastWithFinished = i; } if (firstUpcoming === -1 && matches.some((m) => m.date && new Date(m.date).getTime() >= now)) { firstUpcoming = i; } }); if (lastWithFinished !== -1) return lastWithFinished; if (firstUpcoming !== -1) return firstUpcoming; return 0; };const roundHasLive = (r) => (r.matches || []).some((m) => isLiveState(m.etat)); const groupLiveCount = (g) => (g.rounds || []).reduce((acc, r) => acc + (r.matches || []).filter((m) => isLiveState(m.etat)).length, 0);// — Rendu d’une poule / groupe — const renderGroupPhase = (group, isActive, customTitle) => { const rounds = (group.rounds || []).slice().sort((a, b) => { const na = parseInt((a.name ||  »).match(/\d+/)?.[0] || 0); const nb = parseInt((b.name ||  »).match(/\d+/)?.[0] || 0); return na – nb; });const currentIdx = findCurrentRoundIndex(rounds); const groupKey = esc(group.name); const title = customTitle ? esc(customTitle) : `Poule ${groupKey}`; const liveCount = groupLiveCount(group);const subtabs = rounds.map((r, i) => { const classes = [ ‘phase-subtab’, i === currentIdx ? ‘active current’ :  », roundHasLive(r) ? ‘has-live’ :  » ].filter(Boolean).join(‘ ‘); return ``; }).join( »);const renderMatchesBlock = (matches) => { if (!matches || !matches.length) return ‘
Aucun match pour cette journée
‘; const days = groupMatchesByDay(matches); if (days.length `
${esc(formatDayHeader(d.iso) || ‘Date à définir’)}
${d.matches.map(renderMatch).join( »)}
`).join( »); };const panels = rounds.map((r, i) => `
${renderMatchesBlock(r.matches)}
`).join( »);const standingsHtml = renderStandings(group.standings); const subtabsHtml = rounds.length ? `
${subtabs}
` :  »; const contentHtml = `
${panels || ‘
Aucun match
‘}
`;return `
${title} ${liveCount ? `${liveCount} en direct` :  »}
${standingsHtml ? `` :  »}
${subtabsHtml} ${contentHtml}
`; };const renderGroups = (groups, compName) => { if (!groups || !groups.length) return  »; const sorted = […groups].sort((a, b) => { const na = parseInt(a.name), nb = parseInt(b.name); if (!isNaN(na) && !isNaN(nb)) return na – nb; return String(a.name).localeCompare(String(b.name)); });const singleGroup = sorted.length === 1; const phases = sorted.map((g, i) => renderGroupPhase(g, i === 0, singleGroup ? compName : null)).join( »);if (singleGroup) { return `${phases}`; }const selectorBtns = sorted.map((g, i) => { const live = groupLiveCount(g); return ``; }).join( »);return `
${selectorBtns}
${phases} `; };// — Phases finales (knockout) —// (B) Mappage nom verbeux → label court const SHORT_ROUND_LABELS = [ [/^trente.deuxi[èe]me\s+de\s+finale$/i, ’32es de finale’], [/^seizi[èe]me\s+de\s+finale$/i, ’16es de finale’], [/^huiti[èe]me\s+de\s+finale$/i, ‘8es de finale’], [/^quart\s+de\s+finale$/i, ‘Quarts de finale’], [/^quarts?\s+de\s+finale$/i, ‘Quarts de finale’], [/^demi[\s-]?finales?$/i, ‘Demi-finales’], [/^finale$/i, ‘Finale’], [/^barrages?$/i, ‘Barrages’], [/^match\s+pour\s+la\s+3e\s+place$/i, ‘3e place’], ]; const shortRoundLabel = (name) => { if (!name) return name; const trimmed = String(name).trim(); for (const [re, label] of SHORT_ROUND_LABELS) { if (re.test(trimmed)) return label; } return trimmed; };// (C) Ordre canonique des tours de phase finale const KNOCKOUT_RANK_PATTERNS = [ /classement|consolante/i, // 0 — matches de classement /^barrages?$/i, // 1 /trente.deuxi/i, // 2 — 32es /seizi/i, // 3 — 16es /huiti/i, // 4 — 8es /^quarts?\s+de\s+finale$/i, // 5 /^demi[\s-]?finales?$/i, // 6 /3e\s*place|troisi[èe]me\s+place/i, // 7 /^finale$/i, // 8 (exact, doit passer après demi/3e place) ]; const knockoutRank = (name) => { const n = String(name ||  »).trim(); for (let i = 0; i { const m = String(name ||  »).match(/^(.+?)\s*[-–—]\s*(aller|retour)\s*$/i); if (!m) return { base: String(name ||  »).trim(), leg: null }; return { base: m[1].trim(), leg: m[2].toLowerCase() }; };// Apparie les matchs aller/retour par paire d’équipes (clé = ids triés) const pairLegMatches = (allerMatches, retourMatches) => { const idOf = (t) => t?.id ?? t?.team_id ?? ‘?’; const keyOf = (m) => [idOf(m?.local_team), idOf(m?.visitor_team)].sort().join(‘|’); const retourByKey = new Map(); (retourMatches || []).forEach((m) => retourByKey.set(keyOf(m), m)); const ties = []; const used = new Set(); (allerMatches || []).forEach((aller) => { const k = keyOf(aller); const retour = retourByKey.get(k) || null; if (retour) used.add(k); ties.push({ aller, retour }); }); (retourMatches || []).forEach((retour) => { const k = keyOf(retour); if (!used.has(k)) ties.push({ aller: null, retour }); }); return ties; };// Calcule le score cumulé de l’aller/retour du point de vue de teamA (= local de l’aller). const aggregateTie = (tie) => { let teamA = null, teamB = null; if (tie.aller) { teamA = tie.aller.local_team; teamB = tie.aller.visitor_team; } else if (tie.retour) { teamA = tie.retour.visitor_team; teamB = tie.retour.local_team; } if (!teamA || !teamB) return null; const aId = teamA.id ?? teamA.team_id;let scoreA = 0, scoreB = 0; let aLegFinished = false, rLegFinished = false; let aLive = false, rLive = false;const addLeg = (m, isAller) => { if (!m) return; const finished = m.etat === ‘termine’; const live = isLiveState(m.etat); if (!finished && !live) return; const localId = m.local_team?.id ?? m.local_team?.team_id; const localScore = Number(m.score_locale ?? 0); const visitorScore = Number(m.score_visiteuse ?? 0); if (localId === aId) { scoreA += localScore; scoreB += visitorScore; } else { scoreA += visitorScore; scoreB += localScore; } if (isAller) { aLegFinished = finished; aLive = live; } else { rLegFinished = finished; rLive = live; } }; addLeg(tie.aller, true); addLeg(tie.retour, false);const anyPlayed = aLegFinished || rLegFinished || aLive || rLive; const bothDone = aLegFinished && rLegFinished; const winner = bothDone ? (scoreA > scoreB ? ‘A’ : (scoreB > scoreA ? ‘B’ : null)) : null; return { teamA, teamB, scoreA, scoreB, anyPlayed, bothDone, winner, aLive, rLive }; };// Rendu d’une confrontation aller/retour (ou aller seul / retour seul). // Construit la map des scores cumulés par paire d’équipes — utilisée pour afficher // un badge « Cumul » sur la rangée du retour quand les 2 manches sont jouées. const buildAggregateMap = (allerMatches, retourMatches) => { const map = new Map(); if (!allerMatches?.length || !retourMatches?.length) return map; const ties = pairLegMatches(allerMatches, retourMatches); ties.forEach((t) => { const agg = aggregateTie(t); if (!agg || !agg.bothDone) return; const aId = agg.teamA?.id ?? agg.teamA?.team_id; const bId = agg.teamB?.id ?? agg.teamB?.team_id; if (aId == null || bId == null) return; const key = [aId, bId].sort((x, y) => Number(x) – Number(y)).join(‘|’); map.set(key, agg); }); return map; };// Rangée compacte (mono-ligne) pour les phases finales const renderCompactMatch = (m, aggMap) => { const live = isLiveState(m.etat); const finished = m.etat === ‘termine’; const hasScore = live || finished; const localScore = Number(m.score_locale ?? 0); const visitorScore = Number(m.score_visiteuse ?? 0); const localWon = finished && localScore > visitorScore; const visitorWon = finished && visitorScore > localScore;const stateClasses = []; if (live) stateClasses.push(‘live’); else if (finished) stateClasses.push(‘finished’); if (localWon) stateClasses.push(‘local-winner’, ‘visitor-loser’); else if (visitorWon) stateClasses.push(‘visitor-winner’, ‘local-loser’);let mid; if (hasScore) { mid = `${esc(localScore)}${esc(visitorScore)}${live ? ‘live‘ :  »}`; } else { const date = m.date ? formatDate(m.date) :  »; const time = m.date ? formatTime(m.date) :  »; mid = `${esc(date)}${time ? ` ${esc(time)}` :  »}`; }let cumulBadge =  »; if (aggMap && aggMap.size) { const aId = m.local_team?.id ?? m.local_team?.team_id; const bId = m.visitor_team?.id ?? m.visitor_team?.team_id; if (aId != null && bId != null) { const key = [aId, bId].sort((x, y) => Number(x) – Number(y)).join(‘|’); const agg = aggMap.get(key); if (agg) { // Affiche le cumul du point de vue local du match courant. const matchLocalIsA = String(aId) === String(agg.teamA?.id ?? agg.teamA?.team_id); const cumulLocal = matchLocalIsA ? agg.scoreA : agg.scoreB; const cumulVisitor = matchLocalIsA ? agg.scoreB : agg.scoreA; const drawClass = agg.winner ?  » : ‘draw’; cumulBadge = `★ Cumul ${cumulLocal}${cumulVisitor}`; } } }const localLogo = m.local_team?.img_url ? `Résultats PRO D2` :  »; const visitorLogo = m.visitor_team?.img_url ? `Résultats PRO D2` :  »;return ` ${localLogo} ${esc(m.local_team?.name || ‘?’)} ${mid} ${esc(m.visitor_team?.name || ‘?’)} ${visitorLogo} ${cumulBadge} `; };const renderKnockouts = (phases) => { if (!phases || !phases.length) return  »; const ko = phases.filter((p) => Array.isArray(p.matches) && p.matches.length); if (!ko.length) return  »;// (A) Regroupement aller/retour : on fusionne les phases dont le nom ne diffère // que par le suffixe  » – aller » /  » – retour ». const groups = new Map(); ko.forEach((p) => { const { base, leg } = parseLegName(p.name); if (!groups.has(base)) { groups.set(base, { name: base, aller: null, retour: null, single: null, anyNumber: p.number ?? 999 }); } const g = groups.get(base); if (leg === ‘aller’) g.aller = p; else if (leg === ‘retour’) g.retour = p; else g.single = p; g.anyNumber = Math.min(g.anyNumber, p.number ?? 999); });// (C) Tri canonique : Barrage → 32es → 16es → 8es → 1/4 → 1/2 → 3e place → Finale const sortedGroups = Array.from(groups.values()).sort((a, b) => { const ra = knockoutRank(a.name); const rb = knockoutRank(b.name); if (ra !== rb) return ra – rb; return (a.anyNumber || 0) – (b.anyNumber || 0); });const blocks = sortedGroups.map((g, gi) => { const allMatches = [ …(g.aller?.matches || []), …(g.retour?.matches || []), …(g.single?.matches || []), ]; const liveCount = allMatches.filter((m) => isLiveState(m.etat)).length; const isFinal = /^finale$/i.test(g.name.trim()); const hasBothLegs = g.aller && g.retour;let body; if (hasBothLegs) { // (A) Onglets Aller / Retour, badge « Cumul » sur les retours bouclés const aggMap = buildAggregateMap(g.aller.matches, g.retour.matches); const allerMatches = (g.aller.matches || []).slice().sort((a, b) => new Date(a.date || 0) – new Date(b.date || 0)); const retourMatches = (g.retour.matches || []).slice().sort((a, b) => new Date(a.date || 0) – new Date(b.date || 0)); const allerLive = allerMatches.filter((m) => isLiveState(m.etat)).length; const retourLive = retourMatches.filter((m) => isLiveState(m.etat)).length; const allerDone = allerMatches.length && allerMatches.every((m) => m.etat === ‘termine’); // Onglet actif par défaut : retour si live, sinon le tour le plus récent (aller pas fini → aller, sinon retour). const defaultLeg = retourLive ? ‘retour’ : (allerDone ? ‘retour’ : ‘aller’); const tabsId = `ko-${gi}`; const allerBody = allerMatches.map((m) => renderCompactMatch(m, null)).join( ») || ‘
Aucun match
‘; const retourBody = retourMatches.map((m) => renderCompactMatch(m, aggMap)).join( ») || ‘
Aucun match
‘; body = `
${allerBody}
${retourBody}
`; } else if (g.aller || g.retour || g.single) { const phase = g.single || g.aller || g.retour; const matches = (phase.matches || []).slice().sort((a, b) => new Date(a.date || 0) – new Date(b.date || 0)); body = matches.length ? matches.map((m) => renderCompactMatch(m, null)).join( ») : ‘
Aucun match
‘; } else { body = ‘
Aucun match
‘; }return `
${esc(shortRoundLabel(g.name))} ${liveCount ? `${liveCount} en direct` :  »}
${body}
`; }).join( »);return `
${blocks}
`; };// Bind des onglets aller/retour (ajouté en plus du bindTabs principal) const bindKnockoutLegTabs = () => { root.querySelectorAll(‘.ko-leg-tab’).forEach((btn) => { btn.addEventListener(‘click’, () => { const id = btn.dataset.tabs; const leg = btn.dataset.leg; if (!id || !leg) return; root.querySelectorAll(`.ko-leg-tab[data-tabs= »${id} »]`).forEach((b) => { const on = b === btn; b.classList.toggle(‘active’, on); b.setAttribute(‘aria-selected’, on ? ‘true’ : ‘false’); }); root.querySelectorAll(`.ko-leg-panel[data-tabs= »${id} »]`).forEach((p) => { p.classList.toggle(‘active’, p.dataset.leg === leg); }); }); }); };// — URL state — const updateUrl = () => { const url = new URL(window.location.href); const activePoule = root.querySelector(‘.poule-btn.active’)?.dataset.poule; const activePanel = root.querySelector(‘.phase-container.group.active’); const activeJournee = activePanel?.querySelector(‘.phase-subtab.active’)?.dataset.journee; if (activePoule != null) url.searchParams.set(‘poule’, activePoule); else url.searchParams.delete(‘poule’); if (activeJournee != null) url.searchParams.set(‘journee’, activeJournee); else url.searchParams.delete(‘journee’); history.replaceState(null,  », url.toString()); };const setActivePoule = (p, { scroll = true } = {}) => { if (!p) return; let found = false; root.querySelectorAll(‘.poule-btn’).forEach((b) => { const match = b.dataset.poule === p; b.classList.toggle(‘active’, match); if (match) found = true; }); if (!found) return; root.querySelectorAll(‘.phase-container.group’).forEach((panel) => panel.classList.toggle(‘active’, panel.dataset.group === p)); if (scroll) { const btn = root.querySelector(‘.poule-btn.active’); btn?.scrollIntoView({ behavior: ‘smooth’, block: ‘nearest’, inline: ‘center’ }); } };const setActiveJournee = (groupKey, journeeIdx) => { const key = String(journeeIdx); root.querySelectorAll(`.phase-subtab[data-group= »${groupKey} »]`).forEach((b) => b.classList.toggle(‘active’, b.dataset.journee === key)); root.querySelectorAll(`.phase-journee-panel[data-group= »${groupKey} »]`).forEach((p) => p.classList.toggle(‘active’, p.dataset.journeePanel === key)); };// — Bindings tabs — const bindTabs = () => { root.querySelectorAll(‘.poule-btn’).forEach((btn) => { btn.addEventListener(‘click’, () => { setActivePoule(btn.dataset.poule, { scroll: true }); updateUrl(); }); });root.querySelectorAll(‘.phase-subtab’).forEach((btn) => { btn.addEventListener(‘click’, () => { setActiveJournee(btn.dataset.group, btn.dataset.journee); updateUrl(); }); }); };const applyUrlState = () => { const params = new URLSearchParams(window.location.search); const p = params.get(‘poule’); const j = params.get(‘journee’); if (p) setActivePoule(p, { scroll: false }); if (j != null) { const groupKey = root.querySelector(‘.phase-container.group.active’)?.dataset.group; if (groupKey) setActiveJournee(groupKey, j); } };// — Team search — let teamIndex = new Map(); // team_name (lowercased) → Set const buildTeamIndex = (groups) => { teamIndex = new Map(); (groups || []).forEach((g) => { (g.standings || []).forEach((t) => { const k = (t.team_name ||  »).toLowerCase().trim(); if (!k) return; if (!teamIndex.has(k)) teamIndex.set(k, new Set()); teamIndex.get(k).add(g.name); }); }); };const resetSearch = () => { root.querySelectorAll(‘.poule-btn’).forEach((b) => { b.style.display =  »; b.classList.remove(‘is-match’); }); root.querySelectorAll(‘.comp-standings tbody tr’).forEach((r) => r.classList.remove(‘is-match’)); root.querySelectorAll(‘.match-row’).forEach((r) => r.classList.remove(‘is-match’, ‘is-dim’)); };const bindSearch = () => { const wrap = root.querySelector(‘.team-search’); const input = wrap?.querySelector(‘input’); const clear = wrap?.querySelector(‘.ts-clear’); const hint = root.querySelector(‘.ts-hint’); if (!input) return;const run = () => { const q = input.value.trim().toLowerCase(); wrap.classList.toggle(‘has-value’, !!q); if (!q) { resetSearch(); if (hint) hint.textContent =  »; return; }const matchingPools = new Set(); teamIndex.forEach((pools, name) => { if (name.includes(q)) pools.forEach((p) => matchingPools.add(p)); });// Filter poule buttons + mark matches let firstMatch = null; root.querySelectorAll(‘.poule-btn’).forEach((b) => { const hit = matchingPools.has(b.dataset.poule); b.style.display = matchingPools.size ? (hit ?  » : ‘none’) :  »; b.classList.toggle(‘is-match’, hit); if (hit && !firstMatch) firstMatch = b.dataset.poule; });// Highlight standings rows root.querySelectorAll(‘.comp-standings tbody tr’).forEach((r) => { const n = r.querySelector(‘.team-cell span’)?.textContent.toLowerCase() ||  »; r.classList.toggle(‘is-match’, n.includes(q)); });// Dim non-matching matches, highlight matching ones root.querySelectorAll(‘.match-row’).forEach((r) => { const names = Array.from(r.querySelectorAll(‘.team-name’)).map((t) => t.textContent.toLowerCase()); const hit = names.some((n) => n.includes(q)); r.classList.toggle(‘is-match’, hit); r.classList.toggle(‘is-dim’, !hit); });// Auto-switch to first matching poule if current isn’t matching if (firstMatch) { const current = root.querySelector(‘.poule-btn.active’)?.dataset.poule; if (!matchingPools.has(current)) setActivePoule(firstMatch, { scroll: true }); }if (hint) { const n = matchingPools.size; hint.textContent = n ? `${n} poule${n > 1 ? ‘s’ :  »} contient${n > 1 ? ‘nent’ :  »} « ${input.value.trim()} »` : `Aucune équipe trouvée pour « ${input.value.trim()} »`; } };let t; input.addEventListener(‘input’, () => { clearTimeout(t); t = setTimeout(run, 120); }); clear?.addEventListener(‘click’, () => { input.value =  »; input.focus(); run(); }); };// — Compte le total de matchs live sur la compétition — const countLive = (data) => { let n = 0; const walk = (arr) => (arr || []).forEach((m) => { if (isLiveState(m.etat)) n++; }); (data.groups || []).forEach((g) => (g.rounds || []).forEach((r) => walk(r.matches))); (data.phases || []).forEach((p) => { walk(p.matches); (p.rounds || []).forEach((r) => walk(r.matches)); }); (data.knockout_phases || []).forEach((p) => walk(p.matches)); return n; };// — Fetch + rendu — fetch(`${API_BASE}/competitions/${encodeURIComponent(slug)}/adaptive`) .then((r) => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then((data) => { const title = esc(data.competition?.display_name || slug); const year = data.competition?.years ? esc(data.competition.years) :  »; const liveTotal = countLive(data);// Normalisation : data.groups (championship) — sinon on reconstruit depuis data.phases // – cup : phases type=’group’ avec `journees` + `matches` à plat // – tournament: phases type=’group’ avec `rounds` déjà structurés // – knockouts : phases type=’knockout’ avec `matches` let groupsToRender = data.groups || null; let knockoutsToRender = data.knockout_phases || null;// Détecte un nom de round de phase finale (vs une journée régulière) const isKnockoutRoundName = (n) => /finale|demi|quart|barrage|classement|match\s+pour|(?:huiti|seizi|trente)/i.test(n ||  »);if (!groupsToRender && Array.isArray(data.phases)) { const normGroups = []; const koByName = new Map(); // dédup phase finale entre poules data.phases.forEach((p) => { const hasGroupShape = Array.isArray(p.rounds) || Array.isArray(p.journees); if (hasGroupShape) { let rounds = Array.isArray(p.rounds) ? p.rounds : null; // Cup : journees liste + matches plats → on regroupe par phase_name if (!rounds && Array.isArray(p.journees) && Array.isArray(p.matches)) { const byRound = {}; p.matches.forEach((m) => { const key = m.phase_name ||  »; (byRound[key] = byRound[key] || []).push(m); }); rounds = []; p.journees.forEach((j) => { const matches = byRound[j.name] || []; if (isKnockoutRoundName(j.name)) { // Extrait vers la section « Phases finales », dédupliqué par nom entre poules const existing = koByName.get(j.name); if (existing) { const seen = new Set(existing.matches.map((m) => m.id)); matches.forEach((m) => { if (!seen.has(m.id)) { existing.matches.push(m); seen.add(m.id); } }); } else { koByName.set(j.name, { id: j.id, name: j.name, number: j.journee_number || j.number || 999, matches: matches.slice() }); } } else { rounds.push({ id: j.id, name: j.name, number: j.journee_number || j.number, matches }); } }); } // Nom normalisé : strip « Poule  » pour réutiliser le prefix côté rendu const cleanName = String(p.name || p.id ||  »).replace(/^Poule\s+/i,  »).trim() || String(p.id ||  »); normGroups.push({ name: cleanName, rounds: rounds || [], standings: p.standings }); } else if (Array.isArray(p.matches) && p.matches.length) { koByName.set(p.name || p.id, p); } }); if (normGroups.length) groupsToRender = normGroups; if (koByName.size && !knockoutsToRender) knockoutsToRender = Array.from(koByName.values()); }// Phases finales « actuelles » = au moins un match live, à venir, ou terminé // dans les 7 derniers jours. Si oui, on les rend AVANT la saison régulière // pour que l’utilisateur n’ait pas à scroller. const knockoutsAreCurrent = (knockouts) => { if (!Array.isArray(knockouts) || !knockouts.length) return false; const now = Date.now(); const RECENT_WINDOW_MS = 7 * 24 * 60 * 60 * 1000; return knockouts.some((p) => (p.matches || []).some((m) => { if (isLiveState(m.etat)) return true; if (!m.date) return false; const t = new Date(m.date).getTime(); if (Number.isNaN(t)) return false; return t >= now – RECENT_WINDOW_MS; })); }; const knockoutsFirst = knockoutsAreCurrent(knockoutsToRender);const showSearch = (groupsToRender || []).length > 1 && (groupsToRender || []).some((g) => (g.standings || []).length);const searchHtml = showSearch ? `
` :  »;root.innerHTML = `
Compétition

${title}

${year ? `Saison ${year}` :  »} ${liveTotal ? `${liveTotal} match${liveTotal > 1 ? ‘s’ :  »} en direct` :  »}
${searchHtml} ${knockoutsFirst ? renderKnockouts(knockoutsToRender) + renderGroups(groupsToRender, data.competition?.display_name || slug) : renderGroups(groupsToRender, data.competition?.display_name || slug) + renderKnockouts(knockoutsToRender)} `; buildTeamIndex(groupsToRender); bindTabs(); bindKnockoutLegTabs(); bindSearch(); applyUrlState(); }) .catch((err) => { root.innerHTML = `
Erreur de chargement : ${esc(err.message)}
`; }); })();

Résultats PRO D2

Résultats Pro D2 et tout le calendrier de ce championnat.

À lire aussi | Béziers : le jeune pilier Luqobo Makwedini meurt à 20 ans

Matchs de Pro D2 en direct, cliquez sur le club ou le match de votre choix pour suivre la rencontre en direct/live, voir ou revoir les actions du match, les joueurs qui ont marqué, ainsi que la composition des équipes. Vous pouvez également conserver cette page afin de suivre l’évolution des scores de chaque équipe, y compris les Résultats PRO D2.

Résultats PRO D2 : suivez l’action palpitante du championnat

La PRO D2 est la deuxième division du rugby français. Elle offre une plateforme compétitive aux clubs aspirant à intégrer l’élite du rugby. Ce championnat, à travers ses Résultats PRO D2, est un pilier essentiel du développement du rugby en France. Découvrons ensemble les grandes lignes de cette compétition, son format, et les équipes qui l’animent.

Le format de la PRO D2

Le championnat PRO D2 regroupe 16 équipes professionnelles. Ces clubs s’affrontent au cours d’une saison régulière, où chaque équipe joue à domicile et à l’extérieur. Les Résultats PRO D2 reflètent la lutte acharnée entre ces formations, avec pour objectif ultime de terminer parmi les meilleures et ainsi rêver de promotion.

L’enjeu de la promotion vers le TOP 14

La promotion est au cœur de la compétition. Pour chaque équipe, l’objectif principal est d’accéder au TOP 14, le sommet du rugby français. Les Résultats PRO D2 jouent un rôle crucial dans ce parcours, car ils déterminent les équipes qui se qualifieront pour les phases finales, menant potentiellement à la montée.

À lire aussi | Florian Sotoca (RC Lens) devient actionnaire minoritaire à Narbonne

Les clubs emblématiques de la PRO D2

Des clubs prestigieux comme l’US Oyonnax, le SU Agen ou le Biarritz Olympique sont des noms bien connus du rugby français. Ces équipes, souvent en quête de promotion, contribuent à rendre la compétition plus intense. Les Résultats PRO D2 témoignent de leur ambition et de leur lutte pour atteindre les sommets.

Le rôle de la PRO D2 dans le développement des talents

La PRO D2 est une véritable pépinière de jeunes talents. De nombreux joueurs font leurs débuts dans cette compétition avant d’atteindre l’élite. Les Résultats PRO D2 permettent aux recruteurs et observateurs de repérer ces futurs stars du rugby, qui gagnent en expérience au fil des matchs.

Des matchs sous haute intensité

Les rencontres de la PRO D2 sont souvent intenses, et chaque point compte dans la course à la promotion. Les équipes rivalisent d’efforts pour grimper au classement, et les Résultats PRO D2 reflètent cette détermination. Que ce soit lors des matchs à domicile ou à l’extérieur, les équipes donnent tout sur le terrain.

Suivre les Résultats PRO D2 en direct

Pour les passionnés de rugby, il est désormais facile de suivre les Résultats PRO D2 en direct. Grâce à une couverture complète, que ce soit à la radio, en ligne ou via des applications dédiées, vous ne manquerez rien des exploits de vos équipes préférées. Chaque essai, transformation et pénalité est commenté en direct pour enrichir votre expérience.

À lire aussi | Vannes : Giorgi Beria rejoint le promu breton en provenance de Perpignan

Conclusion

La PRO D2 n’est pas seulement une compétition de rugby, c’est un vivier de talents et une bataille féroce pour la promotion vers le TOP 14. Grâce aux Résultats PRO D2, les amateurs peuvent suivre de près l’évolution de leurs équipes favorites. Ne manquez aucun moment fort de cette compétition captivante et plongez dans l’univers du rugby avec passion.


Pour Résumer : La PRO D2 est un championnat clé du rugby français. Les Résultats permettent aux fans de suivre l’évolution de la saison et d’observer la lutte des équipes pour la promotion vers le TOP 14.

Résultats Pro D2
Résultats Pro D2

Les scores à la mi-temps de la Pro D2 : Analyse des performances

Introduction

La Pro D2, deuxième division du rugby français, est réputée pour son intensité et son niveau de jeu. Les scores à la mi-temps jouent un rôle crucial dans la dynamique des matchs. Comprendre ces chiffres permet d’analyser les performances des équipes et d’anticiper les résultats finaux. Dans cet article, nous examinerons les tendances des scores à la mi-temps de la Pro D2, en mettant en lumière les équipes les plus performantes et les facteurs qui influencent ces résultats.

Les tendances des scores à la mi-temps

Les équipes dominantes

Certaines équipes se distinguent par des performances remarquables à la mi-temps. Par exemple, des clubs comme Perpignan et Bayonne ont souvent affiché des scores avantageux lors des premiers quarante minutes de jeu. Leur capacité à imposer leur jeu dès le début des rencontres leur permet de construire des marges de sécurité qui se révèlent décisives par la suite.

À lire aussi | Radio-rugby, PODCAST “Côté ouvert” : Le Top 14 en conclusion, Championnat des Nations et coup d’œil dans le rétro. Saison 10 épisode 21.

Les scores serrés

À l’inverse, d’autres équipes, comme Béziers et Carcassonne, ont connu des scores plus serrés à la mi-temps. Ces rencontres témoignent d’une forte opposition entre les équipes, où chaque point compte. L’importance des stratégies défensives et des capacités de récupération devient alors primordiale pour ces équipes.

Facteurs influençant les scores à la mi-temps

Les conditions de jeu

Les conditions météorologiques peuvent avoir un impact significatif sur les performances des équipes. Par exemple, des terrains boueux ou des conditions venteuses peuvent influencer le style de jeu et, par conséquent, les scores à la mi-temps. Les équipes habituées à jouer dans des conditions difficiles peuvent tirer parti de ces situations, tandis que d’autres pourraient en pâtir.

La gestion de l’effectif

La profondeur de l’effectif d’une équipe est également un facteur déterminant. Les équipes qui disposent de remplaçants de qualité peuvent ajuster leur stratégie en cours de match, ce qui peut influer sur les scores à la mi-temps. Une rotation efficace permet de garder les joueurs frais et de maintenir un rythme soutenu.

Analyse des scores à la mi-temps des dernières saisons

Comparaison saisonnière

Une analyse des scores à la mi-temps des dernières saisons de la Pro D2 révèle des variations intéressantes. Par exemple, lors de la saison précédente, les équipes ayant mené à la mi-temps ont souvent remporté la victoire. En revanche, des surprises se sont produites, avec des équipes renversant la tendance en seconde période. Cela souligne l’importance de la concentration et de l’engagement sur l’ensemble de la rencontre.

Statistiques clés

Voici quelques statistiques clés concernant les scores à la mi-temps de la Pro D2 :

  • Pourcentage de victoires pour les équipes en tête à la mi-temps : 75%
  • Matchs avec un écart de 10 points ou plus à la mi-temps : 40%
  • Nombre de retournements de situation en seconde période : 20%

Ces chiffres illustrent à quel point il est crucial de bien débuter un match.

Impact des scores à la mi-temps sur les stratégies de jeu

Adaptations tactiques

Les scores à la mi-temps influencent souvent les choix tactiques des entraîneurs. Une équipe en retard pourrait choisir d’augmenter son intensité offensive, tandis qu’une équipe en tête pourrait opter pour un jeu plus conservateur. Par conséquent, les entraîneurs doivent faire preuve de flexibilité et d’anticipation.

Importance des discours de mi-temps

Le discours de l’entraîneur à la mi-temps est également un élément clé. Un discours motivant peut redynamiser une équipe en difficulté, tandis qu’un message de prudence peut aider à maintenir l’avantage. Les décisions prises pendant cette période peuvent souvent déterminer le résultat final du match.

Les enjeux des scores à la mi-temps

La lutte pour la promotion

La lutte pour la promotion vers le Top 14 intensifie l’importance des scores à la mi-temps. Les équipes en quête de points se doivent de bien performer en première mi-temps pour s’assurer un avantage compétitif. Chaque point compte, et une bonne performance à la mi-temps peut s’avérer décisive pour l’issue de la saison.

La lutte contre la relégation

Inversement, les équipes luttant pour éviter la relégation doivent également prêter attention à leurs performances à la mi-temps. Un bon départ peut leur permettre de gagner des matchs cruciaux, les aidant ainsi à sortir de la zone de relégation.

Pour Résumer

Les scores à la mi-temps de la Pro D2 offrent une perspective intéressante sur la performance des équipes. Qu’il s’agisse de clubs dominants ou de matchs serrés, chaque score a une signification et peut influencer les résultats finaux. En tenant compte des conditions de jeu, de la gestion des effectifs et des stratégies d’adaptation, les équipes peuvent maximiser leurs chances de succès. La dynamique des scores à la mi-temps sera sans aucun doute un élément à suivre de près dans les saisons à venir.

Partager :