{"id":600,"date":"2026-06-11T13:12:18","date_gmt":"2026-06-11T11:12:18","guid":{"rendered":"https:\/\/www.zerolimit.best\/?page_id=600"},"modified":"2026-06-11T13:14:29","modified_gmt":"2026-06-11T11:14:29","slug":"tour-de-rouvy","status":"publish","type":"page","link":"https:\/\/www.zerolimit.best\/index.php\/tour-de-rouvy\/","title":{"rendered":"Tour de Rouvy"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Classement<\/h2>\n\n\n\n<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/>\n  <title>Le Tour des Amis \u2014 Rouvy<\/title>\n  <link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\" \/>\n  <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Barlow+Condensed:wght@400;600;700;800&#038;family=Inter:wght@400;500;600&#038;display=swap\" rel=\"stylesheet\" \/>\n  <style>\n    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n    :root {\n      --bg: #0D1117; --surface: #161B22; --border: #21262D;\n      --text: #E6EDF3; --muted: #7D8590;\n      --yellow: #E8C547; --red: #E84040; --green: #4CAF77;\n      --blue: #4A90D9; --purple: #A371F7;\n    }\n    body { background: var(--bg); color: var(--text); font-family: 'Inter', sans-serif; font-size: 14px; min-height: 100vh; }\n\n    header {\n      background: linear-gradient(135deg, #0D1117 0%, #1A1F2E 50%, #0D1117 100%);\n      border-bottom: 2px solid var(--yellow);\n      padding: 28px 24px 24px; text-align: center; position: relative; overflow: hidden;\n    }\n    header::before {\n      content: ''; position: absolute; inset: 0;\n      background: repeating-linear-gradient(-45deg, transparent, transparent 40px, rgba(232,197,71,0.03) 40px, rgba(232,197,71,0.03) 41px);\n      pointer-events: none;\n    }\n    .header-eyebrow { font-family: 'Barlow Condensed', sans-serif; font-size: 13px; font-weight: 600; letter-spacing: 0.2em; color: var(--yellow); text-transform: uppercase; margin-bottom: 8px; }\n    header h1 { font-family: 'Barlow Condensed', sans-serif; font-size: clamp(36px, 8vw, 72px); font-weight: 800; line-height: 0.95; color: #fff; text-transform: uppercase; }\n    header h1 span { color: var(--yellow); }\n    .stage-count-pill { display: inline-block; background: var(--yellow); color: #0D1117; font-family: 'Barlow Condensed', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; padding: 3px 10px; border-radius: 20px; margin-top: 12px; }\n\n    .container { max-width: 1100px; margin: 0 auto; padding: 32px 16px 60px; }\n    .section-title { font-family: 'Barlow Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.25em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }\n\n    .tabs-nav { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 20px; }\n    .tab-btn { background: var(--surface); border: 1px solid var(--border); color: var(--muted); font-family: 'Barlow Condensed', sans-serif; font-size: 13px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; padding: 7px 14px; border-radius: 6px; cursor: pointer; transition: all 0.15s; }\n    .tab-btn:hover { color: var(--text); border-color: #444; }\n    .tab-btn.active { background: var(--yellow); border-color: var(--yellow); color: #0D1117; }\n\n    .maillots-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 32px; }\n    .maillot-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; display: flex; align-items: center; gap: 14px; }\n    .maillot-icon { width: 44px; height: 44px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; }\n    .maillot-card.jaune .maillot-icon { background: rgba(232,197,71,0.15); }\n    .maillot-card.rouge .maillot-icon { background: rgba(232,64,64,0.15); }\n    .maillot-card.vert .maillot-icon  { background: rgba(76,175,119,0.15); }\n    .maillot-card.blanc .maillot-icon { background: rgba(163,113,247,0.15); }\n    .maillot-label { font-family: 'Barlow Condensed', sans-serif; font-size: 11px; font-weight: 600; letter-spacing: 0.15em; text-transform: uppercase; }\n    .maillot-card.jaune .maillot-label { color: var(--yellow); }\n    .maillot-card.rouge .maillot-label { color: var(--red); }\n    .maillot-card.vert .maillot-label  { color: var(--green); }\n    .maillot-card.blanc .maillot-label { color: var(--purple); }\n    .maillot-leader { font-family: 'Barlow Condensed', sans-serif; font-size: 18px; font-weight: 700; color: var(--text); margin-top: 1px; }\n    .maillot-detail { font-size: 11px; color: var(--muted); margin-top: 2px; }\n\n    .table-wrap { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }\n    table { width: 100%; border-collapse: collapse; }\n    thead th { background: var(--bg); font-family: 'Barlow Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--border); }\n    thead th:first-child { width: 44px; text-align: center; }\n    tbody tr { border-bottom: 1px solid var(--border); transition: background 0.1s; }\n    tbody tr:last-child { border-bottom: none; }\n    tbody tr:hover { background: rgba(255,255,255,0.03); }\n    tbody td { padding: 11px 14px; vertical-align: middle; }\n    tbody td:first-child { text-align: center; }\n    .rank { font-family: 'Barlow Condensed', sans-serif; font-size: 16px; font-weight: 800; color: var(--muted); }\n    .rank-1 { color: #FFD700; } .rank-2 { color: #C0C0C0; } .rank-3 { color: #CD7F32; }\n    .rider-name { font-weight: 600; color: var(--text); font-size: 14px; }\n    .rider-flag { margin-right: 6px; font-size: 16px; }\n    .badge { display: inline-block; font-family: 'Barlow Condensed', sans-serif; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; padding: 2px 7px; border-radius: 4px; margin-left: 6px; }\n    .badge-yellow { background: rgba(232,197,71,0.2); color: var(--yellow); }\n    .badge-red    { background: rgba(232,64,64,0.2);  color: var(--red); }\n    .badge-green  { background: rgba(76,175,119,0.2); color: var(--green); }\n    .badge-purple { background: rgba(163,113,247,0.2);color: var(--purple); }\n    .time-val { font-family: 'Barlow Condensed', sans-serif; font-size: 15px; font-weight: 600; color: var(--text); }\n    .time-gap { font-size: 11px; color: var(--muted); }\n    .pts-val { font-family: 'Barlow Condensed', sans-serif; font-size: 15px; font-weight: 700; }\n    .pts-points { color: var(--green); } .pts-mtn { color: var(--red); } .pts-cbt { color: var(--purple); }\n\n    .stage-info-bar { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 14px 18px; margin-bottom: 20px; display: flex; gap: 24px; flex-wrap: wrap; align-items: center; }\n    .stage-info-item { display: flex; flex-direction: column; gap: 2px; }\n    .stage-info-label { font-size: 10px; font-family: 'Barlow Condensed', sans-serif; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); }\n    .stage-info-val { font-family: 'Barlow Condensed', sans-serif; font-size: 16px; font-weight: 700; color: var(--text); }\n    .stage-info-val.accent { color: var(--yellow); }\n\n    .btn { font-family: 'Barlow Condensed', sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; padding: 7px 16px; border-radius: 6px; cursor: pointer; border: 1px solid; transition: all 0.15s; }\n    .btn-primary { background: var(--yellow); border-color: var(--yellow); color: #0D1117; }\n    .btn-primary:hover { opacity: 0.85; }\n    .btn-ghost { background: transparent; border-color: var(--border); color: var(--muted); }\n    .btn-ghost:hover { border-color: #555; color: var(--text); }\n    .btn-danger { background: transparent; border-color: rgba(232,64,64,0.4); color: var(--red); }\n    .btn-danger:hover { background: rgba(232,64,64,0.1); }\n\n    .modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 100; align-items: center; justify-content: center; padding: 16px; }\n    .modal-overlay.open { display: flex; }\n    .modal { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 24px; width: 100%; max-width: 520px; max-height: 90vh; overflow-y: auto; }\n    .modal h2 { font-family: 'Barlow Condensed', sans-serif; font-size: 22px; font-weight: 800; text-transform: uppercase; margin-bottom: 20px; color: var(--yellow); }\n    .form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n    .form-group { display: flex; flex-direction: column; gap: 5px; }\n    .form-group.full { grid-column: 1 \/ -1; }\n    .form-group label { font-family: 'Barlow Condensed', sans-serif; font-size: 11px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); }\n    .form-group input, .form-group select { background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-family: 'Inter', sans-serif; font-size: 14px; padding: 8px 10px; outline: none; transition: border-color 0.15s; }\n    .form-group input:focus, .form-group select:focus { border-color: var(--yellow); }\n    .form-group select option { background: var(--surface); }\n    .modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }\n\n    .empty-state { padding: 48px 24px; text-align: center; color: var(--muted); }\n    .empty-state .empty-icon { font-size: 40px; margin-bottom: 12px; }\n    .empty-state p { font-family: 'Barlow Condensed', sans-serif; font-size: 16px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; }\n\n    .gc-tabs-nav { display: flex; gap: 6px; margin-bottom: 16px; flex-wrap: wrap; }\n    .gc-tab-btn { font-family: 'Barlow Condensed', sans-serif; font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; padding: 6px 14px; border-radius: 20px; cursor: pointer; border: 1px solid; transition: all 0.15s; }\n    .gc-tab-btn.yellow { border-color: var(--yellow); color: var(--yellow); }\n    .gc-tab-btn.yellow.active { background: var(--yellow); color: #0D1117; }\n    .gc-tab-btn.green  { border-color: var(--green);  color: var(--green); }\n    .gc-tab-btn.green.active  { background: var(--green);  color: #0D1117; }\n    .gc-tab-btn.red    { border-color: var(--red);    color: var(--red); }\n    .gc-tab-btn.red.active    { background: var(--red);    color: #fff; }\n    .gc-tab-btn.purple { border-color: var(--purple); color: var(--purple); }\n    .gc-tab-btn.purple.active { background: var(--purple); color: #fff; }\n\n    .loading-overlay { position: fixed; inset: 0; background: rgba(13,17,23,0.85); z-index: 200; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; }\n    .spinner { width: 36px; height: 36px; border: 3px solid var(--border); border-top-color: var(--yellow); border-radius: 50%; animation: spin 0.7s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .loading-msg { font-family: 'Barlow Condensed', sans-serif; font-size: 16px; font-weight: 600; letter-spacing: 0.1em; color: var(--muted); text-transform: uppercase; }\n\n    .sync-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--green); margin-left: 10px; }\n    .sync-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); animation: pulse 2s ease-in-out infinite; }\n    @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }\n\n    .admin-notice { background: rgba(232,197,71,0.08); border: 1px solid rgba(232,197,71,0.25); border-radius: 8px; padding: 10px 14px; margin-bottom: 16px; font-size: 13px; color: var(--yellow); }\n\n    @media (max-width: 600px) {\n      .form-grid { grid-template-columns: 1fr; }\n      .stage-info-bar { gap: 12px; }\n    }\n  <\/style>\n<\/head>\n<body>\n\n<!-- Loading -->\n<div class=\"loading-overlay\" id=\"loadingOverlay\">\n  <div class=\"spinner\"><\/div>\n  <div class=\"loading-msg\">Chargement du tour\u2026<\/div>\n<\/div>\n\n<header>\n  <div class=\"header-eyebrow\">Rouvy \u00b7 Tour Virtuel<\/div>\n  <h1>Le Tour<br\/><span>des Amis<\/span><\/h1>\n  <div id=\"headerPill\" class=\"stage-count-pill\">\u2026<\/div>\n<\/header>\n\n<div class=\"container\">\n\n  <!-- Leaders -->\n  <div class=\"section-title\">Leaders actuels<\/div>\n  <div class=\"maillots-strip\">\n    <div class=\"maillot-card jaune\"><div class=\"maillot-icon\">\ud83d\udfe1<\/div><div><div class=\"maillot-label\">Maillot Jaune<\/div><div class=\"maillot-leader\" id=\"ldr-gc\">\u2014<\/div><div class=\"maillot-detail\" id=\"ldr-gc-d\">Classement g\u00e9n\u00e9ral temps<\/div><\/div><\/div>\n    <div class=\"maillot-card vert\"><div class=\"maillot-icon\">\ud83d\udfe2<\/div><div><div class=\"maillot-label\">Maillot Vert<\/div><div class=\"maillot-leader\" id=\"ldr-pts\">\u2014<\/div><div class=\"maillot-detail\" id=\"ldr-pts-d\">Classement par points<\/div><\/div><\/div>\n    <div class=\"maillot-card rouge\"><div class=\"maillot-icon\">\ud83d\udd34<\/div><div><div class=\"maillot-label\">Maillot \u00e0 Pois<\/div><div class=\"maillot-leader\" id=\"ldr-mtn\">\u2014<\/div><div class=\"maillot-detail\" id=\"ldr-mtn-d\">Classement montagne<\/div><\/div><\/div>\n    <div class=\"maillot-card blanc\"><div class=\"maillot-icon\">\ud83d\udfe3<\/div><div><div class=\"maillot-label\">Combatif<\/div><div class=\"maillot-leader\" id=\"ldr-cbt\">\u2014<\/div><div class=\"maillot-detail\" id=\"ldr-cbt-d\">Prix du combatif<\/div><\/div><\/div>\n  <\/div>\n\n  <!-- Main tabs -->\n  <div class=\"tabs-nav\">\n    <button class=\"tab-btn active\" onclick=\"switchMain('etapes',this)\">\ud83d\udccd \u00c9tapes<\/button>\n    <button class=\"tab-btn\" onclick=\"switchMain('general',this)\">\ud83c\udfc6 Classement G\u00e9n\u00e9ral<\/button>\n    <button class=\"tab-btn\" onclick=\"switchMain('admin',this)\">\u2699\ufe0f Admin<\/button>\n  <\/div>\n\n  <!-- ETAPES -->\n  <div id=\"tab-etapes\">\n    <div style=\"display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:14px;\">\n      <div class=\"section-title\" style=\"margin:0;\">R\u00e9sultats par \u00e9tape<\/div>\n      <button class=\"btn btn-primary\" id=\"btnAddResult\" onclick=\"openModal('resultModal')\" style=\"display:none\">\uff0b Saisir r\u00e9sultat<\/button>\n    <\/div>\n    <div class=\"tabs-nav\" id=\"stageTabs\"><\/div>\n    <div id=\"stagePanels\"><\/div>\n  <\/div>\n\n  <!-- GENERAL -->\n  <div id=\"tab-general\" style=\"display:none\">\n    <div class=\"section-title\">Classements g\u00e9n\u00e9raux<\/div>\n    <div class=\"gc-tabs-nav\">\n      <button class=\"gc-tab-btn yellow active\" onclick=\"switchGC('gc-temps',this)\">\u23f1 Temps<\/button>\n      <button class=\"gc-tab-btn green\"  onclick=\"switchGC('gc-points',this)\">\ud83c\udfc5 Points<\/button>\n      <button class=\"gc-tab-btn red\"    onclick=\"switchGC('gc-montagne',this)\">\u26f0 Montagne<\/button>\n      <button class=\"gc-tab-btn purple\" onclick=\"switchGC('gc-combatif',this)\">\u26a1 Combatif<\/button>\n    <\/div>\n    <div id=\"gc-temps\"><div class=\"table-wrap\"><table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Temps total<\/th><th>Victoires<\/th><\/tr><\/thead><tbody id=\"tb-temps\"><\/tbody><\/table><\/div><\/div>\n    <div id=\"gc-points\" style=\"display:none\"><div class=\"table-wrap\"><table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Points<\/th><th>Victoires<\/th><\/tr><\/thead><tbody id=\"tb-pts\"><\/tbody><\/table><\/div><\/div>\n    <div id=\"gc-montagne\" style=\"display:none\"><div class=\"table-wrap\"><table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Points montagne<\/th><\/tr><\/thead><tbody id=\"tb-mtn\"><\/tbody><\/table><\/div><\/div>\n    <div id=\"gc-combatif\" style=\"display:none\"><div class=\"table-wrap\"><table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Points combatif<\/th><\/tr><\/thead><tbody id=\"tb-cbt\"><\/tbody><\/table><\/div><\/div>\n  <\/div>\n\n  <!-- ADMIN -->\n  <div id=\"tab-admin\" style=\"display:none\">\n    <div id=\"adminLock\">\n      <div class=\"admin-notice\">\ud83d\udd12 Zone r\u00e9serv\u00e9e \u00e0 l&rsquo;organisateur du tour.<\/div>\n      <div style=\"background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:20px;max-width:360px;\">\n        <div class=\"form-group\" style=\"margin-bottom:12px;\">\n          <label>Mot de passe admin<\/label>\n          <input type=\"password\" id=\"adminPwInput\" placeholder=\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\" onkeydown=\"if(event.key==='Enter')checkAdmin()\" \/>\n        <\/div>\n        <button class=\"btn btn-primary\" onclick=\"checkAdmin()\">Acc\u00e9der<\/button>\n      <\/div>\n    <\/div>\n    <div id=\"adminPanel\" style=\"display:none\">\n      <div class=\"admin-notice\">\u2705 Mode administrateur actif \u2014 vos modifications sont visibles par tous les visiteurs.<\/div>\n\n      <!-- Riders -->\n      <div style=\"background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:20px;margin-bottom:20px;\">\n        <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;\">\n          <h3 style=\"font-family:'Barlow Condensed',sans-serif;font-size:16px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;\">Participants<\/h3>\n          <button class=\"btn btn-primary\" onclick=\"openModal('riderModal')\">\uff0b Ajouter<\/button>\n        <\/div>\n        <div id=\"riderListAdmin\"><\/div>\n      <\/div>\n\n      <!-- Stages -->\n      <div style=\"background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:20px;margin-bottom:20px;\">\n        <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;\">\n          <h3 style=\"font-family:'Barlow Condensed',sans-serif;font-size:16px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;\">\u00c9tapes<\/h3>\n          <button class=\"btn btn-primary\" onclick=\"openModal('stageModal')\">\uff0b Ajouter<\/button>\n        <\/div>\n        <div id=\"stageListAdmin\"><\/div>\n      <\/div>\n\n      <!-- Results management -->\n      <div style=\"background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:20px;\">\n        <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;\">\n          <h3 style=\"font-family:'Barlow Condensed',sans-serif;font-size:16px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;\">R\u00e9sultats<\/h3>\n          <button class=\"btn btn-primary\" onclick=\"openModal('resultModal')\">\uff0b Saisir<\/button>\n        <\/div>\n        <div id=\"resultListAdmin\"><\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n<\/div>\n\n<!-- MODAL: Participant -->\n<div class=\"modal-overlay\" id=\"riderModal\">\n  <div class=\"modal\">\n    <h2>Ajouter un participant<\/h2>\n    <div class=\"form-grid\">\n      <div class=\"form-group full\"><label>Nom \/ Pseudo<\/label><input type=\"text\" id=\"riderName\" placeholder=\"Ex: Julien B.\" \/><\/div>\n      <div class=\"form-group\"><label>Drapeau (emoji)<\/label><input type=\"text\" id=\"riderFlag\" placeholder=\"\ud83c\uddeb\ud83c\uddf7\" maxlength=\"4\" \/><\/div>\n    <\/div>\n    <div class=\"modal-actions\">\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('riderModal')\">Annuler<\/button>\n      <button class=\"btn btn-primary\" onclick=\"addRider()\">Ajouter<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- MODAL: \u00c9tape -->\n<div class=\"modal-overlay\" id=\"stageModal\">\n  <div class=\"modal\">\n    <h2>Ajouter une \u00e9tape<\/h2>\n    <div class=\"form-grid\">\n      <div class=\"form-group full\"><label>Nom<\/label><input type=\"text\" id=\"stageName\" placeholder=\"Ex: Col du Galibier\" \/><\/div>\n      <div class=\"form-group\"><label>Distance (km)<\/label><input type=\"number\" id=\"stageDist\" placeholder=\"120\" \/><\/div>\n      <div class=\"form-group\"><label>D\u00e9nivel\u00e9 (m)<\/label><input type=\"number\" id=\"stageElev\" placeholder=\"2200\" \/><\/div>\n      <div class=\"form-group\"><label>Type<\/label>\n        <select id=\"stageType\"><option>Plaine<\/option><option>Vallonn\u00e9<\/option><option>Montagne<\/option><option>Contre-la-montre<\/option><\/select>\n      <\/div>\n      <div class=\"form-group\"><label>Date<\/label><input type=\"date\" id=\"stageDate\" \/><\/div>\n    <\/div>\n    <div class=\"modal-actions\">\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('stageModal')\">Annuler<\/button>\n      <button class=\"btn btn-primary\" onclick=\"addStage()\">Ajouter<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- MODAL: R\u00e9sultat -->\n<div class=\"modal-overlay\" id=\"resultModal\">\n  <div class=\"modal\">\n    <h2>Saisir un r\u00e9sultat<\/h2>\n    <div class=\"form-grid\">\n      <div class=\"form-group full\"><label>\u00c9tape<\/label><select id=\"resStage\"><\/select><\/div>\n      <div class=\"form-group full\"><label>Coureur<\/label><select id=\"resRider\"><\/select><\/div>\n      <div class=\"form-group\"><label>Temps (hh:mm:ss)<\/label><input type=\"text\" id=\"resTime\" placeholder=\"01:23:45\" \/><\/div>\n      <div class=\"form-group\"><label>Position finale<\/label><input type=\"number\" id=\"resPos\" placeholder=\"1\" min=\"1\" \/><\/div>\n      <div class=\"form-group\"><label>Points sprint<\/label><input type=\"number\" id=\"resPts\" placeholder=\"0\" min=\"0\" \/><\/div>\n      <div class=\"form-group\"><label>Points montagne<\/label><input type=\"number\" id=\"resMtn\" placeholder=\"0\" min=\"0\" \/><\/div>\n      <div class=\"form-group\"><label>Points combatif<\/label><input type=\"number\" id=\"resCbt\" placeholder=\"0\" min=\"0\" \/><\/div>\n    <\/div>\n    <div class=\"modal-actions\">\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('resultModal')\">Annuler<\/button>\n      <button class=\"btn btn-primary\" onclick=\"saveResult()\">Enregistrer<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  STORAGE \u2014 shared across all visitors\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nconst STORAGE_KEY = 'rouvy-tour-data';\nconst ADMIN_PW    = 'velo2025'; \/\/ \u2190 CHANGEZ CE MOT DE PASSE\n\nlet state = { riders: [], stages: [], results: [] };\nlet isAdmin = false;\nlet currentStageTab = null;\nlet currentMainTab  = 'etapes';\nlet currentGCTab    = 'gc-temps';\n\nasync function loadState() {\n  try {\n    const r = await window.storage.get(STORAGE_KEY, true);\n    if (r && r.value) state = JSON.parse(r.value);\n  } catch(e) { \/* first load *\/ }\n}\n\nasync function saveState() {\n  try {\n    await window.storage.set(STORAGE_KEY, JSON.stringify(state), true);\n  } catch(e) { console.error('Storage error', e); }\n  render();\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  HELPERS\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction uid() { return Math.random().toString(36).slice(2,9); }\nconst PTS = [25,20,16,13,11,10,9,8,7,6,5,4,3,2,1];\nfunction posPts(p){ return PTS[p-1]||0; }\n\nfunction t2s(t){\n  if(!t) return Infinity;\n  const p = t.split(':').map(Number);\n  if(p.length===3) return p[0]*3600+p[1]*60+p[2];\n  if(p.length===2) return p[0]*60+p[1];\n  return Infinity;\n}\nfunction s2t(s){\n  if(!isFinite(s)) return '\u2014';\n  const h=Math.floor(s\/3600), m=Math.floor((s%3600)\/60), sc=s%60;\n  return [h,m,sc].map(v=>String(v).padStart(2,'0')).join(':');\n}\nfunction gap(d){ return d===0?'leader':'+'+s2t(d); }\n\nfunction rankBadge(n){\n  const c=n===1?'rank-1':n===2?'rank-2':n===3?'rank-3':'';\n  return `<span class=\"rank ${c}\">${n}<\/span>`;\n}\n\nfunction emptyRow(cols, msg, icon='\ud83d\udccb'){\n  return `<tr><td colspan=\"${cols}\"><div class=\"empty-state\"><div class=\"empty-icon\">${icon}<\/div><p>${msg}<\/p><\/div><\/td><\/tr>`;\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  TABS\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction switchMain(name, btn){\n  currentMainTab = name;\n  ['etapes','general','admin'].forEach(t => {\n    document.getElementById('tab-'+t).style.display = t===name?'block':'none';\n  });\n  document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));\n  btn.classList.add('active');\n}\nfunction switchGC(name, btn){\n  currentGCTab = name;\n  ['gc-temps','gc-points','gc-montagne','gc-combatif'].forEach(t=>{\n    document.getElementById(t).style.display = t===name?'block':'none';\n  });\n  document.querySelectorAll('.gc-tab-btn').forEach(b=>b.classList.remove('active'));\n  btn.classList.add('active');\n}\nfunction switchStage(id){\n  currentStageTab = id;\n  document.querySelectorAll('#stagePanels .stage-panel').forEach(p=>p.style.display='none');\n  document.querySelectorAll('#stageTabs .tab-btn').forEach(b=>b.classList.remove('active'));\n  const panel = document.getElementById('sp-'+id);\n  const btn   = document.getElementById('stb-'+id);\n  if(panel) panel.style.display='block';\n  if(btn)   btn.classList.add('active');\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  ADMIN\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction checkAdmin(){\n  const pw = document.getElementById('adminPwInput').value;\n  if(pw === ADMIN_PW){\n    isAdmin = true;\n    document.getElementById('adminLock').style.display='none';\n    document.getElementById('adminPanel').style.display='block';\n    document.getElementById('btnAddResult').style.display='';\n    render();\n  } else {\n    document.getElementById('adminPwInput').value='';\n    document.getElementById('adminPwInput').placeholder='Mot de passe incorrect';\n  }\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  MODALS\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction openModal(id){\n  if(id==='resultModal'){\n    document.getElementById('resStage').innerHTML = state.stages.map(s=>`<option value=\"${s.id}\">${s.name}<\/option>`).join('') || '<option>Aucune \u00e9tape<\/option>';\n    document.getElementById('resRider').innerHTML = state.riders.map(r=>`<option value=\"${r.id}\">${r.flag} ${r.name}<\/option>`).join('') || '<option>Aucun participant<\/option>';\n  }\n  document.getElementById(id).classList.add('open');\n}\nfunction closeModal(id){ document.getElementById(id).classList.remove('open'); }\ndocument.querySelectorAll('.modal-overlay').forEach(m=>{\n  m.addEventListener('click',e=>{ if(e.target===m) m.classList.remove('open'); });\n});\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  CRUD\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nasync function addRider(){\n  const name = document.getElementById('riderName').value.trim();\n  const flag = document.getElementById('riderFlag').value.trim()||'\ud83d\udeb4';\n  if(!name) return alert('Entrez un nom.');\n  state.riders.push({id:uid(), name, flag});\n  document.getElementById('riderName').value='';\n  document.getElementById('riderFlag').value='';\n  closeModal('riderModal');\n  await saveState();\n}\n\nasync function removeRider(id){\n  if(!confirm('Supprimer ce participant et tous ses r\u00e9sultats ?')) return;\n  state.riders = state.riders.filter(r=>r.id!==id);\n  state.results = state.results.filter(r=>r.riderId!==id);\n  await saveState();\n}\n\nasync function addStage(){\n  const name = document.getElementById('stageName').value.trim();\n  const dist = document.getElementById('stageDist').value;\n  const elev = document.getElementById('stageElev').value;\n  const type = document.getElementById('stageType').value;\n  const date = document.getElementById('stageDate').value;\n  if(!name) return alert('Entrez un nom d\\'\u00e9tape.');\n  state.stages.push({id:uid(), name, dist, elev, type, date});\n  document.getElementById('stageName').value='';\n  document.getElementById('stageDist').value='';\n  document.getElementById('stageElev').value='';\n  document.getElementById('stageDate').value='';\n  closeModal('stageModal');\n  await saveState();\n}\n\nasync function removeStage(id){\n  if(!confirm('Supprimer cette \u00e9tape et tous ses r\u00e9sultats ?')) return;\n  state.stages = state.stages.filter(s=>s.id!==id);\n  state.results = state.results.filter(r=>r.stageId!==id);\n  await saveState();\n}\n\nasync function saveResult(){\n  const stageId = document.getElementById('resStage').value;\n  const riderId = document.getElementById('resRider').value;\n  const time    = document.getElementById('resTime').value.trim();\n  const pos     = parseInt(document.getElementById('resPos').value)||0;\n  const pts     = parseInt(document.getElementById('resPts').value)||posPts(pos);\n  const mtn     = parseInt(document.getElementById('resMtn').value)||0;\n  const cbt     = parseInt(document.getElementById('resCbt').value)||0;\n  if(!stageId||!riderId) return alert('S\u00e9lectionnez une \u00e9tape et un coureur.');\n  if(!time) return alert('Entrez un temps (hh:mm:ss).');\n  state.results = state.results.filter(r=>!(r.stageId===stageId&&r.riderId===riderId));\n  state.results.push({stageId, riderId, time, pos, pts, mtn, cbt});\n  ['resTime','resPos','resPts','resMtn','resCbt'].forEach(id=>{ document.getElementById(id).value=''; });\n  closeModal('resultModal');\n  await saveState();\n}\n\nasync function deleteResult(stageId, riderId){\n  if(!confirm('Supprimer ce r\u00e9sultat ?')) return;\n  state.results = state.results.filter(r=>!(r.stageId===stageId&&r.riderId===riderId));\n  await saveState();\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  RENDER\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nfunction render(){\n  renderHeader();\n  renderLeaders();\n  renderStageTabs();\n  renderGC();\n  if(isAdmin) renderAdmin();\n}\n\nfunction renderHeader(){\n  const n = state.stages.length;\n  document.getElementById('headerPill').textContent = n + ' \u00e9tape'+(n!==1?'s':'')+' enregistr\u00e9e'+(n!==1?'s':'');\n}\n\nfunction renderLeaders(){\n  const agg = buildAgg();\n  const riders = Object.values(agg).filter(a=>a.count>0);\n  if(!riders.length) return;\n  const topT = [...riders].sort((a,b)=>a.totalSec-b.totalSec)[0];\n  const topP = [...riders].sort((a,b)=>b.pts-a.pts)[0];\n  const topM = [...riders].sort((a,b)=>b.mtn-a.mtn)[0];\n  const topC = [...riders].sort((a,b)=>b.cbt-a.cbt)[0];\n  const set = (id, dd, rider, detail) => {\n    document.getElementById(id).textContent  = rider?`${rider.rider.flag} ${rider.rider.name}`:'\u2014';\n    document.getElementById(dd).textContent  = detail||'';\n  };\n  set('ldr-gc','ldr-gc-d', topT, topT?s2t(topT.totalSec):'Classement g\u00e9n\u00e9ral temps');\n  set('ldr-pts','ldr-pts-d', topP, topP?`${topP.pts} points`:'Classement par points');\n  set('ldr-mtn','ldr-mtn-d', topM, topM?`${topM.mtn} pts montagne`:'Classement montagne');\n  set('ldr-cbt','ldr-cbt-d', topC, topC?`${topC.cbt} pts combatif`:'Prix du combatif');\n}\n\nfunction renderStageTabs(){\n  const nav    = document.getElementById('stageTabs');\n  const panels = document.getElementById('stagePanels');\n  if(!state.stages.length){\n    nav.innerHTML    = '<div style=\"color:var(--muted);font-size:13px;padding:8px 0;\">Aucune \u00e9tape configur\u00e9e \u2014 connectez-vous en Admin pour en ajouter.<\/div>';\n    panels.innerHTML = '';\n    return;\n  }\n  \/\/ determine which tab is active\n  if(!currentStageTab || !state.stages.find(s=>s.id===currentStageTab)){\n    currentStageTab = state.stages[0].id;\n  }\n  nav.innerHTML = state.stages.map((s,i)=>{\n    const has = state.results.some(r=>r.stageId===s.id);\n    const active = s.id===currentStageTab?'active':'';\n    return `<button id=\"stb-${s.id}\" class=\"tab-btn ${active}\" onclick=\"switchStage('${s.id}')\">${has?'\u2713 ':''}\u00c9tape ${i+1}<\/button>`;\n  }).join('');\n\n  panels.innerHTML = state.stages.map((s,i)=>{\n    const sr = state.results.filter(r=>r.stageId===s.id).sort((a,b)=>t2s(a.time)-t2s(b.time));\n    const leader = sr[0];\n    const maxMtn = sr.length ? Math.max(...sr.map(r=>r.mtn)) : 0;\n    const maxCbt = sr.length ? Math.max(...sr.map(r=>r.cbt)) : 0;\n    const rows = sr.length ? sr.map((res,idx)=>{\n      const rider = state.riders.find(r=>r.id===res.riderId);\n      const d = idx===0?0:t2s(res.time)-t2s(leader.time);\n      return `<tr>\n        <td>${rankBadge(idx+1)}<\/td>\n        <td>\n          <span class=\"rider-flag\">${rider?.flag||'\ud83d\udeb4'}<\/span>\n          <span class=\"rider-name\">${rider?.name||'?'}<\/span>\n          ${idx===0?'<span class=\"badge badge-yellow\">Vainqueur<\/span>':''}\n          ${res.mtn===maxMtn&&res.mtn>0?'<span class=\"badge badge-red\">\u26f0<\/span>':''}\n          ${res.cbt===maxCbt&&res.cbt>0?'<span class=\"badge badge-purple\">\u26a1<\/span>':''}\n        <\/td>\n        <td><div class=\"time-val\">${res.time}<\/div>${idx>0?`<div class=\"time-gap\">${gap(d)}<\/div>`:''}<\/td>\n        <td><span class=\"pts-val pts-points\">${res.pts}<\/span><\/td>\n        <td><span class=\"pts-val pts-mtn\">${res.mtn||0}<\/span><\/td>\n        <td><span class=\"pts-val pts-cbt\">${res.cbt||0}<\/span><\/td>\n      <\/tr>`;\n    }).join('') : emptyRow(6,'Aucun r\u00e9sultat saisi');\n\n    return `<div id=\"sp-${s.id}\" class=\"stage-panel\" style=\"display:${s.id===currentStageTab?'block':'none'}\">\n      <div class=\"stage-info-bar\">\n        <div class=\"stage-info-item\"><div class=\"stage-info-label\">\u00c9tape ${i+1}<\/div><div class=\"stage-info-val accent\">${s.name}<\/div><\/div>\n        ${s.dist?`<div class=\"stage-info-item\"><div class=\"stage-info-label\">Distance<\/div><div class=\"stage-info-val\">${s.dist} km<\/div><\/div>`:''}\n        ${s.elev?`<div class=\"stage-info-item\"><div class=\"stage-info-label\">D\u00e9nivel\u00e9<\/div><div class=\"stage-info-val\">${s.elev} m<\/div><\/div>`:''}\n        ${s.type?`<div class=\"stage-info-item\"><div class=\"stage-info-label\">Type<\/div><div class=\"stage-info-val\">${s.type}<\/div><\/div>`:''}\n        ${s.date?`<div class=\"stage-info-item\"><div class=\"stage-info-label\">Date<\/div><div class=\"stage-info-val\">${s.date}<\/div><\/div>`:''}\n        <div class=\"stage-info-item\" style=\"margin-left:auto;\"><div class=\"stage-info-label\">Partants<\/div><div class=\"stage-info-val\">${sr.length}\/${state.riders.length}<\/div><\/div>\n      <\/div>\n      <div class=\"table-wrap\">\n        <table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Temps<\/th><th>Points \ud83c\udfc5<\/th><th>Montagne \u26f0<\/th><th>Combatif \u26a1<\/th><\/tr><\/thead>\n        <tbody>${rows}<\/tbody><\/table>\n      <\/div>\n    <\/div>`;\n  }).join('');\n}\n\nfunction buildAgg(){\n  const agg = {};\n  state.riders.forEach(r=>{ agg[r.id]={rider:r,totalSec:0,pts:0,mtn:0,cbt:0,wins:0,count:0}; });\n  state.stages.forEach(stage=>{\n    const sr = state.results.filter(r=>r.stageId===stage.id).sort((a,b)=>t2s(a.time)-t2s(b.time));\n    sr.forEach((res,idx)=>{\n      if(!agg[res.riderId]) return;\n      agg[res.riderId].totalSec+=t2s(res.time);\n      agg[res.riderId].pts+=res.pts||0;\n      agg[res.riderId].mtn+=res.mtn||0;\n      agg[res.riderId].cbt+=res.cbt||0;\n      agg[res.riderId].count++;\n      if(idx===0) agg[res.riderId].wins++;\n    });\n  });\n  return agg;\n}\n\nfunction renderGC(){\n  const agg    = buildAgg();\n  const riders = Object.values(agg).filter(a=>a.count>0);\n\n  const byTime = [...riders].sort((a,b)=>a.totalSec-b.totalSec);\n  const ls = byTime[0]?.totalSec||0;\n  document.getElementById('tb-temps').innerHTML = byTime.length\n    ? byTime.map((a,i)=>`<tr>\n        <td>${rankBadge(i+1)}<\/td>\n        <td><span class=\"rider-flag\">${a.rider.flag}<\/span><span class=\"rider-name\">${a.rider.name}<\/span>${i===0?'<span class=\"badge badge-yellow\">Maillot Jaune<\/span>':''}<\/td>\n        <td><div class=\"time-val\">${s2t(a.totalSec)}<\/div>${i>0?`<div class=\"time-gap\">${gap(a.totalSec-ls)}<\/div>`:''}<\/td>\n        <td style=\"color:var(--muted)\">${a.wins} victoire${a.wins!==1?'s':''}<\/td><\/tr>`).join('')\n    : emptyRow(4,'Aucun r\u00e9sultat disponible','\u23f3');\n\n  const byPts = [...riders].sort((a,b)=>b.pts-a.pts);\n  document.getElementById('tb-pts').innerHTML = byPts.length\n    ? byPts.map((a,i)=>`<tr>\n        <td>${rankBadge(i+1)}<\/td>\n        <td><span class=\"rider-flag\">${a.rider.flag}<\/span><span class=\"rider-name\">${a.rider.name}<\/span>${i===0?'<span class=\"badge badge-green\">Maillot Vert<\/span>':''}<\/td>\n        <td><span class=\"pts-val pts-points\">${a.pts} pts<\/span><\/td>\n        <td style=\"color:var(--muted)\">${a.wins} victoire${a.wins!==1?'s':''}<\/td><\/tr>`).join('')\n    : emptyRow(4,'Aucun r\u00e9sultat disponible','\u23f3');\n\n  const byMtn = [...riders].sort((a,b)=>b.mtn-a.mtn);\n  document.getElementById('tb-mtn').innerHTML = byMtn.length\n    ? byMtn.map((a,i)=>`<tr>\n        <td>${rankBadge(i+1)}<\/td>\n        <td><span class=\"rider-flag\">${a.rider.flag}<\/span><span class=\"rider-name\">${a.rider.name}<\/span>${i===0?'<span class=\"badge badge-red\">Maillot \u00e0 Pois<\/span>':''}<\/td>\n        <td><span class=\"pts-val pts-mtn\">${a.mtn} pts<\/span><\/td><\/tr>`).join('')\n    : emptyRow(3,'Aucun r\u00e9sultat disponible','\u23f3');\n\n  const byCbt = [...riders].sort((a,b)=>b.cbt-a.cbt);\n  document.getElementById('tb-cbt').innerHTML = byCbt.length\n    ? byCbt.map((a,i)=>`<tr>\n        <td>${rankBadge(i+1)}<\/td>\n        <td><span class=\"rider-flag\">${a.rider.flag}<\/span><span class=\"rider-name\">${a.rider.name}<\/span>${i===0?'<span class=\"badge badge-purple\">Combatif<\/span>':''}<\/td>\n        <td><span class=\"pts-val pts-cbt\">${a.cbt} pts<\/span><\/td><\/tr>`).join('')\n    : emptyRow(3,'Aucun r\u00e9sultat disponible','\u23f3');\n}\n\nfunction renderAdmin(){\n  \/\/ riders\n  const rl = document.getElementById('riderListAdmin');\n  rl.innerHTML = state.riders.length\n    ? state.riders.map(r=>`<div style=\"display:flex;align-items:center;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 14px;margin-bottom:8px;gap:10px;\">\n        <span style=\"font-size:18px\">${r.flag}<\/span>\n        <span style=\"flex:1;font-weight:500\">${r.name}<\/span>\n        <button class=\"btn btn-danger\" style=\"padding:4px 10px;font-size:11px\" onclick=\"removeRider('${r.id}')\">Supprimer<\/button>\n      <\/div>`).join('')\n    : '<div style=\"color:var(--muted);font-size:13px\">Aucun participant.<\/div>';\n\n  \/\/ stages\n  const sl = document.getElementById('stageListAdmin');\n  sl.innerHTML = state.stages.length\n    ? state.stages.map((s,i)=>`<div style=\"display:flex;align-items:center;flex-wrap:wrap;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 14px;margin-bottom:8px;\">\n        <span style=\"font-family:'Barlow Condensed',sans-serif;font-size:12px;font-weight:700;color:var(--yellow);text-transform:uppercase;letter-spacing:.1em\">\u00c9tape ${i+1}<\/span>\n        <span style=\"flex:1;font-weight:600\">${s.name}<\/span>\n        ${s.dist?`<span style=\"color:var(--muted);font-size:12px\">${s.dist} km<\/span>`:''}\n        ${s.elev?`<span style=\"color:var(--muted);font-size:12px\">\u26f0 ${s.elev} m<\/span>`:''}\n        <button class=\"btn btn-danger\" style=\"padding:4px 10px;font-size:11px\" onclick=\"removeStage('${s.id}')\">Supprimer<\/button>\n      <\/div>`).join('')\n    : '<div style=\"color:var(--muted);font-size:13px\">Aucune \u00e9tape.<\/div>';\n\n  \/\/ results\n  const resEl = document.getElementById('resultListAdmin');\n  if(!state.results.length){\n    resEl.innerHTML = '<div style=\"color:var(--muted);font-size:13px\">Aucun r\u00e9sultat saisi.<\/div>';\n    return;\n  }\n  resEl.innerHTML = state.stages.map(stage=>{\n    const sr = state.results.filter(r=>r.stageId===stage.id).sort((a,b)=>t2s(a.time)-t2s(b.time));\n    if(!sr.length) return '';\n    const rows = sr.map((res,idx)=>{\n      const rider = state.riders.find(r=>r.id===res.riderId);\n      return `<tr>\n        <td>${rankBadge(idx+1)}<\/td>\n        <td>${rider?.flag||'\ud83d\udeb4'} ${rider?.name||'?'}<\/td>\n        <td class=\"time-val\">${res.time}<\/td>\n        <td class=\"pts-val pts-points\">${res.pts}<\/td>\n        <td class=\"pts-val pts-mtn\">${res.mtn}<\/td>\n        <td class=\"pts-val pts-cbt\">${res.cbt}<\/td>\n        <td><button class=\"btn btn-danger\" style=\"padding:3px 8px;font-size:11px\" onclick=\"deleteResult('${res.stageId}','${res.riderId}')\">\u2715<\/button><\/td>\n      <\/tr>`;\n    }).join('');\n    return `<div style=\"margin-bottom:16px\">\n      <div style=\"font-family:'Barlow Condensed',sans-serif;font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--yellow);margin-bottom:8px\">${stage.name}<\/div>\n      <div class=\"table-wrap\"><table><thead><tr><th>#<\/th><th>Coureur<\/th><th>Temps<\/th><th>Pts<\/th><th>\u26f0<\/th><th>\u26a1<\/th><th><\/th><\/tr><\/thead><tbody>${rows}<\/tbody><\/table><\/div>\n    <\/div>`;\n  }).join('');\n}\n\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\/\/  INIT + AUTO-REFRESH\n\/\/ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n(async function init(){\n  await loadState();\n  render();\n  document.getElementById('loadingOverlay').style.display='none';\n  \/\/ Refresh toutes les 30s pour les visiteurs\n  setInterval(async ()=>{\n    await loadState();\n    render();\n  }, 30000);\n})();\n<\/script>\n<\/body>\n<\/html>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Classement Le Tour des Amis \u2014 Rouvy Chargement du tour\u2026 Rouvy \u00b7 Tour Virtuel Le Tourdes Amis \u2026 Leaders actuels \ud83d\udfe1 Maillot Jaune \u2014 Classement g\u00e9n\u00e9ral temps \ud83d\udfe2 Maillot Vert \u2014 Classement par points \ud83d\udd34 Maillot \u00e0 Pois \u2014 Classement montagne \ud83d\udfe3 Combatif \u2014 Prix du combatif \ud83d\udccd \u00c9tapes \ud83c\udfc6 Classement G\u00e9n\u00e9ral \u2699\ufe0f Admin R\u00e9sultats &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.zerolimit.best\/index.php\/tour-de-rouvy\/\" class=\"more-link\">Lire la suite de<span class=\"screen-reader-text\">\u00ab\u00a0Tour de Rouvy\u00a0\u00bb<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_tribe_ticket_capacity":"0","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":"","_tec_slr_enabled":"","_tec_slr_layout":""},"class_list":["post-600","page","type-page","status-publish","hentry"],"ticketed":false,"_links":{"self":[{"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/pages\/600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/comments?post=600"}],"version-history":[{"count":2,"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/pages\/600\/revisions"}],"predecessor-version":[{"id":604,"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/pages\/600\/revisions\/604"}],"wp:attachment":[{"href":"https:\/\/www.zerolimit.best\/index.php\/wp-json\/wp\/v2\/media?parent=600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}