// --- MODE SELECTOR (Solo / Multi) ---
console.log('groups-ui.js chargé !');

// selectMode et enterMultiMode sont maintenant définis dans script.js pour être disponibles immédiatement

function renderGroupsNotLoggedIn() {
  const page = document.getElementById('groupsPage');
  if (!page) return;
  showMultiPage('groupsPage');
  page.innerHTML = `
    <div class="card" style="text-align:center;padding:40px 20px">
      <div style="font-size:3rem;margin-bottom:16px">👥</div>
      <h2>Groupes</h2>
      <p class="muted">Connecte-toi pour acceder aux groupes.</p>
      <button class="btn primary" style="margin-top:16px" onclick="openLoginFromPanel()">Se connecter</button>
    </div>`;
}
window.renderGroupsNotLoggedIn = renderGroupsNotLoggedIn;

function showMultiPage(id) {
  document.querySelectorAll('#multiApp .page').forEach(p => p.classList.remove('active'));
  const el = document.getElementById(id);
  if (el) el.classList.add('active');
}

// --- STATE GROUPES ---
const groupState = {
  groups: [],
  currentGroup: null,
  currentChallenge: null,
  chatLastId: 0,
  chatPollTimer: null,
  feedPollTimer: null,
  challengeQuiz: null,
};

// --- LABELS CATEGORIES ---
const CAT_LABELS = {
  rap: "Rap expert",
  history: "Histoire",
  christian: "Chrétienté",
  insolite: "Insolite",
  mythologie: "Mythologie",
  geopo: "Géopolitique",
  science: "Sciences",
  etymologie: "Étymologie",
  films_facile: "Films - facile",
  films_difficile: "Films - difficile",
  paris: "Paris",
  alcool: "Alcools",
  ortho_choix: "Orthographe QCM",
  ortho_faute_f: "Fautes faciles",
  ortho_faute_d: "Fautes difficiles"
};

async function loadGroups() {
  try {
    const data = await api.request('/groups.php?action=list');
    groupState.groups = data.groups || [];
  } catch (e) {}
}
window.loadGroups = loadGroups;

// --- NAVIGATION ---
function showGroupsPage() {
  stopPolling();
  showMultiPage('groupsPage');
  if (!api.getToken()) {
    renderGroupsNotLoggedIn();
    return;
  }
  loadGroups().then(() => renderGroupsList());
}

function showGroupDetail(groupId) {
  stopPolling();
  const group = groupState.groups.find(g => g.id == groupId);
  if (!group) return;
  groupState.currentGroup = group;
  showMultiPage('groupDetailPage');
  renderGroupDetail(group);
  startChatPolling(groupId);
  startFeedPolling(groupId);
}

function backToGroups() {
  stopPolling();
  showGroupsPage();
}

// --- RENDER LISTE GROUPES ---
function renderGroupsList() {
  const page = document.getElementById('groupsPage');
  const groups = groupState.groups;

  const cards = groups.map(g => {
    const members = g.members || [];
    const avatars = members.slice(0, 5).map(m => memberAvatarHtml(m.username)).join('');
    const myDaily = g.my_daily;
    const dailyDot = myDaily
      ? `<span class="g-daily-dot ${myDaily.correct ? 'correct' : 'wrong'}"></span>`
      : `<span class="g-daily-dot pending"></span>`;
    const memberCount = g.member_count || members.length;
    return `
    <div class="g-card" onclick="showGroupDetail(${g.id})">
      <div class="g-card-top">
        <div>
          <div class="g-card-name">${escHtml(g.name)}</div>
          <div class="g-card-meta">${members.length} membre${members.length > 1 ? 's' : ''} · code <b>${g.code}</b></div>
        </div>
        <div style="display:flex;align-items:center;gap:8px">
          ${dailyDot}
          <div class="g-answered-badge">${g.daily_answered_count || 0}/${memberCount} aujourd'hui</div>
        </div>
      </div>
      <div class="g-avatars">${avatars}</div>
    </div>`;
  }).join('');

  page.innerHTML = `
    <div class="g-header">
      <h2 style="margin:0">👥 Groupes</h2>
    </div>
    <button class="g-create-btn" onclick="showCreateGroup()">CRÉER GROUPE</button>
    <button class="g-join-btn" onclick="showJoinGroup()">Rejoindre un groupe</button>
    ${groups.length === 0 ? `
      <div class="card" style="text-align:center;padding:32px 20px;margin-top:12px">
        <div style="font-size:2.5rem;margin-bottom:12px">🫂</div>
        <p class="muted">Tu n'es dans aucun groupe pour l'instant.</p>
      </div>` : cards}
  `;
}
window.renderGroupsList = renderGroupsList;

// --- RENDER DETAIL GROUPE ---
function renderGroupDetail(group) {
  const page = document.getElementById('groupDetailPage');
  const members = group.members || [];
  const challenges = group.challenges || [];
  const answeredToday = group.daily_answered_count || 0;
  const memberCount = group.member_count || members.length;
  const playedCats = challenges.map(c => c.category);
  const availableCats = Object.keys(CAT_LABELS).filter(c => !playedCats.includes(c));

  const challengeCards = challenges.map(c => {
    const done = c.my_finished_at !== null;
    const resultsCount = parseInt(c.results_count) || 0;
    const pct = memberCount > 0 ? Math.round((resultsCount / memberCount) * 100) : 0;
    return `
    <div class="g-challenge-card ${done ? 'done' : 'todo'}" onclick="openChallenge(${c.id})">
      <div style="display:flex;justify-content:space-between;align-items:center">
        <div>
          <div class="g-challenge-cat">${CAT_LABELS[c.category] || c.category}</div>
          <div class="g-challenge-meta">${resultsCount}/${memberCount} ont joué</div>
        </div>
        <div style="text-align:right">
          ${done ? `<div class="g-score-badge">Score : ${c.my_score}</div>` : '<div class="g-todo-badge">À jouer →</div>'}
        </div>
      </div>
      <div class="g-progress-bar"><span style="width:${pct}%"></span></div>
    </div>`;
  }).join('');

  page.innerHTML = `
    <div class="g-detail-header">
      <button class="btn small ghost" onclick="backToGroups()">← Retour</button>
      <div class="g-detail-title">${escHtml(group.name)}</div>
      <button class="btn small ghost" onclick="showGroupOptions(${group.id})" title="Options">⚙️</button>
    </div>
    <div class="g-members-row">
      ${members.map(m => `<div class="g-member-chip">${memberAvatarHtml(m.username, 'lg')}<span>${escHtml(m.username)}</span></div>`).join('')}
    </div>
    <div class="g-section-title">${miniIcon('daily')}Question du jour <span class="g-daily-count">${answeredToday}/${memberCount}</span></div>
    <div id="groupDailyWrap"></div>
    <div class="g-section-title" style="margin-top:18px">
      ⚔️ Défis catégories
      ${availableCats.length > 0 ? `<button class="btn small primary" style="margin-left:auto" onclick="showNewChallenge(${group.id})">+ Lancer</button>` : ''}
    </div>
    ${challenges.length === 0 ? '<p class="muted" style="text-align:center;padding:12px">Aucun défi lancé pour l\'instant.</p>' : challengeCards}
    <div class="g-section-title" style="margin-top:18px">${miniIcon('play')}Jeux de la semaine</div>
    <div id="groupGamesWrap"><p class="muted" style="text-align:center;padding:8px">Chargement...</p></div>

    <div class="g-section-title" style="margin-top:18px">${miniIcon('review')}Chat du groupe</div>
    <div class="g-chat-wrap">
      <div class="g-chat-header">
        <span class="g-chat-header-dot"></span>
        Chat en direct
        <span style="margin-left:auto;font-size:.75rem;opacity:.5">Partagez vos réactions</span>
      </div>
      <div id="groupChatMessages" class="g-chat-messages"></div>
      <div class="g-chat-input-row">
        <div class="g-reactions-bar" id="groupReactionsBar">
          ${['👏','🔥','😂','🤯','💀','🎉','😤','❤️'].map(e =>
            `<button class="g-reaction-btn" onclick="sendReaction('${e}')" title="${e}">${reactionIconHtml(e)}</button>`
          ).join('')}
        </div>
        <div class="g-chat-input-wrap">
          <input id="groupChatInput" class="g-chat-input" type="text" placeholder="Ecris un message..." maxlength="500"
            onkeydown="if(event.key==='Enter')sendChatMessage()" />
          <button class="g-chat-send-btn" onclick="sendChatMessage()" title="Envoyer">→</button>
        </div>
      </div>
    </div>
  `;
  loadDailyQuestion(group.id);
  loadWeekGames(group.id);
}

// --- QUESTION DU JOUR ---
async function loadDailyQuestion(groupId) {
  const wrap = document.getElementById('groupDailyWrap');
  if (!wrap) return;
  wrap.innerHTML = '<p class="muted" style="text-align:center;padding:12px">Chargement...</p>';
  try {
    const data = await api.request(`/group_daily.php?action=question&group_id=${groupId}`);
    renderDailyQuestion(groupId, data);
  } catch (e) {
    wrap.innerHTML = '<p class="muted" style="text-align:center">Erreur de chargement.</p>';
  }
}

function renderDailyQuestion(groupId, data) {
  const wrap = document.getElementById('groupDailyWrap');
  if (!wrap) return;
  const q = data.question;
  const myAnswer = data.my_answer;
  const answered = !!myAnswer;

  let optionsHtml = '';
  if (!answered) {
    optionsHtml = (q.options || []).map((opt, i) => {
      return `<button class="g-daily-option" data-answer="${escHtml(opt).replace(/"/g,'&quot;')}" data-group="${groupId}" onclick="answerDailyBtn(this)">${escHtml(opt)}</button>`;
    }).join('');
  }

  let feedHtml = '';
  if (answered && data.feed && data.feed.length > 0) {
    feedHtml = renderDailyFeed(data.feed, q.answer);
  }

  wrap.innerHTML = `
    <div class="g-daily-card">
      <div class="g-daily-q">${escHtml(q.question)}</div>
      ${!answered ? `<div class="g-daily-options">${optionsHtml}</div>` : ''}
      ${answered ? `
        <div class="g-my-answer ${myAnswer.correct ? 'correct' : 'wrong'}">
          Ta réponse : <b>${escHtml(myAnswer.answer)}</b>
          ${myAnswer.correct ? ' ✅' : ` ❌ → ${escHtml(q.answer)}`}
        </div>` : ''}
      ${answered ? `<div id="dailyFeedWrap">${feedHtml}</div>` : ''}
    </div>
  `;
}

function renderDailyFeed(feed, correctAnswer) {
  if (!feed || feed.length === 0) return '<p class="muted" style="padding:8px 0">Aucune réponse encore.</p>';
  return `
    <div class="g-daily-feed">
      <div class="g-feed-title">Réponses du groupe :</div>
      ${feed.map(f => `
        <div class="g-feed-item ${f.correct ? 'correct' : 'wrong'}">
          ${memberAvatarHtml(f.username, 'g-feed-avatar')}
          <span class="g-feed-name">${escHtml(f.username)}</span>
          <span class="g-feed-answer">${escHtml(f.answer)}</span>
          <span class="g-feed-icon">${f.correct ? '✅' : '❌'}</span>
        </div>`).join('')}
    </div>`;
}

async function answerGroupDaily(groupId, answer) {
  document.querySelectorAll('.g-daily-option').forEach(b => b.disabled = true);
  try {
    const data = await api.request('/group_daily.php?action=answer', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupId, answer: answer })
    });
    setTimeout(() => loadDailyQuestion(groupId), 300);
  } catch(e) {
    document.querySelectorAll('.g-daily-option').forEach(b => b.disabled = false);
    alert(e.message || 'Erreur');
  }
}

// Wrapper appelé depuis data-answer (évite les problèmes d'apostrophes dans onclick)
function answerDailyBtn(btn) {
  const answer = btn.getAttribute('data-answer');
  const groupId = parseInt(btn.getAttribute('data-group'), 10);
  answerGroupDaily(groupId, answer);
}



// --- LONG POLLING FEED ---
function startFeedPolling(groupId) {
  stopFeedPolling();
  let lastTs = '';
  async function poll() {
    if (!document.getElementById('dailyFeedWrap')) return;
    try {
      const url = `/group_daily.php?action=poll&group_id=${groupId}` + (lastTs ? `&since=${lastTs}` : '');
      const data = await api.request(url);
      if (data.feed && data.feed.length > 0) {
        lastTs = encodeURIComponent(data.feed[data.feed.length - 1].answered_at);
        const qData = await api.request(`/group_daily.php?action=question&group_id=${groupId}`);
        const feedWrap = document.getElementById('dailyFeedWrap');
        if (feedWrap) feedWrap.innerHTML = renderDailyFeed(data.feed, qData.question.answer);
      }
    } catch(e) {}
    groupState.feedPollTimer = setTimeout(poll, 5000);
  }
  groupState.feedPollTimer = setTimeout(poll, 5000);
}

function stopFeedPolling() {
  if (groupState.feedPollTimer) { clearTimeout(groupState.feedPollTimer); groupState.feedPollTimer = null; }
}

// --- CHAT ---
function startChatPolling(groupId) {
  stopChatPolling();
  loadChatHistory(groupId);
}

async function loadChatHistory(groupId) {
  try {
    const data = await api.request(`/group_chat.php?action=poll&group_id=${groupId}&last_id=0`);
    const msgs = data.messages || [];
    if (msgs.length > 0) {
      groupState.chatLastId = msgs[msgs.length - 1].id;
      appendChatMessages(msgs);
    }
    pollChat(groupId);
  } catch(e) { pollChat(groupId); }
}

async function pollChat(groupId) {
  if (!document.getElementById('groupChatMessages')) return;
  try {
    const data = await api.request(`/group_chat.php?action=poll&group_id=${groupId}&last_id=${groupState.chatLastId}`);
    const msgs = data.messages || [];
    if (msgs.length > 0) {
      groupState.chatLastId = msgs[msgs.length - 1].id;
      appendChatMessages(msgs);
    }
  } catch(e) {}
  groupState.chatPollTimer = setTimeout(() => pollChat(groupId), 3000);
}

function stopChatPolling() {
  if (groupState.chatPollTimer) { clearTimeout(groupState.chatPollTimer); groupState.chatPollTimer = null; }
}

function stopPolling() {
  stopChatPolling();
  stopFeedPolling();
}

function appendChatMessages(msgs) {
  const container = document.getElementById('groupChatMessages');
  if (!container) return;
  const myUser = localStorage.getItem('tq_user') || '';
  const myAvatar = getSavedGroupAvatar();
  const wasAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 40;
  msgs.forEach(msg => {
    const isMe = msg.username === myUser;
    const avatar = isMe && typeof window.avatarIconHtml === 'function'
      ? window.avatarIconHtml(myAvatar, 'avatar-mark-chat')
      : `<span class="avatar-initials">${escHtml(String(msg.username || '?').slice(0, 2).toUpperCase() || '?')}</span>`;
    const avatarAttr = isMe ? ` data-avatar-user="${escDataAttr(msg.username)}"` : '';
    const el = document.createElement('div');
    if (msg.type === 'reaction') {
      el.className = 'g-chat-reaction';
      el.innerHTML = `<span class="g-reaction-emoji">${reactionIconHtml(msg.content)}</span><span class="g-reaction-user"><span${avatarAttr}>${avatar}</span> ${escHtml(msg.username)}</span>`;
    } else {
      el.className = `g-chat-msg ${isMe ? 'me' : 'other'}`;
      el.innerHTML = `
        ${!isMe ? `<span class="g-msg-avatar">${avatar}</span>` : ''}
        ${!isMe ? `<span class="g-msg-user">${escHtml(msg.username)}</span>` : ''}
        <span class="g-msg-bubble">${escHtml(msg.content)}</span>
        ${isMe ? `<span class="g-msg-avatar"${avatarAttr}>${avatar}</span>` : ''}`;
    }
    container.appendChild(el);
  });
  if (wasAtBottom) container.scrollTop = container.scrollHeight;
}

const REACTION_ICON_KEYS = {
  '👏': 'clap',
  '🔥': 'flame',
  '😂': 'laugh',
  '🤯': 'mind',
  '💀': 'skull',
  '🎉': 'party',
  '😤': 'steam',
  '❤️': 'heart'
};

function reactionIconHtml(value) {
  const key = REACTION_ICON_KEYS[value] || 'spark';
  const src = {
    clap: GROUP_ICON_ASSETS.review,
    flame: GROUP_ICON_ASSETS.rap || 'assets/icons/generated/rap.png',
    laugh: GROUP_ICON_ASSETS.popcorn,
    mind: GROUP_ICON_ASSETS.quiz,
    skull: GROUP_ICON_ASSETS.mythologie || 'assets/icons/generated/mythologie.png',
    party: GROUP_ICON_ASSETS.popcorn,
    steam: GROUP_ICON_ASSETS.science || 'assets/icons/generated/science.png',
    heart: GROUP_ICON_ASSETS.review,
    spark: GROUP_ICON_ASSETS.quiz
  }[key] || GROUP_ICON_ASSETS.quiz;
  return iconImg(src, `reaction-mark reaction-${key}`);
}

async function sendChatMessage() {
  const input = document.getElementById('groupChatInput');
  const content = input ? input.value.trim() : '';
  if (!content || !groupState.currentGroup) return;
  input.value = '';
  try {
    await api.request('/group_chat.php?action=send', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupState.currentGroup.id, type: 'text', content })
    });
  } catch(e) { input.value = content; }
}

async function sendReaction(emoji) {
  if (!groupState.currentGroup) return;
  try {
    await api.request('/group_chat.php?action=send', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupState.currentGroup.id, type: 'reaction', content: emoji })
    });
  } catch(e) {}
}

// --- CREER GROUPE ---
async function showCreateGroup() {
  const name = await customPrompt('Nom du groupe (2-50 caracteres) :', '', 'Créer un groupe');
  if (!name || name.trim().length < 2) return;
  createGroup(name.trim());
}

async function createGroup(name) {
  try {
    const data = await api.request('/groups.php?action=create', {
      method: 'POST',
      body: JSON.stringify({ name })
    });
    groupState.groups.unshift(data.group);
    renderGroupsList();
    await customAlert(`Groupe "${data.group.name}" créé !\n\nCode d'invitation : ${data.group.code}`, 'Groupe créé', '🎉');
  } catch(e) { 
    await customAlert(e.message || 'Erreur lors de la création', 'Erreur', '❌'); 
  }
}

// --- REJOINDRE GROUPE ---
async function showJoinGroup() {
  const code = await customPrompt('Entre le code du groupe (6 caracteres) :', '', 'Rejoindre un groupe');
  if (!code || code.trim().length < 4) return;
  joinGroup(code.trim().toUpperCase());
}

async function joinGroup(code) {
  try {
    const data = await api.request('/groups.php?action=join', {
      method: 'POST',
      body: JSON.stringify({ code })
    });
    groupState.groups.unshift(data.group);
    renderGroupsList();
    showGroupDetail(data.group.id);
  } catch(e) { 
    await customAlert(e.message || 'Code invalide', 'Erreur', '❌'); 
  }
}

// --- OPTIONS GROUPE ---
async function showGroupOptions(groupId) {
  const group = groupState.groups.find(g => g.id == groupId);
  if (!group) return;
  const choice = await customConfirm(`Veux-tu quitter le groupe "${group.name}" ?\n\nCode : ${group.code}`, 'Options du groupe');
  if (choice) leaveGroup(groupId);
}

async function leaveGroup(groupId) {
  try {
    await api.request('/groups.php?action=leave', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupId })
    });
    groupState.groups = groupState.groups.filter(g => g.id != groupId);
    backToGroups();
  } catch(e) { 
    await customAlert(e.message || 'Erreur', 'Erreur', '❌'); 
  }
}

// --- LANCER UN DEFI ---
async function showNewChallenge(groupId) {
  const group = groupState.groups.find(g => g.id == groupId);
  if (!group) return;
  const playedCats = (group.challenges || []).map(c => c.category);
  const available = Object.entries(CAT_LABELS).filter(([k]) => !playedCats.includes(k));
  if (available.length === 0) {
    await customAlert('Toutes les catégories ont déjà été jouées dans ce groupe !', 'Info', 'ℹ️');
    return;
  }
  const overlay = document.createElement('div');
  overlay.className = 'g-modal-overlay';
  overlay.innerHTML = `
    <div class="g-modal">
      <h3 style="margin:0 0 16px">Choisir une catégorie</h3>
      <div class="g-cat-list">
        ${available.map(([k, label]) => `
          <button class="g-cat-choice" onclick="launchChallenge(${groupId},'${k}',this.closest('.g-modal-overlay'))">${label}</button>
        `).join('')}
      </div>
      <button class="btn ghost" style="width:100%;margin-top:12px" onclick="this.closest('.g-modal-overlay').remove()">Annuler</button>
    </div>`;
  document.body.appendChild(overlay);
}

async function launchChallenge(groupId, category, overlay) {
  if (overlay) overlay.remove();
  if (!QUIZ_DATA || !QUIZ_DATA[category]) { 
    await customAlert('Catégorie introuvable', 'Erreur', '❌'); 
    return; 
  }
  const pool = [...QUIZ_DATA[category]];
  const shuffled = pool.sort(() => Math.random() - 0.5).slice(0, 15);
  const questions = shuffled.map(q => ({
    question: q.question,
    options: q.options || [],
    answer: q.answer,
    explanation: q.explanation || '',
    difficulty: q.difficulty || 'standard'
  }));
  try {
    const data = await api.request('/groups.php?action=create_challenge', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupId, category, questions })
    });
    const res = await api.request('/groups.php?action=list');
    groupState.groups = res.groups || [];
    const updated = groupState.groups.find(g => g.id == groupId);
    if (updated) {
      groupState.currentGroup = updated;
      renderGroupDetail(updated);
      startChatPolling(groupId);
      startFeedPolling(groupId);
    }
    openChallenge(data.challenge_id);
  } catch(e) { 
    await customAlert(e.message || 'Erreur lors de la création du défi', 'Erreur', '❌'); 
  }
}

// --- JOUER UN DEFI ---
async function openChallenge(challengeId) {
  try {
    const data = await api.request(`/groups.php?action=get_challenge&challenge_id=${challengeId}`);
    const challenge = data.challenge;
    if (challenge.my_result) {
      showChallengeResults(challenge);
    } else {
      startChallengeQuiz(challenge);
    }
  } catch(e) { 
    await customAlert(e.message || 'Erreur', 'Erreur', '❌'); 
  }
}

function startChallengeQuiz(challenge) {
  stopPolling();
  groupState.challengeQuiz = {
    challenge, questions: challenge.questions,
    index: 0, score: 0, good: 0, bad: 0, answers: [], answered: false
  };
  showMultiPage('challengePlayPage');
  renderChallengeQuestion();
}

function renderChallengeQuestion() {
  const qz = groupState.challengeQuiz;
  if (!qz) return;
  const page = document.getElementById('challengePlayPage');
  const q = qz.questions[qz.index];
  const total = qz.questions.length;
  const pct = Math.round((qz.index / total) * 100);
  const optionsHtml = (q.options || []).map(opt =>
    `<button class="option-btn" onclick="answerChallenge('${escAttr(opt)}')">${escHtml(opt)}</button>`
  ).join('');
  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card">
          <div class="between">
            <button class="btn small ghost" onclick="abandonChallenge()">✕ Abandonner</button>
            <div class="badge">${CAT_LABELS[qz.challenge.category] || qz.challenge.category}</div>
          </div>
          <div class="meta" style="margin-top:12px">
            <div class="badge">Question ${qz.index + 1}/${total}</div>
            <div class="badge">Score ${qz.score}</div>
          </div>
          <div class="progress"><span id="cProgressBar" style="width:${pct}%"></span></div>
          <div class="question" id="cQuestion">${escHtml(q.question)}</div>
          <div class="answers" id="cAnswers">${optionsHtml}</div>
          <div class="feedback" id="cFeedback"></div>
          <button class="btn primary" id="cNextBtn" style="display:none;margin-top:12px" onclick="nextChallengeQ()">
            ${qz.index < total - 1 ? 'Question suivante →' : 'Voir les résultats 🏆'}
          </button>
        </div>
      </div>
    </div>`;
}

function answerChallenge(answer) {
  const qz = groupState.challengeQuiz;
  if (!qz || qz.answered) return;
  qz.answered = true;
  const q = qz.questions[qz.index];
  const correct = answer === q.answer;
  if (correct) { qz.score += 10; qz.good++; }
  else { qz.bad++; }
  qz.answers.push({ question: q.question, userAnswer: answer, correct: q.answer, isCorrect: correct, explanation: q.explanation || '' });
  document.querySelectorAll('.option-btn').forEach(btn => {
    btn.disabled = true;
    if (btn.textContent === q.answer) btn.classList.add('correct');
    else if (btn.textContent === answer && !correct) btn.classList.add('wrong');
  });
  const fb = document.getElementById('cFeedback');
  if (fb) {
    fb.textContent = correct ? `✅ Correct ! ${q.explanation || ''}` : `❌ ${q.explanation || `La réponse était : ${q.answer}`}`;
    fb.className = 'feedback has-content';
  }
  document.getElementById('cNextBtn').style.display = 'block';
}

function nextChallengeQ() {
  const qz = groupState.challengeQuiz;
  if (!qz) return;
  qz.index++;
  qz.answered = false;
  if (qz.index >= qz.questions.length) { submitChallengeResults(); }
  else { renderChallengeQuestion(); }
}

async function submitChallengeResults() {
  const qz = groupState.challengeQuiz;
  if (!qz) return;
  try {
    const data = await api.request('/groups.php?action=submit_challenge', {
      method: 'POST',
      body: JSON.stringify({
        challenge_id: qz.challenge.id,
        score: qz.score, good: qz.good, bad: qz.bad, answers: qz.answers
      })
    });
    showChallengeResults({ ...qz.challenge, results: data.results, my_result: { score: qz.score, good: qz.good, bad: qz.bad } });
  } catch(e) { 
    await customAlert(e.message || 'Erreur lors de la soumission', 'Erreur', '❌'); 
  }
}

function showChallengeResults(challenge) {
  const page = document.getElementById('challengePlayPage');
  const results = challenge.results || [];
  const myScore = challenge.my_result ? challenge.my_result.score : '?';
  const podium = results.map((r, i) => `
    <div class="g-result-row ${i === 0 ? 'first' : ''}">
      <span class="g-result-rank">${i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '#'+(i+1)}</span>
      <span class="g-result-name">${escHtml(r.username)}</span>
      <span class="g-result-score">${r.score} pts</span>
      <span class="g-result-detail">${r.good}✅ ${r.bad}❌</span>
    </div>`).join('');
  const waitingCount = (challenge.member_count || 0) - results.length;
  page.innerHTML = `
    <div class="card" style="max-width:500px;margin:0 auto">
      <div class="between" style="margin-bottom:16px">
        <button class="btn small ghost" onclick="backToGroupDetail()">← Retour</button>
        <div class="badge">${CAT_LABELS[challenge.category] || challenge.category}</div>
      </div>
      <h2 style="margin:0 0 4px;text-align:center">🏆 Résultats</h2>
      <p class="muted" style="text-align:center;margin:0 0 20px">Ton score : <b>${myScore} pts</b></p>
      <div class="g-results-list">${podium}</div>
      ${waitingCount > 0 ? `<p class="muted" style="text-align:center;margin-top:16px">⏳ ${waitingCount} joueur${waitingCount > 1 ? 's' : ''} n'a pas encore joué</p>` : ''}
    </div>`;
}

function backToGroupDetail() {
  if (groupState.currentGroup) {
    showMultiPage('groupDetailPage');
    api.request('/groups.php?action=list').then(res => {
      groupState.groups = res.groups || [];
      const updated = groupState.groups.find(g => g.id == groupState.currentGroup.id);
      if (updated) {
        groupState.currentGroup = updated;
        renderGroupDetail(updated);
        startChatPolling(updated.id);
        startFeedPolling(updated.id);
      }
    }).catch(() => {});
  }
}

async function abandonChallenge() {
  const choice = await customConfirm('Abandonner ce défi ? Ta progression sera perdue.', 'Abandonner');
  if (choice) {
    groupState.challengeQuiz = null;
    backToGroupDetail();
  }
}

// --- UTILITAIRES ---
function escHtml(str) {
  return String(str || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
}
function escAttr(str) {
  return String(str || '').replace(/'/g,"\\'").replace(/"/g,'&quot;');
}
function escDataAttr(str) {
  return String(str || '')
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}
const GROUP_EMBEDDED_ICONS = window.TQ_EMBEDDED_ICONS || {};
const GROUP_AVATAR_IMAGES = window.TQ_AVATAR_IMAGES || {};
const REAL_GROUP_ICONS = {
  quiz: GROUP_AVATAR_IMAGES.av_dice || 'assets/avatars/av_dice.png',
  daily: GROUP_AVATAR_IMAGES.av_star || 'assets/avatars/av_star.png',
  review: GROUP_AVATAR_IMAGES.av_paint || 'assets/avatars/av_paint.png',
  stats: GROUP_AVATAR_IMAGES.av_potion || 'assets/avatars/av_potion.png',
  memory: GROUP_AVATAR_IMAGES.av_starbook || 'assets/avatars/av_starbook.png',
  timeline: GROUP_AVATAR_IMAGES.av_book || 'assets/avatars/av_book.png',
  popcorn: GROUP_AVATAR_IMAGES.av_popcorn || 'assets/avatars/av_popcorn.png',
  geo: GROUP_AVATAR_IMAGES.av_planet || 'assets/avatars/av_planet.png',
  sondage: GROUP_AVATAR_IMAGES.av_potion || 'assets/avatars/av_potion.png',
  price: GROUP_AVATAR_IMAGES.av_crown || 'assets/avatars/av_crown.png',
  play: GROUP_AVATAR_IMAGES.av_gamepad || 'assets/avatars/av_gamepad.png',
  multi: GROUP_AVATAR_IMAGES.av_gamepad || 'assets/avatars/av_gamepad.png',
  rap: GROUP_AVATAR_IMAGES.av_star || 'assets/avatars/av_star.png',
  mythologie: GROUP_AVATAR_IMAGES.av_ghost || 'assets/avatars/av_ghost.png',
  science: GROUP_AVATAR_IMAGES.av_potion || 'assets/avatars/av_potion.png'
};
const GROUP_ICON_ASSETS = {
  quiz: GROUP_EMBEDDED_ICONS.quiz || 'assets/icons/generated/quiz.png',
  daily: GROUP_EMBEDDED_ICONS.sondage || 'assets/icons/generated/sondage.png',
  review: GROUP_EMBEDDED_ICONS.review || 'assets/icons/generated/review.png',
  stats: GROUP_EMBEDDED_ICONS.sondage || 'assets/icons/generated/sondage.png',
  memory: GROUP_EMBEDDED_ICONS.memory || 'assets/icons/generated/memory.png',
  timeline: GROUP_EMBEDDED_ICONS.timeline || 'assets/icons/generated/timeline.png',
  popcorn: GROUP_EMBEDDED_ICONS.popcorn || 'assets/icons/generated/popcorn.png',
  geo: GROUP_EMBEDDED_ICONS.geo || 'assets/icons/generated/geo.png',
  sondage: GROUP_EMBEDDED_ICONS.sondage || 'assets/icons/generated/sondage.png',
  price: GROUP_EMBEDDED_ICONS.price || 'assets/icons/generated/price.png',
  play: GROUP_EMBEDDED_ICONS.play || GROUP_EMBEDDED_ICONS.titoue_play || 'assets/icons/generated/titoue-play.png',
  multi: GROUP_EMBEDDED_ICONS.multi || 'assets/icons/generated/multi.png',
  rap: GROUP_EMBEDDED_ICONS.rap || 'assets/icons/generated/rap.png',
  mythologie: GROUP_EMBEDDED_ICONS.mythologie || 'assets/icons/generated/mythologie.png',
  science: GROUP_EMBEDDED_ICONS.science || 'assets/icons/generated/science.png'
};
function iconImg(src, classes) {
  return `<img class="asset-icon ${classes}" src="${src}" alt="" aria-hidden="true" loading="lazy" decoding="async">`;
}
function miniIcon(key, extra = '') {
  const cls = `mini-mark mini-mark-${key}${extra ? ` ${extra}` : ''}`;
  return iconImg(GROUP_ICON_ASSETS[key] || GROUP_ICON_ASSETS.quiz, cls);
}
function getMyUsername() {
  return localStorage.getItem('tq_user') || '';
}
function getSavedGroupAvatar() {
  const saved = localStorage.getItem('tq_avatar') || localStorage.getItem('tq_emoji') || 'av_butterfly';
  const images = window.TQ_AVATAR_IMAGES || {};
  return images[saved] || String(saved).startsWith('av_') ? saved : 'av_butterfly';
}
function getMemberAvatar(username) {
  const name = String(username || '');
  if (name && name === getMyUsername()) return getSavedGroupAvatar();
  return name.slice(0, 2).toUpperCase() || '?';
}
function memberAvatarContentHtml(username) {
  const value = getMemberAvatar(username);
  if (String(username || '') === getMyUsername() && typeof window.avatarIconHtml === 'function') {
    return window.avatarIconHtml(value, 'avatar-mark-chat');
  }
  return `<span class="avatar-initials">${escHtml(value)}</span>`;
}
function memberAvatarHtml(username, extraClass = '') {
  const safeName = escDataAttr(username);
  const cls = ['g-avatar', extraClass].filter(Boolean).join(' ');
  return `<span class="${cls}" data-avatar-user="${safeName}" title="${safeName}">${memberAvatarContentHtml(username)}</span>`;
}
function refreshVisibleAvatars() {
  document.querySelectorAll('[data-avatar-user]').forEach(el => {
    const username = el.getAttribute('data-avatar-user') || '';
    el.innerHTML = memberAvatarContentHtml(username);
  });
}
window.addEventListener('avatarChanged', refreshVisibleAvatars);

// =====================================================================
// JEUX DE GROUPE : MEMORY / GEO / SONDAGE / JUSTE PRIX / LIGUE
// =====================================================================

const GAME_INFO = {
  memory:     { label: 'Memory Dates',    desc: 'Retourne les cartes et associe chaque événement à sa date.', icon: 'memory' },
  timeline:   { label: 'Timeline Battle', desc: 'Replace les événements dans le bon ordre chronologique.', icon: 'timeline' },
  popcorn:    { label: 'Popcorn Clash',   desc: "Devine la série, le film, l'anime ou le créateur avec des indices.", icon: 'popcorn' },
  geo:        { label: 'Géo-devinette',   desc: '5 lieux à deviner avec des indices progressifs. 4 tentatives max.', icon: 'geo' },
  sondage:    { label: 'Sondage',         desc: 'Estime des pourcentages. Le plus proche gagne !', icon: 'sondage' },
  juste_prix: { label: 'Le Juste Prix',   desc: 'Estime des valeurs numériques. Le plus proche gagne !', icon: 'price' },
  alcool_order: { label: 'Degré Battle',  desc: "Classe les boissons du degré le plus faible au plus fort.", icon: 'sondage' },
};

function gameIconHtml(type) {
  const key = GAME_INFO[type] ? GAME_INFO[type].icon : type;
  return iconImg(GROUP_ICON_ASSETS[key] || GROUP_ICON_ASSETS.quiz, `game-mark game-mark-${escDataAttr(key || 'quiz')}`);
}

const SOLO_MEMORY_QUESTIONS = [
  { question: 'Lancement de YouTube', answer: '2005', explanation: 'YouTube est lancé en 2005 avant son rachat par Google.' },
  { question: 'Sortie du premier iPhone', answer: '2007', explanation: 'Steve Jobs présente le premier iPhone en 2007.' },
  { question: 'Minecraft sort officiellement', answer: '2011', explanation: 'La version officielle de Minecraft sort en novembre 2011.' },
  { question: 'Première diffusion de Stranger Things', answer: '2016', explanation: 'La série arrive sur Netflix en 2016.' },
  { question: 'TikTok devient disponible mondialement', answer: '2018', explanation: 'Après la fusion avec Musical.ly, TikTok explose hors de Chine.' },
  { question: 'Squid Game devient un phénomène mondial', answer: '2021', explanation: 'La série sud-coréenne bat des records sur Netflix.' },
  { question: 'Lancement public de ChatGPT', answer: '2022', explanation: 'ChatGPT devient accessible au public fin 2022.' },
  { question: 'Sortie du film Barbie', answer: '2023', explanation: 'Le film de Greta Gerwig sort en 2023.' }
];

const SOLO_TIMELINE_QUESTIONS = [
  { question: 'Lancement de Netflix en streaming', answer: '2007', explanation: 'Netflix passe de la location DVD au streaming.' },
  { question: 'Sortie du premier iPhone', answer: '2007', explanation: 'Le smartphone d’Apple change les usages mobiles.' },
  { question: 'Lancement d’Instagram', answer: '2010', explanation: 'Instagram démarre comme application de partage photo.' },
  { question: 'Sortie officielle de Minecraft', answer: '2011', explanation: 'Minecraft sort officiellement après sa bêta.' },
  { question: 'Première diffusion de Stranger Things', answer: '2016', explanation: 'La série devient vite une référence pop culture.' },
  { question: 'Fortnite Battle Royale sort', answer: '2017', explanation: 'Le mode Battle Royale popularise massivement Fortnite.' },
  { question: 'Squid Game devient mondialement viral', answer: '2021', explanation: 'La série devient l’un des plus gros succès de Netflix.' },
  { question: 'Sortie du film Barbie', answer: '2023', explanation: 'Barbie domine le box-office mondial en 2023.' }
];

const SOLO_POPCORN_QUESTIONS = [
  {
    question: 'Quel univers faut-il deviner ?',
    answer: 'Stranger Things',
    options: ['Stranger Things', 'Riverdale', 'Wednesday', 'The Umbrella Academy'],
    hints: ['Une ville américaine appelée Hawkins.', 'Des enfants affrontent une menace surnaturelle.', 'Le Monde à l’envers est central.'],
    explanation: 'Stranger Things mélange années 80, science-fiction et horreur.'
  },
  {
    question: 'Quel animé est caché derrière ces indices ?',
    answer: 'One Piece',
    options: ['Naruto', 'One Piece', 'Demon Slayer', 'Jujutsu Kaisen'],
    hints: ['Un équipage de pirates.', 'Un héros au chapeau de paille.', 'Le rêve est de trouver un trésor légendaire.'],
    explanation: 'Luffy cherche le One Piece avec son équipage.'
  },
  {
    question: 'Quel créateur est-ce ?',
    answer: 'MrBeast',
    options: ['Squeezie', 'MrBeast', 'Inoxtag', 'Cyprien'],
    hints: ['Vidéos avec de très gros défis.', 'Il donne souvent de l’argent ou des prix.', 'Il a lancé Beast Games et Feastables.'],
    explanation: 'MrBeast est connu pour ses défis à très gros budgets.'
  },
  {
    question: 'Quel film faut-il reconnaître ?',
    answer: 'Barbie',
    options: ['Barbie', 'La La Land', 'Mean Girls', 'Clueless'],
    hints: ['Beaucoup de rose.', 'Un duo quitte un monde parfait.', 'Margot Robbie et Ryan Gosling.'],
    explanation: 'Barbie est sorti en 2023 avec Margot Robbie.'
  },
  {
    question: 'Quelle série est visée ?',
    answer: 'Wednesday',
    options: ['Wednesday', 'The Witcher', 'Elite', 'Dark'],
    hints: ['Une école étrange.', 'Une héroïne de la famille Addams.', 'Une danse devenue virale.'],
    explanation: 'Wednesday suit Mercredi Addams à Nevermore Academy.'
  }
];

const SOLO_JUSTE_PRIX_QUESTIONS = [
  { question: 'Combien de saisons compte la série Friends ?', answer: '10', explanation: 'Friends compte 10 saisons diffusées entre 1994 et 2004.' },
  { question: 'Combien de films Harry Potter principaux existent ?', answer: '8', explanation: 'Les 7 livres ont été adaptés en 8 films.' },
  { question: 'En quelle année YouTube a-t-il été lancé ?', answer: '2005', explanation: 'YouTube est lancé en 2005.' },
  { question: 'Combien y a-t-il de signes astrologiques ?', answer: '12', explanation: 'Le zodiaque occidental compte 12 signes.' },
  { question: 'Combien de joueurs y a-t-il dans une équipe de football sur le terrain ?', answer: '11', explanation: 'Une équipe joue à 11 sur le terrain.' }
];

const SOLO_ALCOOL_ORDER_QUESTIONS = [
  { question: 'Bière sans alcool', answer: '0.5% vol.', explanation: 'En France, une bière dite sans alcool peut contenir jusqu’à 1,2% vol., mais beaucoup tournent autour de 0,5%.' },
  { question: 'Panaché', answer: '1.5% vol.', explanation: 'Le panaché mélange bière et limonade : son degré est généralement faible.' },
  { question: 'Bière légère', answer: '3.5% vol.', explanation: 'Une bière légère se situe souvent autour de 3 à 4% vol.' },
  { question: 'Bière blonde classique', answer: '5% vol.', explanation: 'Beaucoup de lagers et blondes classiques sont proches de 5% vol.' },
  { question: 'Cidre brut', answer: '5.5% vol.', explanation: 'Le cidre brut varie selon les maisons, souvent autour de 4,5 à 6% vol.' },
  { question: 'Bière IPA', answer: '6.5% vol.', explanation: 'Les IPA modernes sont souvent plus fortes qu’une lager classique, autour de 6 à 7% vol.' },
  { question: 'Prosecco', answer: '11% vol.', explanation: 'Le prosecco tourne souvent autour de 11% vol., selon les cuvées.' },
  { question: 'Champagne brut', answer: '12% vol.', explanation: 'Le champagne est généralement autour de 12% vol.' },
  { question: 'Vin blanc sec', answer: '12.5% vol.', explanation: 'Un vin blanc sec est souvent entre 11,5 et 13,5% vol.' },
  { question: 'Vin rouge classique', answer: '13.5% vol.', explanation: 'Beaucoup de rouges modernes se situent autour de 13 à 14,5% vol.' },
  { question: 'Vermouth', answer: '16% vol.', explanation: 'Le vermouth est un vin aromatisé et fortifié, souvent autour de 15 à 18% vol.' },
  { question: 'Liqueur type crème', answer: '17% vol.', explanation: 'Certaines liqueurs crémeuses sont autour de 17% vol., mais les recettes varient.' },
  { question: 'Porto', answer: '20% vol.', explanation: 'Le porto est un vin fortifié, souvent proche de 19 à 22% vol.' },
  { question: 'Rhum blanc', answer: '37.5% vol.', explanation: 'Beaucoup de rhums blancs de grande distribution commencent autour de 37,5% vol.' },
  { question: 'Vodka', answer: '40% vol.', explanation: 'La vodka classique est très souvent embouteillée à 40% vol.' },
  { question: 'Whisky', answer: '43% vol.', explanation: 'Le whisky existe souvent à 40% vol., mais beaucoup de single malts sont embouteillés autour de 43% vol.' },
  { question: 'Pastis', answer: '45% vol.', explanation: 'Le pastis est souvent à 45% vol. avant dilution avec de l’eau.' },
  { question: 'Absinthe', answer: '60% vol.', explanation: 'L’absinthe peut être très forte, souvent entre 45 et 70% vol. selon les bouteilles.' }
];

function soloMiniQuestionsFor(type) {
  if (type === 'memory') return memoryShuffle([...SOLO_MEMORY_QUESTIONS]).slice(0, 6);
  if (type === 'timeline') return memoryShuffle([...SOLO_TIMELINE_QUESTIONS]).slice(0, 8);
  if (type === 'popcorn') return memoryShuffle([...SOLO_POPCORN_QUESTIONS]).slice(0, 5);
  if (type === 'juste_prix') return memoryShuffle([...SOLO_JUSTE_PRIX_QUESTIONS]).slice(0, 5);
  if (type === 'alcool_order') return memoryShuffle([...SOLO_ALCOOL_ORDER_QUESTIONS]).slice(0, 8);
  return [];
}

function showSoloMiniShell() {
  window.soloMiniGameActive = true;
  const app = document.getElementById('multiApp');
  const soloApp = document.querySelector('.app');
  const soloNav = document.querySelector('.nav');
  const backBtn = document.getElementById('multiBackBtn');
  if (app) app.style.display = 'block';
  if (soloApp) soloApp.style.display = 'none';
  if (soloNav) soloNav.style.display = 'none';
  if (backBtn) backBtn.textContent = '← Titoue joue';
  document.body.style.overflow = '';
}

function backToSoloPlay() {
  window.soloMiniGameActive = false;
  window.quizModeActive = false;
  clearInterval(blitzTimerInterval);
  if (typeof stopPolling === 'function') stopPolling();
  const app = document.getElementById('multiApp');
  const soloApp = document.querySelector('.app');
  const soloNav = document.querySelector('.nav');
  const backBtn = document.getElementById('multiBackBtn');
  if (app) app.style.display = 'none';
  if (soloApp) soloApp.style.display = '';
  if (soloNav) soloNav.style.display = 'none';
  if (backBtn) backBtn.textContent = '← Accueil';
  document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
  const home = document.getElementById('homePage');
  if (home) home.classList.add('active');
  document.querySelectorAll('.nav button').forEach(b => b.classList.toggle('active', b.dataset.page === 'homePage'));
  const selector = document.getElementById('modeSelector');
  const soloContent = document.getElementById('soloContent');
  const playContent = document.getElementById('playContent');
  if (selector) selector.style.display = 'none';
  if (soloContent) soloContent.style.display = 'none';
  if (playContent) playContent.style.display = 'block';
}

function startSoloMiniGame(type) {
  window.quizModeActive = false;
  const soloNav = document.querySelector('.nav');
  if (soloNav) soloNav.style.display = 'none';
  if (type === 'geo' && typeof startSoloGeoGame === 'function') {
    startSoloGeoGame();
    return;
  }
  if (type === 'sondage' && typeof startSondageSolo === 'function') {
    startSondageSolo();
    return;
  }
  const game = {
    id: `solo-${type}-${Date.now()}`,
    game_type: type,
    questions: soloMiniQuestionsFor(type),
    localSolo: true,
    member_count: 1,
    results: []
  };
  if (!game.questions.length) {
    alert('Pas assez de questions disponibles pour ce jeu.');
    return;
  }
  showSoloMiniShell();
  startGame(game);
}

function showSoloGameResults(game, score, good, bad, detailsHtml = '') {
  const page = document.getElementById('challengePlayPage');
  const info = GAME_INFO[game.game_type] || {};
  page.innerHTML = `
    <div class="card" style="max-width:620px;margin:0 auto">
      <div class="between" style="margin-bottom:16px">
        <button class="btn small ghost" onclick="backToSoloPlay()">← Titoue joue</button>
        <div class="badge">${info.label || game.game_type}</div>
      </div>
      <h2 style="margin:0 0 4px;text-align:center">${miniIcon('stats')}Résultat solo</h2>
      <p class="muted" style="text-align:center;margin:0 0 20px">Ton score : <b>${score} pts</b></p>
      <div class="g-results-list">
        <div class="g-result-row first">
          <span class="g-result-rank">${miniIcon('stats')}</span>
          <span class="g-result-name">${escHtml(getMyUsername() || 'Toi')}</span>
          <span class="g-result-score">${score} pts</span>
          <span class="g-result-detail">${good}✅ ${bad}❌</span>
        </div>
      </div>
      ${detailsHtml}
      <button class="btn primary" style="width:100%;margin-top:18px" onclick="backToSoloPlay()">Revenir aux jeux</button>
    </div>`;
}

window.startSoloMiniGame = startSoloMiniGame;
window.backToSoloPlay = backToSoloPlay;

// --- Charge et affiche les jeux de la semaine ---
async function loadWeekGames(groupId) {
  const wrap = document.getElementById('groupGamesWrap');
  if (!wrap) return;
  try {
    const data = await api.request(`/games.php?action=week_games&group_id=${groupId}`);
    renderWeekGames(groupId, data.games || [], data.week);
  } catch(e) {
    wrap.innerHTML = '<p class="muted" style="text-align:center;padding:8px">Erreur de chargement des jeux.</p>';
  }
}

function renderWeekGames(groupId, games, week) {
  const wrap = document.getElementById('groupGamesWrap');
  if (!wrap) return;

  const visibleGames = games.filter(g => GAME_INFO[g.game_type]);
  const existingTypes = visibleGames.map(g => g.game_type);
  const availableTypes = Object.keys(GAME_INFO).filter(t => !existingTypes.includes(t));

  // Cartes des jeux déjà lancés cette semaine
  const gameCards = visibleGames.map(g => {
    const info = GAME_INFO[g.game_type] || {};
    const played = g.my_score !== null;
    return `
    <div class="g-game-tile ${played ? 'done' : 'todo'}" role="button" tabindex="0" data-game-id="${g.id}">
      <div class="g-game-tile-icon">${gameIconHtml(g.game_type)}</div>
      <div class="g-game-tile-label">${info.label || g.game_type}</div>
      <div class="g-game-tile-desc">${info.desc || ''}</div>
      <div class="g-game-tile-footer">
        ${played
          ? `<span class="g-score-badge">${g.my_score} pts</span>`
          : `<span class="g-todo-badge">Jouer</span>`}
        <span class="g-game-tile-count">${g.played_count} joueur${g.played_count > 1 ? 's' : ''}</span>
      </div>
    </div>`;
  }).join('');

  // Cartes des jeux disponibles (pas encore lancés)
  const availableCards = availableTypes.map(t => {
    const info = GAME_INFO[t];
    return `
    <div class="g-game-tile available" role="button" tabindex="0" data-group-id="${groupId}" data-launch-game="${t}">
      <div class="g-game-tile-icon">${gameIconHtml(t)}</div>
      <div class="g-game-tile-label">${info.label}</div>
      <div class="g-game-tile-desc">${info.desc}</div>
      <div class="g-game-tile-footer">
        <span class="g-game-tile-launch">Lancer ↗</span>
      </div>
    </div>`;
  }).join('');

  wrap.innerHTML = `
    <div class="g-games-grid">
      ${gameCards}
      ${availableCards}
    </div>
    <button class="g-league-btn" onclick="showLeague(${groupId})">🏆 Voir la ligue de la semaine</button>
  `;
}

document.addEventListener('click', function(e) {
  const tile = e.target.closest && e.target.closest('.g-game-tile[data-game-id], .g-game-tile[data-launch-game]');
  if (!tile) return;
  e.preventDefault();
  if (tile.dataset.gameId) {
    openGame(Number(tile.dataset.gameId));
    return;
  }
  if (tile.dataset.launchGame) {
    launchGame(Number(tile.dataset.groupId), tile.dataset.launchGame);
  }
});

document.addEventListener('keydown', function(e) {
  if (e.key !== 'Enter' && e.key !== ' ') return;
  const tile = e.target.closest && e.target.closest('.g-game-tile[data-game-id], .g-game-tile[data-launch-game]');
  if (!tile) return;
  e.preventDefault();
  tile.click();
});

// --- Lance un nouveau jeu ---
async function launchGame(groupId, gameType) {
  try {
    const data = await api.request('/games.php?action=create_game', {
      method: 'POST',
      body: JSON.stringify({ group_id: groupId, game_type: gameType })
    });
    openGame(data.game_id);
  } catch(e) { alert(e.message || 'Erreur'); }
}

// --- Ouvre un jeu ---
async function openGame(gameId) {
  try {
    const data = await api.request(`/games.php?action=get_game&game_id=${gameId}`);
    const game = data.game;
    if (game.my_result) {
      showGameResults(game);
    } else {
      startGame(game);
    }
  } catch(e) { alert(e.message || 'Erreur'); }
}

// =====================================================================
// MOTEUR DE JEU GENERIQUE
// =====================================================================
const gameState = {
  game: null,
  index: 0,
  score: 0,
  good: 0,
  bad: 0,
  answered: false,
  startTime: 0,
  questionStartTime: 0,
  totalTimeMs: 0,
};

const memoryState = {
  cards: [],
  selected: [],
  matchedPairs: 0,
  moves: 0,
  combo: 0,
  locked: false,
};

const timelineState = {
  game: null,
  order: [],
  submitted: false,
};

const popcornState = {
  game: null,
  index: 0,
  score: 0,
  good: 0,
  bad: 0,
  hintsShown: 1,
  answered: false,
  startedAt: 0,
};

function startGame(game) {
  // Route vers le moteur specifique selon le type
  if (game.game_type === 'memory')     { startMemoryGame(game); return; }
  if (game.game_type === 'timeline')   { startTimelineGame(game); return; }
  if (game.game_type === 'alcool_order') { startTimelineGame(game); return; }
  if (game.game_type === 'popcorn')    { startPopcornGame(game); return; }
  if (game.game_type === 'geo')        { startGeoGame(game); return; }
  if (game.game_type === 'sondage')    { startSondageGame(game); return; }
  if (game.game_type === 'juste_prix') { startJustePrixGame(game); return; }

  // Moteur generique (blitz, memory, imposteur)
  stopPolling();
  gameState.game = game;
  gameState.index = 0;
  gameState.score = 0;
  gameState.good = 0;
  gameState.bad = 0;
  gameState.answered = false;
  gameState.startTime = Date.now();
  gameState.totalTimeMs = 0;
  showMultiPage('challengePlayPage');
  renderGameQuestion();
}

function renderGameQuestion() {
  const g = gameState.game;
  const page = document.getElementById('challengePlayPage');
  const q = g.questions[gameState.index];
  const total = g.questions.length;
  const pct = Math.round((gameState.index / total) * 100);
  const info = GAME_INFO[g.game_type] || {};
  const isBlitz = g.game_type === 'blitz';
  const opts = Array.isArray(q.options) ? q.options : JSON.parse(q.options || '[]');
  
  // Créer les boutons avec onclick INLINE (plus fiable)
  let optionsHtml = '';
  for (let i = 0; i < opts.length; i++) {
    const opt = String(opts[i]);
    const escaped = opt.replace(/'/g, "\\'").replace(/"/g, '&quot;');
    optionsHtml += `<button class="option-btn" onclick="window.answerGame('${escaped}')">${escHtml(opt)}</button>`;
  }

  const timerHtml = isBlitz ? `<div id="gameTimer" class="g-timer">10</div>` : '';
  const contextHtml = g.is_imposteur && gameState.index === 0 ? `<div class="g-imposteur-hint">⚠️ Tu es peut-être l'imposteur sur cette question...</div>` : '';

  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card">
          <div class="between">
            <button class="btn small ghost" onclick="window.abandonGame()">✕ Quitter</button>
            <div class="badge">${info.label || g.game_type}</div>
            ${timerHtml}
          </div>
          <div class="meta" style="margin-top:12px">
            <div class="badge">Question ${gameState.index + 1}/${total}</div>
            <div class="badge">⭐ Score ${gameState.score}</div>
          </div>
          <div class="progress"><span style="width:${pct}%"></span></div>
          ${contextHtml}
          <div class="question">${escHtml(q.question)}</div>
          <div class="answers">${optionsHtml}</div>
          <div class="feedback" id="gameFeedback"></div>
          <button class="btn primary" id="gameNextBtn" style="display:none;margin-top:12px" onclick="window.nextGameQuestion()">
            ${gameState.index < total - 1 ? 'Question suivante →' : 'Voir les résultats 🏆'}
          </button>
        </div>
      </div>
    </div>`;

  gameState.answered = false;
  gameState.questionStartTime = Date.now();

  if (isBlitz) startBlitzTimer();
}

// Délégation d'événements pour les jeux génériques (attaché une seule fois)
document.addEventListener('click', function(e) {
  // Boutons de réponse
  if (e.target.classList.contains('game-option-btn') && !gameState.answered) {
    const answer = e.target.dataset.answer;
    if (answer) answerGame(answer);
  }
  
  // Bouton abandon
  if (e.target.classList.contains('game-abandon-btn')) {
    abandonGame();
  }
  
  // Bouton suivant
  if (e.target.classList.contains('game-next-btn')) {
    nextGameQuestion();
  }
});

// =====================================================================
// TIMELINE BATTLE
// =====================================================================
const TIMELINE_EXACT_POINTS = 8;
const TIMELINE_SEQUENCE_POINTS = 2;

function startTimelineGame(game) {
  stopPolling();
  gameState.game = game;
  gameState.index = 0;
  gameState.score = 0;
  gameState.good = 0;
  gameState.bad = 0;
  gameState.answered = false;
  gameState.startTime = Date.now();
  gameState.totalTimeMs = 0;
  timelineState.game = game;
  timelineState.order = memoryShuffle((game.questions || []).map((_, idx) => idx));
  timelineState.submitted = false;
  showMultiPage('challengePlayPage');
  renderTimelineBoard();
}

function timelineYear(q) {
  const raw = String(q.answer || q.date_event || '').toLowerCase();
  const cleaned = raw.replace(',', '.');
  const match = cleaned.match(/-?\d+(?:\.\d+)?/);
  const num = match ? parseFloat(match[0]) : NaN;
  if (Number.isNaN(num)) return 0;
  return raw.includes('av') || raw.includes('bc') ? -Math.abs(num) : num;
}

function getTimelineCorrectOrder() {
  return (timelineState.game.questions || [])
    .map((q, idx) => ({ idx, year: timelineYear(q), title: q.question }))
    .sort((a, b) => a.year - b.year || a.title.localeCompare(b.title));
}

function renderTimelineBoard(result = null) {
  const page = document.getElementById('challengePlayPage');
  const questions = timelineState.game.questions || [];
  const gameType = timelineState.game.game_type;
  const info = GAME_INFO[gameType] || GAME_INFO.timeline;
  const isAlcoholOrder = gameType === 'alcool_order';
  const itemLabel = isAlcoholOrder ? 'boissons' : 'événements';
  const placedWord = isAlcoholOrder ? 'placées' : 'placés';
  const introText = isAlcoholOrder
    ? "Classe les cartes du degré d'alcool le plus faible au plus fort. Les degrés moyens se révèlent après validation."
    : "Replace les cartes de la plus ancienne à la plus récente. Les dates exactes se révèlent après validation.";
  const correctOrder = getTimelineCorrectOrder();
  const sortedYears = correctOrder.map(item => item.year);
  const expectedPosByIdx = new Map(correctOrder.map((item, pos) => [item.idx, pos]));
  const cards = timelineState.order.map((qIdx, pos) => {
    const q = questions[qIdx];
    const expectedPos = expectedPosByIdx.get(qIdx);
    const isCorrect = result && timelineYear(q) === sortedYears[pos];
    const dateHtml = result ? `<div class="timeline-date">${escHtml(q.date_event || q.answer)}</div>` : '';
    const correctionHtml = result && !isCorrect ? `
      <div class="timeline-correction">
        À placer en position ${expectedPos + 1}
      </div>` : '';
    const stateClass = result ? (isCorrect ? 'correct' : 'wrong') : '';
    return `
      <div class="timeline-row ${stateClass}">
        <div class="timeline-rank">${pos + 1}</div>
        <div class="timeline-content">
          <div class="timeline-title">${escHtml(q.question)}</div>
          ${dateHtml}
          ${correctionHtml}
        </div>
        ${result ? '' : `
          <div class="timeline-controls">
            <button type="button" title="Monter" data-timeline-pos="${pos}" data-timeline-move="-1" ${pos === 0 ? 'disabled' : ''}>↑</button>
            <button type="button" title="Descendre" data-timeline-pos="${pos}" data-timeline-move="1" ${pos === timelineState.order.length - 1 ? 'disabled' : ''}>↓</button>
          </div>`}
      </div>`;
  }).join('');

  const correctionList = result ? correctOrder.map((item, pos) => {
    const q = questions[item.idx];
    const userPos = timelineState.order.indexOf(item.idx);
    const ok = userPos === pos || timelineYear(q) === sortedYears[userPos];
    return `
      <div class="timeline-answer-row ${ok ? 'ok' : 'miss'}">
        <span class="timeline-answer-rank">${pos + 1}</span>
        <span class="timeline-answer-main">
          <b>${escHtml(q.question)}</b>
          <small>${escHtml(q.date_event || q.answer)}</small>
        </span>
        <span class="timeline-answer-status">${ok ? 'OK' : `Tu l'avais mise ${userPos + 1}`}</span>
      </div>`;
  }).join('') : '';

  const resultHtml = result ? `
    <div class="timeline-result">
      <strong>${result.exact}/${questions.length}</strong> ${itemLabel} exactement ${placedWord}.
      <span>${result.adjacent} enchaînement${result.adjacent > 1 ? 's' : ''} correct${result.adjacent > 1 ? 's' : ''}.</span>
      <span>Score : <b>${result.score} pts</b></span>
      <small>Calcul : ${result.exact} × ${TIMELINE_EXACT_POINTS} pts + ${result.adjacent} × ${TIMELINE_SEQUENCE_POINTS} pts = ${result.score} pts.</small>
    </div>
    <div class="timeline-answer-key">
      <div class="timeline-answer-title">Correction complète</div>
      ${correctionList}
    </div>` : '';

  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card timeline-card">
          <div class="between">
            <button class="btn small ghost" onclick="window.abandonGame()">✕ Quitter</button>
            <div class="badge timeline-badge">${gameIconHtml(gameType)} ${escHtml(info.label || 'Timeline Battle')}</div>
          </div>
          <div class="timeline-intro">${escHtml(introText)}</div>
          <div class="timeline-list">${cards}</div>
          ${resultHtml}
          <div class="feedback" id="gameFeedback"></div>
          <button class="btn primary" id="timelineSubmitBtn" style="margin-top:14px" data-timeline-action="${result ? 'submit' : 'check'}">
            ${result ? 'Envoyer mon score 🏆' : 'Valider ma timeline'}
          </button>
        </div>
      </div>
    </div>`;
}

document.addEventListener('click', function(e) {
  const moveBtn = e.target.closest && e.target.closest('[data-timeline-move]');
  if (moveBtn) {
    e.preventDefault();
    if (!moveBtn.disabled) moveTimelineItem(Number(moveBtn.dataset.timelinePos), Number(moveBtn.dataset.timelineMove));
    return;
  }

  const actionBtn = e.target.closest && e.target.closest('[data-timeline-action]');
  if (actionBtn) {
    e.preventDefault();
    actionBtn.dataset.timelineAction === 'submit' ? submitTimelineGame() : checkTimelineGame();
  }
});

function moveTimelineItem(pos, direction) {
  if (timelineState.submitted) return;
  const next = pos + direction;
  if (next < 0 || next >= timelineState.order.length) return;
  [timelineState.order[pos], timelineState.order[next]] = [timelineState.order[next], timelineState.order[pos]];
  renderTimelineBoard();
}

function checkTimelineGame() {
  if (timelineState.submitted) return;
  const sortedYears = getTimelineCorrectOrder().map(item => item.year);
  const userYears = timelineState.order.map(idx => timelineYear(timelineState.game.questions[idx]));
  let exact = 0;
  let adjacent = 0;
  userYears.forEach((year, pos) => {
    if (year === sortedYears[pos]) exact++;
  });
  for (let i = 0; i < userYears.length - 1; i++) {
    if (userYears[i] <= userYears[i + 1]) adjacent++;
  }
  const score = (exact * TIMELINE_EXACT_POINTS) + (adjacent * TIMELINE_SEQUENCE_POINTS);
  gameState.score = score;
  gameState.good = exact;
  gameState.bad = timelineState.order.length - exact;
  gameState.totalTimeMs = Date.now() - gameState.startTime;
  timelineState.submitted = true;
  timelineState.lastResult = { exact, adjacent, score };
  renderTimelineBoard({ exact, adjacent, score });
}

function submitTimelineGame() {
  if (timelineState.game && timelineState.game.localSolo) {
    const result = timelineState.lastResult || { exact: gameState.good || 0, adjacent: 0, score: gameState.score || 0 };
    const score = gameState.score || 0;
    const good = gameState.good || 0;
    const bad = gameState.bad || 0;
    const isAlcoholOrder = timelineState.game.game_type === 'alcool_order';
    const itemLabel = isAlcoholOrder ? 'boissons' : 'événements';
    const placedWord = isAlcoholOrder ? 'placées' : 'placés';
    const orderLabel = isAlcoholOrder ? 'enchaînements corrects' : 'voisinages chronologiques';
    showSoloGameResults(timelineState.game, score, good, bad, `
      <p class="muted" style="text-align:center;margin-top:14px">
        Calcul : ${result.exact} ${itemLabel} exactement ${placedWord} × ${TIMELINE_EXACT_POINTS} pts + ${result.adjacent} ${orderLabel} × ${TIMELINE_SEQUENCE_POINTS} pts.
      </p>
    `);
    return;
  }
  submitGame();
}

// =====================================================================
// POPCORN CLASH
// =====================================================================
function parseMaybeJsonList(value) {
  if (Array.isArray(value)) return value;
  if (!value) return [];
  try {
    const parsed = JSON.parse(value);
    return Array.isArray(parsed) ? parsed : [];
  } catch(e) {
    return String(value).split('|').map(x => x.trim()).filter(Boolean);
  }
}

function startPopcornGame(game) {
  stopPolling();
  popcornState.game = game;
  popcornState.index = 0;
  popcornState.score = 0;
  popcornState.good = 0;
  popcornState.bad = 0;
  popcornState.hintsShown = 1;
  popcornState.answered = false;
  popcornState.startedAt = Date.now();
  showMultiPage('challengePlayPage');
  renderPopcornQuestion();
}

function currentPopcornQuestion() {
  return popcornState.game.questions[popcornState.index];
}

function popcornPoints() {
  return popcornState.hintsShown === 1 ? 10 : popcornState.hintsShown === 2 ? 7 : 4;
}

function renderPopcornQuestion() {
  const page = document.getElementById('challengePlayPage');
  const q = currentPopcornQuestion();
  const total = popcornState.game.questions.length;
  const hints = parseMaybeJsonList(q.hints).slice(0, popcornState.hintsShown);
  const options = parseMaybeJsonList(q.options);
  const pct = Math.round((popcornState.index / total) * 100);
  const hintHtml = hints.map((hint, idx) => `
    <div class="popcorn-hint">
      <span>Indice ${idx + 1}</span>
      <strong>${escHtml(hint)}</strong>
    </div>`).join('');
  const optionHtml = options.map(opt => `
    <button type="button" class="popcorn-option" data-answer="${escDataAttr(opt)}">${escHtml(opt)}</button>
  `).join('');

  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card popcorn-card">
          <div class="between">
            <button class="btn small ghost" onclick="window.abandonGame()">✕ Quitter</button>
            <div class="badge popcorn-badge">${gameIconHtml('popcorn')} Popcorn Clash</div>
          </div>
          <div class="meta" style="margin-top:12px">
            <div class="badge">Question ${popcornState.index + 1}/${total}</div>
            <div class="badge">Score ${popcornState.score}</div>
            <div class="badge">${popcornPoints()} pts possibles</div>
          </div>
          <div class="progress"><span style="width:${pct}%"></span></div>
          <div class="popcorn-question">${escHtml(q.question)}</div>
          <div class="popcorn-hints">${hintHtml}</div>
          <div class="popcorn-options">${optionHtml}</div>
          <div class="feedback" id="popcornFeedback"></div>
          <div class="between" style="margin-top:12px">
            <button class="btn ghost" id="popcornHintBtn" data-popcorn-action="hint" ${popcornState.hintsShown >= 3 || popcornState.answered ? 'disabled' : ''}>Indice suivant</button>
            <button class="btn primary" id="popcornNextBtn" style="display:none" data-popcorn-action="next">
              ${popcornState.index < total - 1 ? 'Question suivante →' : 'Voir les résultats 🏆'}
            </button>
          </div>
        </div>
      </div>
    </div>`;
}

document.addEventListener('click', function(e) {
  const option = e.target.closest && e.target.closest('.popcorn-option');
  if (option) {
    e.preventDefault();
    if (!option.disabled) answerPopcorn(option.getAttribute('data-answer') || '');
    return;
  }

  const action = e.target.closest && e.target.closest('[data-popcorn-action]');
  if (!action) return;
  e.preventDefault();
  if (action.disabled) return;
  action.dataset.popcornAction === 'hint' ? showMorePopcornHint() : nextPopcornQuestion();
});

function showMorePopcornHint() {
  if (popcornState.answered || popcornState.hintsShown >= 3) return;
  popcornState.hintsShown++;
  renderPopcornQuestion();
}

function answerPopcorn(answer) {
  if (popcornState.answered) return;
  const q = currentPopcornQuestion();
  const correct = answer === q.answer;
  popcornState.answered = true;
  document.querySelectorAll('.popcorn-option').forEach(btn => {
    btn.disabled = true;
    if (btn.dataset.answer === q.answer) btn.classList.add('correct');
    if (btn.dataset.answer === answer && !correct) btn.classList.add('wrong');
  });
  const fb = document.getElementById('popcornFeedback');
  if (correct) {
    const pts = popcornPoints();
    popcornState.score += pts;
    popcornState.good++;
    fb.innerHTML = `✅ Bonne réponse : <strong>${escHtml(q.answer)}</strong>. +${pts} pts<div class="popcorn-expl">${q.explanation || ''}</div>`;
    fb.className = 'feedback has-content correct';
  } else {
    popcornState.bad++;
    fb.innerHTML = `❌ C'était <strong>${escHtml(q.answer)}</strong>.<div class="popcorn-expl">${q.explanation || ''}</div>`;
    fb.className = 'feedback has-content wrong';
  }
  const nextBtn = document.getElementById('popcornNextBtn');
  const hintBtn = document.getElementById('popcornHintBtn');
  if (nextBtn) nextBtn.style.display = 'block';
  if (hintBtn) hintBtn.disabled = true;
}

function nextPopcornQuestion() {
  popcornState.index++;
  popcornState.hintsShown = 1;
  popcornState.answered = false;
  if (popcornState.index >= popcornState.game.questions.length) {
    submitPopcornGame();
  } else {
    renderPopcornQuestion();
  }
}

async function submitPopcornGame() {
  const g = popcornState.game;
  if (g && g.localSolo) {
    showSoloGameResults(g, popcornState.score, popcornState.good, popcornState.bad);
    return;
  }
  try {
    await api.request('/games.php?action=submit_game', {
      method: 'POST',
      body: JSON.stringify({
        game_id: g.id,
        score: popcornState.score,
        good: popcornState.good,
        bad: popcornState.bad,
        time_ms: Date.now() - popcornState.startedAt
      })
    });
    const data = await api.request(`/games.php?action=get_game&game_id=${g.id}`);
    showGameResults(data.game);
  } catch(e) {
    alert(e.message || 'Erreur');
  }
}

// =====================================================================
// JEU MEMORY - VRAI PLATEAU DE PAIRES
// =====================================================================
function startMemoryGame(game) {
  stopPolling();
  gameState.game = game;
  gameState.index = 0;
  gameState.score = 0;
  gameState.good = 0;
  gameState.bad = 0;
  gameState.answered = false;
  gameState.startTime = Date.now();
  gameState.totalTimeMs = 0;

  const pairs = (game.questions || []).slice(0, 8);
  memoryState.cards = memoryShuffle(pairs.flatMap((q, pairIndex) => {
    const date = String(q.answer || q.date_event || '');
    return [
      {
        id: `event-${pairIndex}`,
        pairIndex,
        type: 'event',
        label: 'Événement',
        text: q.question,
        mate: date,
        explanation: q.explanation || '',
      },
      {
        id: `date-${pairIndex}`,
        pairIndex,
        type: 'date',
        label: 'Date',
        text: date,
        mate: q.question,
        explanation: q.explanation || '',
      }
    ];
  }));
  memoryState.selected = [];
  memoryState.matchedPairs = 0;
  memoryState.moves = 0;
  memoryState.combo = 0;
  memoryState.locked = false;

  showMultiPage('challengePlayPage');
  renderMemoryBoard();
}

function memoryShuffle(items) {
  const arr = [...items];
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr;
}

function renderMemoryBoard() {
  const page = document.getElementById('challengePlayPage');
  const totalPairs = memoryState.cards.length / 2;
  const pct = totalPairs ? Math.round((memoryState.matchedPairs / totalPairs) * 100) : 0;
  const cardsHtml = memoryState.cards.map((card, idx) => {
    const stateClass = card.matched ? 'matched' : card.flipped ? 'flipped' : '';
    return `
      <button type="button" class="memory-tile ${stateClass}" data-card-index="${idx}" ${card.matched ? 'disabled' : ''}>
        <span class="memory-tile-back">?</span>
        <span class="memory-tile-front">
          <span class="memory-tile-kind">${card.type === 'date' ? 'Date' : 'Événement'}</span>
          <span class="memory-tile-text">${escHtml(card.text || 'Carte vide')}</span>
        </span>
      </button>`;
  }).join('');

  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card memory-card">
          <div class="between">
            <button class="btn small ghost" onclick="window.abandonGame()">✕ Quitter</button>
            <div class="badge memory-badge">${gameIconHtml('memory')} Memory Dates</div>
          </div>
          <div class="memory-scorebar">
            <div class="badge">Paires ${memoryState.matchedPairs}/${totalPairs}</div>
            <div class="badge">⭐ Score ${gameState.score}</div>
            <div class="badge">Coups ${memoryState.moves}</div>
            <div class="badge">Combo x${Math.max(1, memoryState.combo + 1)}</div>
          </div>
          <div class="progress"><span style="width:${pct}%"></span></div>
          <div class="memory-intro">
            <strong>Associe les cartes.</strong>
            Retourne un événement puis sa date, ou l’inverse. Les bonnes paires restent ouvertes.
          </div>
          <div class="memory-board">${cardsHtml}</div>
          <div class="feedback" id="gameFeedback"></div>
          <button class="btn primary" id="gameNextBtn" style="display:${memoryState.matchedPairs === totalPairs ? 'block' : 'none'};margin-top:12px" onclick="window.submitMemoryGame()">
            Voir les résultats 🏆
          </button>
        </div>
      </div>
    </div>`;
}

document.addEventListener('click', function(e) {
  const btn = e.target.closest && e.target.closest('.memory-tile');
  if (!btn) return;
  e.preventDefault();
  selectMemoryCard(Number(btn.getAttribute('data-card-index')));
});

function selectMemoryCard(cardIndex) {
  if (memoryState.locked) return;
  const card = memoryState.cards[cardIndex];
  if (!card || card.matched || card.flipped) return;

  card.flipped = true;
  memoryState.selected.push(cardIndex);
  renderMemoryBoard();

  if (memoryState.selected.length < 2) return;

  memoryState.moves++;
  const [firstIndex, secondIndex] = memoryState.selected;
  const first = memoryState.cards[firstIndex];
  const second = memoryState.cards[secondIndex];
  const isPair = first && second && first.pairIndex === second.pairIndex && first.type !== second.type;

  memoryState.locked = true;
  if (isPair) {
    first.matched = true;
    second.matched = true;
    memoryState.matchedPairs++;
    memoryState.combo++;
    gameState.good++;
    const pts = 10 + Math.min(10, memoryState.combo * 2);
    gameState.score += pts;
    memoryState.selected = [];
    memoryState.locked = false;
    renderMemoryBoard();
    setMemoryFeedback(`✅ Paire trouvée ! +${pts} pts`, first.explanation, 'good');

    if (memoryState.matchedPairs === memoryState.cards.length / 2) {
      gameState.totalTimeMs = Date.now() - gameState.startTime;
      renderMemoryBoard();
      setMemoryFeedback(`🏆 Plateau terminé en ${memoryState.moves} coups !`, gameState.game && gameState.game.localSolo ? 'Tu peux voir ton résultat.' : 'Tu peux voir les résultats du groupe.', 'good');
    }
  } else {
    gameState.bad++;
    memoryState.combo = 0;
    setMemoryFeedback('❌ Ce n’est pas la bonne paire.', 'Regarde bien les deux cartes avant qu’elles se retournent.', 'bad');
    setTimeout(() => {
      if (memoryState.cards[firstIndex]) memoryState.cards[firstIndex].flipped = false;
      if (memoryState.cards[secondIndex]) memoryState.cards[secondIndex].flipped = false;
      memoryState.selected = [];
      memoryState.locked = false;
      renderMemoryBoard();
    }, 2200);
  }
}

function setMemoryFeedback(title, detail, type) {
  const fb = document.getElementById('gameFeedback');
  if (!fb) return;
  fb.innerHTML = `${escHtml(title)}${detail ? `<div class="memory-feedback-detail">${detail}</div>` : ''}`;
  fb.className = `feedback has-content memory-feedback ${type === 'bad' ? 'wrong' : 'correct'}`;
}

function submitMemoryGame() {
  gameState.totalTimeMs = gameState.totalTimeMs || (Date.now() - gameState.startTime);
  if (gameState.game && gameState.game.localSolo) {
    submitGame();
    return;
  }
  submitGame();
}

// --- Timer Blitz ---
let blitzTimerInterval = null;
function startBlitzTimer() {
  clearInterval(blitzTimerInterval);
  let remaining = 10;
  const el = document.getElementById('gameTimer');
  blitzTimerInterval = setInterval(() => {
    remaining--;
    if (el) {
      el.textContent = remaining;
      el.className = 'g-timer' + (remaining <= 3 ? ' danger' : '');
    }
    if (remaining <= 0) {
      clearInterval(blitzTimerInterval);
      if (!gameState.answered) {
        // Temps écoulé → mauvaise réponse
        autoFailGame();
      }
    }
  }, 1000);
}

function autoFailGame() {
  if (gameState.answered) return;
  gameState.answered = true;
  clearInterval(blitzTimerInterval);
  const q = gameState.game.questions[gameState.index];
  gameState.bad++;
  gameState.totalTimeMs += 10000;
  document.querySelectorAll('.option-btn').forEach(btn => {
    btn.disabled = true;
    if (btn.textContent === q.answer) btn.classList.add('correct');
  });
  const fb = document.getElementById('gameFeedback');
  if (fb) { fb.textContent = `⏱️ Temps écoulé ! Réponse : ${q.answer}`; fb.className = 'feedback has-content'; }
  document.getElementById('gameNextBtn').style.display = 'block';
}

function answerGame(answer) {
  if (gameState.answered) return;
  gameState.answered = true;
  clearInterval(blitzTimerInterval);

  const q = gameState.game.questions[gameState.index];
  const opts = Array.isArray(q.options) ? q.options : JSON.parse(q.options || '[]');
  const correct = answer === q.answer;
  const elapsed = Date.now() - gameState.questionStartTime;
  gameState.totalTimeMs += elapsed;

  // Score : base 10 pts + bonus vitesse pour blitz
  let pts = 0;
  if (correct) {
    pts = 10;
    if (gameState.game.game_type === 'blitz') {
      pts += Math.max(0, Math.floor((10000 - elapsed) / 1000)); // max +10 pts bonus
    }
    gameState.score += pts;
    gameState.good++;
  } else {
    gameState.bad++;
  }

  document.querySelectorAll('.option-btn, .memory-date-card').forEach(btn => {
    btn.disabled = true;
    const btnText = btn.textContent.trim();
    if (btnText === q.answer) btn.classList.add('correct');
    else if (btnText === answer && !correct) btn.classList.add('wrong');
  });

  const fb = document.getElementById('gameFeedback');
  if (fb) {
    fb.textContent = correct
      ? `✅ +${pts} pts ! ${q.explanation || ''}`
      : `❌ Réponse : ${q.answer}. ${q.explanation || ''}`;
    fb.className = 'feedback has-content';
  }
  const nextBtn = document.getElementById('gameNextBtn');
  if (nextBtn) nextBtn.style.display = 'block';
}

function nextGameQuestion() {
  gameState.index++;
  if (gameState.index >= gameState.game.questions.length) {
    submitGame();
  } else {
    renderGameQuestion();
  }
}

async function submitGame() {
  const g = gameState.game;
  if (g && g.localSolo) {
    showSoloGameResults(g, gameState.score, gameState.good, gameState.bad);
    return;
  }
  try {
    await api.request('/games.php?action=submit_game', {
      method: 'POST',
      body: JSON.stringify({
        game_id: g.id,
        score: gameState.score,
        good: gameState.good,
        bad: gameState.bad,
        time_ms: gameState.totalTimeMs
      })
    });
    // Recharge le jeu pour avoir les resultats
    const data = await api.request(`/games.php?action=get_game&game_id=${g.id}`);
    showGameResults(data.game);
  } catch(e) {
    alert(e.message || 'Erreur lors de la soumission');
  }
}

function showGameResults(game) {
  const page = document.getElementById('challengePlayPage');
  const info = GAME_INFO[game.game_type] || {};
  const results = game.results || [];
  const myScore = game.my_result ? game.my_result.score : gameState.score;

  const podium = results.map((r, i) => `
    <div class="g-result-row ${i === 0 ? 'first' : ''}">
      <span class="g-result-rank">${i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '#'+(i+1)}</span>
      <span class="g-result-name">${escHtml(r.username)}</span>
      <span class="g-result-score">${r.score} pts</span>
      <span class="g-result-detail">${r.good}✅ ${r.bad}❌</span>
    </div>`).join('');

  const waitingCount = (game.member_count || 0) - results.length;

  // Section vote imposteur
  let imposteurSection = '';
  if (game.game_type === 'imposteur') {
    const members = groupState.currentGroup ? (groupState.currentGroup.members || []) : [];
    const myVote = game.votes && game.votes.length > 0;
    if (!myVote && members.length > 0) {
      const voteBtns = members.map(m =>
        `<button class="g-vote-btn" onclick="voteImposteur(${game.id}, ${m.id})">${escHtml(m.username)}</button>`
      ).join('');
      imposteurSection = `
        <div style="margin-top:16px">
          <div class="g-section-title">🎭 Qui est l'imposteur ?</div>
          <div class="g-vote-grid">${voteBtns}</div>
        </div>`;
    } else if (game.imposteur_revealed) {
      const voteResults = (game.votes || []).map(v =>
        `<div class="g-feed-item"><span class="g-feed-name">${escHtml(v.voted_for)}</span><span class="g-feed-answer">${v.vote_count} vote${v.vote_count > 1 ? 's' : ''}</span></div>`
      ).join('');
      imposteurSection = `
        <div style="margin-top:16px">
          <div class="g-section-title">🎭 L'imposteur etait : <b>${escHtml(game.imposteur_revealed)}</b></div>
          ${voteResults}
        </div>`;
    }
  }

  page.innerHTML = `
    <div class="card" style="max-width:500px;margin:0 auto">
      <div class="between" style="margin-bottom:16px">
        <button class="btn small ghost" onclick="backToGroupDetail()">← Retour</button>
        <div class="badge">${info.label || game.game_type}</div>
      </div>
      <h2 style="margin:0 0 4px;text-align:center">${miniIcon('stats')}Résultats</h2>
      <p class="muted" style="text-align:center;margin:0 0 20px">Ton score : <b>${myScore} pts</b></p>
      <div class="g-results-list">${podium}</div>
      ${waitingCount > 0 ? `<p class="muted" style="text-align:center;margin-top:12px">⏳ ${waitingCount} joueur${waitingCount > 1 ? 's' : ''} n'a pas encore joué</p>` : ''}
      ${imposteurSection}
    </div>`;
}

async function voteImposteur(gameId, votedForUserId) {
  try {
    await api.request('/games.php?action=vote_imposteur', {
      method: 'POST',
      body: JSON.stringify({ game_id: gameId, voted_for_user_id: votedForUserId })
    });
    // Recharge les resultats
    const data = await api.request(`/games.php?action=get_game&game_id=${gameId}`);
    showGameResults(data.game);
  } catch(e) { alert(e.message || 'Erreur'); }
}

async function confirmChoice(message) {
  const result = confirm(message);
  return result && typeof result.then === 'function' ? await result : !!result;
}

function isSoloMiniGameActive() {
  return !!(
    window.soloMiniGameActive ||
    (gameState.game && gameState.game.localSolo) ||
    (timelineState.game && timelineState.game.localSolo) ||
    (popcornState.game && popcornState.game.localSolo) ||
    (geoState.game && geoState.game.localSolo) ||
    (sondageState.game && sondageState.game.localSolo) ||
    (justePrixState.game && justePrixState.game.localSolo)
  );
}

async function abandonGame() {
  if (!(await confirmChoice('Quitter ce jeu ? Ta progression sera perdue.'))) return;
  clearInterval(blitzTimerInterval);
  gameState.game = null;
  if (isSoloMiniGameActive() && typeof backToSoloPlay === 'function') {
    backToSoloPlay();
    return;
  }
  backToGroupDetail();
}

// --- LIGUE ---
async function showLeague(groupId) {
  const page = document.getElementById('challengePlayPage');
  showMultiPage('challengePlayPage');
  page.innerHTML = '<div class="card" style="text-align:center;padding:40px"><p class="muted">Chargement...</p></div>';
  try {
    const data = await api.request(`/games.php?action=league&group_id=${groupId}`);
    renderLeague(groupId, data);
  } catch(e) {
    page.innerHTML = '<div class="card"><p class="muted">Erreur de chargement.</p></div>';
  }
}

function renderLeague(groupId, data) {
  const page = document.getElementById('challengePlayPage');
  const current = data.current || [];
  const previous = data.previous || [];

  const podium = current.map((r, i) => `
    <div class="g-result-row ${i === 0 ? 'first' : ''}">
      <span class="g-result-rank">${i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '#'+(i+1)}</span>
      <span class="g-result-name">${escHtml(r.username)}</span>
      <span class="g-result-score">${r.points} pts</span>
    </div>`).join('');

  const prevPodium = previous.length > 0
    ? previous.map((r, i) => `
      <div class="g-result-row" style="opacity:.6">
        <span class="g-result-rank">#${i+1}</span>
        <span class="g-result-name">${escHtml(r.username)}</span>
        <span class="g-result-score">${r.points} pts</span>
      </div>`).join('')
    : '<p class="muted" style="text-align:center">Pas de donnees.</p>';

  page.innerHTML = `
    <div class="card" style="max-width:500px;margin:0 auto">
      <div class="between" style="margin-bottom:16px">
        <button class="btn small ghost" onclick="backToGroupDetail()">← Retour</button>
        <div class="badge">Semaine ${data.week}</div>
      </div>
      <h2 style="margin:0 0 16px;text-align:center">🏆 Ligue de la semaine</h2>
      ${current.length === 0
        ? '<p class="muted" style="text-align:center">Personne n\'a encore joué cette semaine.</p>'
        : `<div class="g-results-list">${podium}</div>`}
      <div class="g-section-title" style="margin-top:20px">Semaine precedente</div>
      <div class="g-results-list">${prevPodium}</div>
      <p class="muted" style="text-align:center;font-size:.8rem;margin-top:16px">
        La ligue se remet a zero chaque lundi. Points cumules sur tous les jeux.
      </p>
    </div>`;
}

// =====================================================================
// JEU GEO-DEVINETTE
// =====================================================================
const geoState = {
  game: null,
  questionIndex: 0,
  score: 0,
  attempts: 0,
  hintsShown: 1,
  finished: false,
  answers: [],
};

// Questions géographiques complexes avec indices progressifs (8 questions complètes)
const GEO_QUESTIONS = [
  {
    question: "De quel pays s'agit-il ?",
    hints: [
      "Ce pays possède la plus longue côte au monde avec plus de 202 000 km",
      "Il partage sa frontière terrestre uniquement avec les États-Unis",
      "Sa feuille d'érable rouge est l'un des symboles nationaux les plus reconnus"
    ],
    options: ["Canada", "Norvège", "Russie", "Australie", "Finlande", "Chili"],
    answer: "Canada",
    year: "1867 - Confédération canadienne",
    explanation: "🍁 <strong>Le Canada</strong> est le deuxième plus grand pays du monde par superficie (9,98 millions km²). Sa côte de 202 080 km est la plus longue au monde grâce à ses nombreuses îles arctiques. Le pays compte 10 provinces et 3 territoires, avec Ottawa comme capitale. Fait fascinant : le Canada possède plus de lacs que le reste du monde combiné (environ 2 millions de lacs de plus de 5 hectares) !"
  },
  {
    question: "Quel pays mystérieux se cache derrière ces indices ?",
    hints: [
      "Ce pays insulaire est composé de plus de 17 000 îles",
      "Il abrite la plus grande population musulmane au monde",
      "Sa capitale Jakarta s'enfonce de 25 cm par an dans la mer"
    ],
    options: ["Philippines", "Indonésie", "Malaisie", "Thaïlande", "Brunei", "Singapour"],
    answer: "Indonésie",
    year: "1945 - Indépendance de l'Indonésie",
    explanation: "🏝️ <strong>L'Indonésie</strong> est l'archipel le plus vaste du monde avec 17 508 îles officielles, dont seulement 6 000 sont habitées ! Avec 275 millions d'habitants, c'est le 4ème pays le plus peuplé. Jakarta, sa capitale de 10 millions d'habitants, s'enfonce dramatiquement à cause de la surexploitation des nappes phréatiques. Le pays prévoit de déménager sa capitale vers Nusantara (Bornéo) d'ici 2045. L'Indonésie s'étend sur 3 fuseaux horaires et possède une biodiversité exceptionnelle avec des espèces uniques comme le dragon de Komodo."
  },
  {
    question: "Identifie ce pays aux caractéristiques uniques :",
    hints: [
      "C'est le seul pays au monde sans capitale officielle",
      "Il possède 11 langues officielles, record mondial",
      "Ses trois capitales sont Cape Town, Pretoria et Bloemfontein"
    ],
    options: ["Afrique du Sud", "Nigeria", "Kenya", "Botswana", "Zimbabwe", "Namibie"],
    answer: "Afrique du Sud",
    year: "1994 - Fin de l'apartheid et démocratie",
    explanation: "🌍 <strong>L'Afrique du Sud</strong> est unique avec ses 3 capitales : Cape Town (législative), Pretoria (exécutive) et Bloemfontein (judiciaire). Ses 11 langues officielles incluent l'afrikaans, l'anglais, le zoulou, le xhosa... Le cratère de Vredefort (300 km de diamètre) date de 2 milliards d'années ! Le pays possède les plus grandes réserves d'or, de platine et de chrome au monde. Fait surprenant : il abrite le plus ancien désert du monde (Kalahari, 65 millions d'années) et la plus ancienne chaîne de montagnes (Barberton, 3,5 milliards d'années)."
  },
  {
    question: "Quel pays se cache derrière ces indices géographiques ?",
    hints: [
      "Il possède des frontières avec 14 pays, record mondial ex-aequo avec la Russie",
      "Sa muraille antique s'étend sur plus de 21 000 km au total",
      "Il abrite le plus haut plateau du monde, surnommé 'le toit du monde'"
    ],
    options: ["Chine", "Inde", "Mongolie", "Kazakhstan", "Pakistan", "Afghanistan"],
    answer: "Chine",
    year: "1949 - République populaire de Chine",
    explanation: "🐼 <strong>La Chine</strong> partage ses frontières avec 14 pays : Russie, Mongolie, Kazakhstan, Kirghizistan, Tadjikistan, Afghanistan, Pakistan, Inde, Népal, Bhoutan, Myanmar, Laos, Vietnam et Corée du Nord. La Grande Muraille, avec ses fortifications, murs et tranchées, s'étend sur 21 196 km ! Le plateau tibétain (4 500m d'altitude moyenne) couvre 2,5 millions km². Les pandas géants, symbole national, ne vivent que dans 3 provinces chinoises. Fait étonnant : la Chine a 56 groupes ethniques reconnus et utilise un seul fuseau horaire malgré sa taille continentale !"
  },
  {
    question: "Découvre ce pays aux records impressionnants :",
    hints: [
      "Il détient le record du plus grand nombre de fuseaux horaires : 11",
      "Sa superficie représente plus de 11% des terres émergées de la planète",
      "Il possède la plus grande forêt du monde, qui produit 20% de l'oxygène terrestre"
    ],
    options: ["Russie", "Canada", "États-Unis", "Brésil", "Australie", "Chine"],
    answer: "Russie",
    year: "1991 - Fédération de Russie moderne",
    explanation: "🐻 <strong>La Russie</strong> est le plus grand pays du monde (17,1 millions km²), soit 1,8 fois la taille des États-Unis ! Ses 11 fuseaux horaires s'étendent de Kaliningrad à Kamchatka. La taïga sibérienne couvre 5,1 millions km² (plus grande que l'Inde !). Le lac Baïkal, vieux de 25 millions d'années, est le plus profond (1 642m) et contient autant d'eau que les 5 Grands Lacs américains réunis. Fait incroyable : la Russie possède plus de forêts que l'Amazonie et ses réserves de gaz naturel représentent 24% des réserves mondiales !"
  },
  {
    question: "Identifie ce pays aux caractéristiques extraordinaires :",
    hints: [
      "C'est le seul pays au monde situé entièrement au-dessus de 1000m d'altitude",
      "Il est enclavé entre deux géants : la Chine et l'Inde",
      "Sa philosophie du 'Bonheur National Brut' remplace le PIB traditionnel"
    ],
    options: ["Bhoutan", "Népal", "Tibet", "Ladakh", "Sikkim", "Andorre"],
    answer: "Bhoutan",
    year: "1907 - Royaume du Bhoutan unifié",
    explanation: "🏔️ <strong>Le Bhoutan</strong> est un royaume unique de 770 000 habitants niché dans l'Himalaya. Son altitude minimale est de 1 400m ! Le pays mesure le 'Bonheur National Brut' basé sur 4 piliers : développement durable, conservation environnementale, préservation culturelle et bonne gouvernance. Grâce à sa constitution qui impose 60% de couverture forestière, le Bhoutan absorbe 6 millions de tonnes de CO₂ par an tout en n'en émettant que 2,3 millions. Fait unique : la télévision n'y est autorisée que depuis 1999 et le tourisme est strictement régulé !"
  },
  {
    question: "Quel pays insulaire se distingue par ces caractéristiques ?",
    hints: [
      "Il est composé de 7 641 îles, dont seulement 2 000 sont habitées",
      "C'est le seul pays asiatique majoritairement chrétien (80% catholique)",
      "Il possède la plus grande diversité linguistique au monde avec 180 langues"
    ],
    options: ["Philippines", "Indonésie", "Japon", "Malaisie", "Papouasie-Nouvelle-Guinée", "Fidji"],
    answer: "Philippines",
    year: "1946 - Indépendance des Philippines",
    explanation: "🏝️ <strong>Les Philippines</strong> forment un archipel de 7 641 îles s'étendant sur 1 840 km du nord au sud. Colonisées par l'Espagne pendant 333 ans, elles sont le 3ème pays catholique au monde. Leurs 180 langues en font le pays le plus linguistiquement diversifié ! Les terrasses de Banaue, vieilles de 2 000 ans, s'élèvent jusqu'à 1 500m d'altitude. Fait fascinant : l'archipel se situe sur la 'Ceinture de feu du Pacifique' avec 53 volcans actifs, et subit 20 typhons par an en moyenne. Manille, la capitale, est l'une des villes les plus densément peuplées au monde !"
  },
  {
    question: "Découvre ce pays aux records géographiques uniques :",
    hints: [
      "Il possède la plus haute chute d'eau du monde : 979 mètres de hauteur",
      "Ses tepuis (montagnes tabulaires) ont inspiré le film 'Là-haut' de Pixar",
      "Il détient les plus grandes réserves pétrolières prouvées au monde"
    ],
    options: ["Venezuela", "Colombie", "Guyana", "Brésil", "Suriname", "Équateur"],
    answer: "Venezuela",
    year: "1811 - Indépendance du Venezuela",
    explanation: "💧 <strong>Le Venezuela</strong> abrite le Salto Ángel (979m), 15 fois plus haut que les chutes du Niagara ! Ses tepuis du plateau guyanais sont des formations géologiques de 2 milliards d'années, isolées comme des îles dans le ciel. Avec 303 milliards de barils, ses réserves pétrolières dépassent celles de l'Arabie Saoudite. L'Orénoque, 3ème fleuve d'Amérique du Sud (2 140 km), traverse tout le pays. Fait remarquable : le Venezuela possède 43 parcs nationaux couvrant 15% du territoire, et le lac Maracaibo est le plus grand lac d'Amérique du Sud (13 210 km²) !"
  }
];

function startGeoGame(game) {
  stopPolling();
  geoState.game = game;
  
  // FORCER l'utilisation des questions locales améliorées (5 questions comme geo-test.html)
  geoState.game.localQuestions = GEO_QUESTIONS.sort(() => Math.random() - 0.5).slice(0, 5);
  
  geoState.questionIndex = 0;
  geoState.score = 0;
  geoState.answers = [];
  geoState.hintsShown = 1;
  geoState.attempts = 0;
  geoState.finished = false;
  showMultiPage('challengePlayPage');
  renderGeoQuestion();
}

function renderGeoQuestion() {
  const g = geoState.game;
  const page = document.getElementById('challengePlayPage');
  
  // Utiliser directement les questions locales (déjà initialisées dans startGeoGame)
  const q = g.localQuestions[geoState.questionIndex];
  const total = g.localQuestions.length;
  const pct = Math.round((geoState.questionIndex / total) * 100);

  // Afficher les indices déjà débloqués
  const hintsHtml = q.hints.slice(0, geoState.hintsShown).map((h, idx) =>
    `<div class="g-geo-hint" style="animation-delay:${idx * 0.1}s;margin-bottom:8px;padding:8px;background:rgba(16,185,129,0.1);border-radius:8px;">
      <span style="opacity:0.7;margin-right:6px">💡 Indice ${idx + 1}:</span>
      <span style="font-weight:600">${escHtml(h)}</span>
    </div>`
  ).join('');

  // Créer les boutons, puis brancher les clics après le rendu.
  // C'est plus fiable que du JS inline avec des réponses accentuées ou apostrophées.
  let optionsHtml = '';
  for (let i = 0; i < q.options.length; i++) {
    const opt = String(q.options[i]);
    optionsHtml += `<button type="button" class="option-btn geo-option" data-answer="${escDataAttr(opt)}" style="margin:4px;padding:12px 16px;border:2px solid #e9ecef;border-radius:8px;background:#fff;cursor:pointer;transition:all 0.2s;">${escHtml(opt)}</button>`;
  }

  // Calculer les points potentiels (8 pts avec 1 indice, 6 avec 2, 4 avec 3)
  const potentialPoints = Math.max(4, 10 - (geoState.hintsShown * 2));

  page.innerHTML = `
    <div class="quiz-layout">
      <div class="quiz-main">
        <div class="card geo-card">
          <div class="between">
            <button class="btn small ghost" onclick="abandonGame()">✕ Quitter</button>
            <div class="badge geo-badge">${gameIconHtml('geo')} Géo-devinette</div>
          </div>
          <div class="meta" style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap">
            <div class="badge">🗺️ Lieu ${geoState.questionIndex + 1}/${total}</div>
            <div class="badge">⭐ Score ${geoState.score}</div>
            <div class="badge ${geoState.attempts >= 3 ? 'badge-danger' : ''}">🎯 ${geoState.attempts}/4 tentatives</div>
            <div class="badge" style="background:linear-gradient(135deg,#10b981,#34d399);color:#fff">💎 ${potentialPoints} pts possibles</div>
          </div>
          <div class="progress" style="margin:12px 0"><span style="width:${pct}%"></span></div>
          
          <div class="geo-question-box" style="margin:16px 0;text-align:center;">
            <div class="geo-title" style="font-size:1.2rem;font-weight:600;color:var(--accent);margin-bottom:8px;">${escHtml(q.question)}</div>
            <div class="geo-subtitle" style="color:#666;font-size:0.9rem;">Utilise les indices ci-dessous pour deviner !</div>
          </div>
          
          <div id="geoHints" class="geo-hints-container" style="margin:16px 0;">${hintsHtml}</div>
          
          <div class="answers geo-answers" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;margin:16px 0;">${optionsHtml}</div>
          <div class="feedback" id="geoFeedback"></div>
          <button class="btn primary" id="geoNextBtn" style="display:none;margin-top:12px" data-geo-action="next">
            ${geoState.questionIndex < total - 1 ? '🗺️ Lieu suivant →' : '🏆 Voir les résultats'}
          </button>
        </div>
      </div>
    </div>`;

  geoState.finished = false;
}

document.addEventListener('click', function(e) {
  const btn = e.target.closest && e.target.closest('.geo-option');
  if (btn) {
    e.preventDefault();
    if (!geoState.finished && !btn.disabled) answerGeo(btn.getAttribute('data-answer') || '');
    return;
  }

  const nextBtn = e.target.closest && e.target.closest('[data-geo-action="next"]');
  if (nextBtn) {
    e.preventDefault();
    nextGeoQuestion();
  }
});

async function answerGeo(answer) {
  if (geoState.finished) return;
  const g = geoState.game;
  const q = g.localQuestions[geoState.questionIndex];

  geoState.attempts++;
  const correct = answer === q.answer;

  if (correct) {
    geoState.finished = true;
    const points = Math.max(4, 10 - (geoState.hintsShown * 2));
    geoState.score += points;
    geoState.answers.push({ qi: geoState.questionIndex, correct: true, pts: points });
    
      document.querySelectorAll('.geo-option').forEach(b => { 
        b.disabled = true; 
        if ((b.getAttribute('data-answer') || '') === answer) b.style.background = '#dcfce7';
      });
    
    const fb = document.getElementById('geoFeedback');
    if (fb) { 
      fb.innerHTML = `
        <div style="background:#dcfce7;color:#15803d;padding:16px;border-radius:10px;margin-top:16px;">
          ✅ +${points} pts !
          <div style="margin-top:12px;padding-top:12px;border-top:1px solid rgba(0,0,0,0.1);">${q.explanation}</div>
          <div style="margin-top:12px;padding:8px;background:rgba(16,185,129,0.1);border-radius:6px;text-align:center;font-weight:600;">
            📅 ${q.year}
          </div>
        </div>
      `;
    }
    document.getElementById('geoNextBtn').style.display = 'block';
  } else {
    if (geoState.attempts >= 4) {
      geoState.finished = true;
      geoState.answers.push({ qi: geoState.questionIndex, correct: false, pts: 0 });
      
      document.querySelectorAll('.geo-option').forEach(b => { 
        b.disabled = true; 
        const btnAnswer = b.getAttribute('data-answer') || '';
        if (btnAnswer === q.answer) b.style.background = '#dcfce7';
        else if (btnAnswer === answer) b.style.background = '#fef2f2';
      });
      
      const fb = document.getElementById('geoFeedback');
      if (fb) { 
        fb.innerHTML = `
          <div style="background:#fef2f2;color:#dc2626;padding:16px;border-radius:10px;margin-top:16px;">
            ❌ C'était : <strong>${q.answer}</strong>
            <div style="margin-top:12px;padding-top:12px;border-top:1px solid rgba(0,0,0,0.1);">${q.explanation}</div>
            <div style="margin-top:12px;padding:8px;background:rgba(16,185,129,0.1);border-radius:6px;text-align:center;font-weight:600;">
              📅 ${q.year}
            </div>
          </div>
        `;
      }
      document.getElementById('geoNextBtn').style.display = 'block';
    } else {
      // Afficher un indice supplémentaire si disponible
      if (geoState.hintsShown < 3) {
        geoState.hintsShown++;
        const hintsHtml = q.hints.slice(0, geoState.hintsShown).map((h, idx) =>
          `<div class="g-geo-hint" style="animation-delay:${idx * 0.1}s;margin-bottom:8px;padding:8px;background:rgba(16,185,129,0.1);border-radius:8px;">
            <span style="opacity:0.7;margin-right:6px">💡 Indice ${idx + 1}:</span>
            <span style="font-weight:600">${escHtml(h)}</span>
          </div>`
        ).join('');
        const hintEl = document.getElementById('geoHints');
        if (hintEl) hintEl.innerHTML = hintsHtml;
      }
      
      const fb = document.getElementById('geoFeedback');
      if (fb) { 
        fb.innerHTML = `<div style="background:#fef2f2;color:#dc2626;padding:12px;border-radius:8px;margin-top:12px;">❌ Mauvaise réponse. ${4 - geoState.attempts} tentative${4 - geoState.attempts > 1 ? 's' : ''} restante${4 - geoState.attempts > 1 ? 's' : ''}.</div>`;
        setTimeout(() => { fb.innerHTML = ''; }, 2000);
      }
      
      // Marquer temporairement la mauvaise réponse
      document.querySelectorAll('.geo-option').forEach(b => { 
        if ((b.getAttribute('data-answer') || '') === answer) {
          b.style.background = '#fef2f2';
          setTimeout(() => { b.style.background = '#fff'; }, 1000);
        }
      });
    }
  }
}

function nextGeoQuestion() {
  geoState.questionIndex++;
  geoState.hintsShown = 1;
  geoState.attempts = 0;
  if (geoState.questionIndex >= geoState.game.localQuestions.length) {
    submitGeoGame();
  } else {
    renderGeoQuestion();
  }
}

async function submitGeoGame() {
  const g = geoState.game;
  try {
    await api.request('/games.php?action=submit_game', {
      method: 'POST',
      body: JSON.stringify({ 
        game_id: g.id, 
        score: geoState.score, 
        good: geoState.answers.filter(a => a.correct).length, 
        bad: geoState.answers.filter(a => !a.correct).length, 
        time_ms: 0 
      })
    });
    const data = await api.request(`/games.php?action=get_game&game_id=${g.id}`);
    showGameResults(data.game);
  } catch(e) { 
    alert(e.message || 'Erreur'); 
  }
}

// =====================================================================
// GEO-DEVINETTE SOLO INTEGREE AU SITE
// =====================================================================
const soloGeoState = {
  questions: [],
  index: 0,
  score: 0,
  good: 0,
  bad: 0,
  attempts: 0,
  hintsShown: 1,
  finished: false
};

function startSoloGeoGame() {
  soloGeoState.questions = [...GEO_QUESTIONS].sort(() => Math.random() - 0.5).slice(0, 5);
  soloGeoState.index = 0;
  soloGeoState.score = 0;
  soloGeoState.good = 0;
  soloGeoState.bad = 0;
  soloGeoState.attempts = 0;
  soloGeoState.hintsShown = 1;
  soloGeoState.finished = false;
  if (typeof state !== 'undefined') {
    state.category = 'geo_solo';
    state.mode = 'geo';
    state.gameMode = 'normal';
  }
  if (typeof showPage === 'function') showPage('quizPage');
  renderSoloGeoQuestion();
}

function renderSoloGeoHints() {
  const q = soloGeoState.questions[soloGeoState.index];
  const hintsEl = document.getElementById('soloGeoHints');
  if (!q || !hintsEl) return;
  hintsEl.innerHTML = q.hints.slice(0, soloGeoState.hintsShown).map((hint, index) => `
    <div class="g-geo-hint">
      <span style="opacity:.75;margin-right:6px">💡 Indice ${index + 1} :</span>
      <span style="font-weight:700">${escHtml(hint)}</span>
    </div>
  `).join('');
}

function renderSoloGeoQuestion() {
  const q = soloGeoState.questions[soloGeoState.index];
  if (!q) {
    finishSoloGeoGame();
    return;
  }

  const total = soloGeoState.questions.length;
  const potentialPoints = Math.max(4, 10 - (soloGeoState.hintsShown * 2));
  const optionsHtml = q.options.map(opt => `
    <button type="button" class="answer solo-geo-option" data-answer="${escDataAttr(opt)}">${escHtml(opt)}</button>
  `).join('');

  document.getElementById('categoryBadge').textContent = 'Géo-devinette';
  document.getElementById('modeLabel').textContent = 'Géo-devinette · Indices';
  document.getElementById('qNum').textContent = soloGeoState.index + 1;
  document.getElementById('qTot').textContent = total;
  document.getElementById('scoreNum').textContent = soloGeoState.score;
  document.getElementById('streakNum').textContent = soloGeoState.good;
  document.getElementById('difficulty').textContent = 'indices progressifs';
  document.getElementById('questionText').textContent = q.question;
  document.getElementById('questionContext').textContent = 'Devine le pays. Une erreur débloque un indice, tu as 4 tentatives.';
  document.getElementById('progressBar').style.width = `${(soloGeoState.index / total) * 100}%`;
  document.getElementById('goodCount').textContent = soloGeoState.good;
  document.getElementById('badCount').textContent = soloGeoState.bad;
  document.getElementById('bestStreak').textContent = soloGeoState.good;
  document.getElementById('livesDisplay').style.display = 'none';
  document.getElementById('feedback').textContent = '';
  document.getElementById('feedback').className = 'feedback';
  document.getElementById('freeWrap').classList.add('hidden');
  document.getElementById('qcmWrap').classList.remove('hidden');
  document.getElementById('qcmWrap').innerHTML = `
    <div class="meta" style="margin-bottom:12px">
      <div class="badge">🎯 ${soloGeoState.attempts}/4 tentatives</div>
      <div class="badge">💎 ${potentialPoints} pts possibles</div>
    </div>
    <div id="soloGeoHints" class="geo-hints-container" style="margin:12px 0"></div>
    <div class="answers">${optionsHtml}</div>
    <button type="button" class="btn primary" id="soloGeoNextBtn" style="display:none;margin-top:12px" onclick="nextSoloGeoQuestion()">
      ${soloGeoState.index < total - 1 ? 'Lieu suivant →' : 'Voir les résultats'}
    </button>
  `;
  soloGeoState.finished = false;
  renderSoloGeoHints();
}

document.addEventListener('click', function(e) {
  const btn = e.target.closest && e.target.closest('.solo-geo-option');
  if (!btn || soloGeoState.finished) return;
  e.preventDefault();
  answerSoloGeo(btn.getAttribute('data-answer') || '');
});

function answerSoloGeo(answer) {
  if (soloGeoState.finished) return;
  const q = soloGeoState.questions[soloGeoState.index];
  if (!q) return;

  soloGeoState.attempts++;
  const correct = answer === q.answer;
  const fb = document.getElementById('feedback');

  if (correct) {
    soloGeoState.finished = true;
    const points = Math.max(4, 10 - (soloGeoState.hintsShown * 2));
    soloGeoState.score += points;
    soloGeoState.good++;
    document.getElementById('scoreNum').textContent = soloGeoState.score;
    document.getElementById('goodCount').textContent = soloGeoState.good;
    document.querySelectorAll('.solo-geo-option').forEach(btn => {
      btn.disabled = true;
      if ((btn.getAttribute('data-answer') || '') === q.answer) btn.classList.add('correct');
    });
    if (fb) {
      fb.className = 'feedback has-content';
      fb.innerHTML = `✅ Bonne réponse ! +${points} pts <div class="explanation">${q.explanation}</div><div class="country-year"><strong>📅 ${q.year}</strong></div>`;
    }
    document.getElementById('soloGeoNextBtn').style.display = 'block';
    return;
  }

  if (soloGeoState.attempts >= 4) {
    soloGeoState.finished = true;
    soloGeoState.bad++;
    document.getElementById('badCount').textContent = soloGeoState.bad;
    document.querySelectorAll('.solo-geo-option').forEach(btn => {
      btn.disabled = true;
      const btnAnswer = btn.getAttribute('data-answer') || '';
      if (btnAnswer === q.answer) btn.classList.add('correct');
      if (btnAnswer === answer) btn.classList.add('wrong');
    });
    if (fb) {
      fb.className = 'feedback has-content';
      fb.innerHTML = `❌ C'était <strong>${escHtml(q.answer)}</strong>.<div class="explanation">${q.explanation}</div><div class="country-year"><strong>📅 ${q.year}</strong></div>`;
    }
    document.getElementById('soloGeoNextBtn').style.display = 'block';
    return;
  }

  if (soloGeoState.hintsShown < 3) {
    soloGeoState.hintsShown++;
    renderSoloGeoHints();
  }

  document.querySelectorAll('.solo-geo-option').forEach(btn => {
    if ((btn.getAttribute('data-answer') || '') === answer) {
      btn.classList.add('wrong');
      setTimeout(() => btn.classList.remove('wrong'), 900);
    }
  });
  if (fb) {
    fb.className = 'feedback has-content';
    fb.textContent = `Mauvaise réponse. ${4 - soloGeoState.attempts} tentative${4 - soloGeoState.attempts > 1 ? 's' : ''} restante${4 - soloGeoState.attempts > 1 ? 's' : ''}.`;
  }
  renderSoloGeoQuestionMeta();
}

function renderSoloGeoQuestionMeta() {
  const potentialPoints = Math.max(4, 10 - (soloGeoState.hintsShown * 2));
  const qcmWrap = document.getElementById('qcmWrap');
  if (!qcmWrap) return;
  const meta = qcmWrap.querySelector('.meta');
  if (meta) {
    meta.innerHTML = `
      <div class="badge">🎯 ${soloGeoState.attempts}/4 tentatives</div>
      <div class="badge">💎 ${potentialPoints} pts possibles</div>
    `;
  }
}

function nextSoloGeoQuestion() {
  soloGeoState.index++;
  soloGeoState.attempts = 0;
  soloGeoState.hintsShown = 1;
  soloGeoState.finished = false;
  if (soloGeoState.index >= soloGeoState.questions.length) {
    finishSoloGeoGame();
  } else {
    renderSoloGeoQuestion();
  }
}

function finishSoloGeoGame() {
  const total = soloGeoState.questions.length;
  document.getElementById('categoryBadge').textContent = 'Géo-devinette';
  document.getElementById('modeLabel').textContent = 'Résultats';
  document.getElementById('qNum').textContent = total;
  document.getElementById('qTot').textContent = total;
  document.getElementById('progressBar').style.width = '100%';
  document.getElementById('questionText').textContent = 'Géo-devinette terminée';
  document.getElementById('questionContext').textContent = `${soloGeoState.good} bonne${soloGeoState.good > 1 ? 's' : ''} réponse${soloGeoState.good > 1 ? 's' : ''} sur ${total}.`;
  document.getElementById('feedback').textContent = '';
  document.getElementById('feedback').className = 'feedback';
  document.getElementById('freeWrap').classList.add('hidden');
  document.getElementById('qcmWrap').classList.remove('hidden');
  document.getElementById('qcmWrap').innerHTML = `
    <div class="sondage-result">
      <strong>Score final : ${soloGeoState.score} points</strong>
      <span>${soloGeoState.good} réussites · ${soloGeoState.bad} erreurs</span>
      <button type="button" class="btn primary full" onclick="startSoloGeoGame()">Rejouer</button>
      <button type="button" class="btn ghost full" onclick="showPage('homePage')">Accueil</button>
    </div>
  `;
}

// =====================================================================
// JEU SONDAGE
// =====================================================================
const sondageState = { game: null, answers: [], index: 0 };

// Fonctions globales pour le slider (AVANT renderSondageQuestion)
window.updateSondageFromSlider = function(value) {
  const display = document.getElementById('sondageSliderVal');
  const input = document.getElementById('sondageInput');
  if (display) display.textContent = value + '%';
  if (input) input.value = value;
  console.log('Slider updated to:', value);
};

window.updateSondageFromInput = function(value) {
  const val = Math.max(0, Math.min(100, parseInt(value) || 50));
  const slider = document.getElementById('sondageSlider');
  const display = document.getElementById('sondageSliderVal');
  if (slider) slider.value = val;
  if (display) display.textContent = val + '%';
  console.log('Input updated to:', val);
};

function startSondageGame(game) {
  stopPolling();
  sondageState.game = game;
  sondageState.answers = [];
  sondageState.index = 0;
  showMultiPage('challengePlayPage');
  renderSondageQuestion();
}

function renderSondageQuestion() {
  const g = sondageState.game;
  const page = document.getElementById('challengePlayPage');
  const q = g.questions[sondageState.index];
  const total = g.questions.length;
  const pct = Math.round((sondageState.index / total) * 100);

  page.innerHTML = `
    <div class="quiz-layout"><div class="quiz-main"><div class="card">
      <div class="between">
        <button class="btn small ghost" id="sondageAbandonBtn">✕ Quitter</button>
        <div class="badge">${gameIconHtml('sondage')} Sondage</div>
      </div>
      <div class="meta" style="margin-top:12px">
        <div class="badge">Question ${sondageState.index + 1}/${total}</div>
      </div>
      <div class="progress"><span style="width:${pct}%"></span></div>
      <div class="question">${escHtml(q.question)}</div>
      <p class="muted" style="font-size:.85rem">Estime un pourcentage entre 0 et 100.</p>
      <div style="margin:16px 0">
        <input id="sondageInput" type="number" min="0" max="100" class="input" placeholder="Ton estimation (0-100)" style="font-size:1.2rem;text-align:center" value="50" />
        <div id="sondageSliderVal" style="text-align:center;font-size:2rem;font-weight:900;margin:8px 0;color:var(--accent)">50%</div>
        <input id="sondageSlider" type="range" min="0" max="100" value="50" 
          style="width:100%;height:12px;cursor:pointer;-webkit-appearance:none;appearance:none;background:var(--bg2);border-radius:999px;border:2px solid var(--line);outline:none" />
      </div>
      <button class="btn primary" id="sondageSubmitBtn" style="width:100%" onclick="window.submitSondageAnswer()">Valider →</button>
    </div></div></div>`;
  
  // Attacher les event listeners pour le slider
  setTimeout(() => {
    const slider = document.getElementById('sondageSlider');
    const input = document.getElementById('sondageInput');
    const display = document.getElementById('sondageSliderVal');
    
    if (slider) {
      slider.addEventListener('input', (e) => {
        const val = e.target.value;
        if (display) display.textContent = val + '%';
        if (input) input.value = val;
      });
    }
    
    if (input) {
      input.addEventListener('input', (e) => {
        const val = Math.max(0, Math.min(100, parseInt(e.target.value) || 50));
        if (slider) slider.value = val;
        if (display) display.textContent = val + '%';
      });
    }
  }, 50);
}

function submitSondageAnswer() {
  const input = document.getElementById('sondageInput');
  const val = parseInt(input ? input.value : '50');
  if (isNaN(val) || val < 0 || val > 100) { alert('Entre un nombre entre 0 et 100'); return; }

  const q = sondageState.game.questions[sondageState.index];
  const correct = parseInt(q.answer);
  const diff = Math.abs(val - correct);
  const pts = Math.max(0, 10 - Math.floor(diff / 2));

  sondageState.answers.push({ question_index: sondageState.index, estimate: val });

  // Affiche le résultat de cette question
  const page = document.getElementById('challengePlayPage');
  const total = sondageState.game.questions.length;
  const isLast = sondageState.index >= total - 1;

  page.innerHTML = `
    <div class="quiz-layout"><div class="quiz-main"><div class="card">
      <div class="question">${escHtml(q.question)}</div>
      <div class="g-estimate-result">
        <div class="g-estimate-row"><span>Ta réponse</span><strong>${val}%</strong></div>
        <div class="g-estimate-row correct"><span>Bonne réponse</span><strong>${correct}%</strong></div>
        <div class="g-estimate-row"><span>Ecart</span><strong>${diff}%</strong></div>
        <div class="g-estimate-row"><span>Points</span><strong>+${pts} pts</strong></div>
      </div>
      <p class="muted" style="font-size:.85rem;margin-top:8px">${escHtml(q.explanation || '')}</p>
      <button class="btn primary" id="sondageNextBtn" style="width:100%;margin-top:16px" onclick="window.nextSondageQuestion()">
        ${isLast ? 'Voir les résultats 🏆' : 'Question suivante →'}
      </button>
    </div></div></div>`;

  sondageState.index++;
}

async function nextSondageQuestion() {
  if (sondageState.index >= sondageState.game.questions.length) {
    // Soumet tout
    try {
      const data = await api.request('/games.php?action=submit_sondage', {
        method: 'POST',
        body: JSON.stringify({ game_id: sondageState.game.id, answers: sondageState.answers })
      });
      const gameData = await api.request(`/games.php?action=get_game&game_id=${sondageState.game.id}`);
      showGameResults(gameData.game);
    } catch(e) { alert(e.message || 'Erreur'); }
  } else {
    renderSondageQuestion();
  }
}

// =====================================================================
// JEU JUSTE PRIX
// =====================================================================
const justePrixState = { game: null, answers: [], index: 0 };

// Explications détaillées pour enrichir les réponses
const JUSTE_PRIX_EXPLANATIONS = {
  // Exemples d'explications enrichies - à adapter selon tes questions
  "population": "🌍 <strong>Contexte démographique :</strong> La population mondiale a atteint 8 milliards d'habitants en novembre 2022. Elle augmente d'environ 80 millions de personnes par an, soit l'équivalent de la population de l'Allemagne ! Les projections indiquent un pic à 10,4 milliards vers 2080, avant une possible décroissance. L'Asie concentre 60% de l'humanité, l'Afrique connaît la croissance la plus rapide (+2,5%/an), tandis que l'Europe et l'Asie de l'Est voient leur population stagner ou diminuer.",
  
  "distance": "🚀 <strong>Échelles astronomiques :</strong> Les distances dans l'espace sont vertigineuses. La Lune est à 384 400 km (1,3 seconde-lumière), le Soleil à 150 millions de km (8 minutes-lumière), et l'étoile la plus proche, Proxima du Centaure, à 4,24 années-lumière (40 000 milliards de km !). Pour mettre en perspective : si la Terre était une bille de 1 cm, la Lune serait à 30 cm, le Soleil serait un ballon de 1,09 m à 117 mètres, et Proxima serait à 31 000 km !",
  
  "vitesse": "⚡ <strong>Records de vitesse :</strong> La vitesse de la lumière (299 792 km/s) est la limite absolue de l'univers. Aucune information ne peut voyager plus vite. Les sondes spatiales les plus rapides atteignent 150 km/s (Parker Solar Probe), soit 0,05% de la vitesse lumière. Un avion de ligne vole à 900 km/h (0,25 km/s), le son se propage à 340 m/s dans l'air, et un guépard court à 30 m/s. L'échelle des vitesses dans l'univers est immense !",
  
  "temperature": "🌡️ <strong>Échelle des températures :</strong> Le zéro absolu (-273,15°C) est la température la plus basse possible, où l'agitation moléculaire est minimale. Le cœur du Soleil atteint 15 millions de degrés, sa surface 5 500°C. Sur Terre, le record de froid est -89,2°C (Antarctique) et de chaud +56,7°C (Vallée de la Mort). Le corps humain fonctionne dans une plage étroite autour de 37°C. Les températures extrêmes de l'univers vont de 10⁻¹⁵ K (expansion de l'univers) à 10³² K (Big Bang) !",
  
  "hauteur": "🏔️ <strong>Altitudes remarquables :</strong> L'Everest culmine à 8 849 m, mais ce n'est pas le point le plus éloigné du centre de la Terre : c'est le Chimborazo (Équateur, 6 263 m) grâce au renflement équatorial. Le point le plus profond des océans est la fosse des Mariannes (-10 984 m). Les avions de ligne volent à 10-12 km d'altitude, la Station Spatiale Internationale orbite à 400 km, et les satellites géostationnaires à 36 000 km. L'atmosphère s'étend jusqu'à 10 000 km mais devient très ténue au-delà de 100 km (ligne de Kármán).",
  
  "poids": "⚖️ <strong>Masses et poids :</strong> La masse est une propriété intrinsèque d'un objet, tandis que le poids dépend de la gravité. Sur la Lune, tu pèserais 6 fois moins ! Les ordres de grandeur : un grain de riz (20 mg), une pomme (200 g), un humain (70 kg), une voiture (1 500 kg), un éléphant (6 tonnes), une baleine bleue (180 tonnes), un Airbus A380 (575 tonnes à vide). La Terre pèse 6 × 10²⁴ kg, le Soleil 2 × 10³⁰ kg (333 000 fois la Terre) !",
  
  "duree": "⏱️ <strong>Échelles de temps :</strong> Un battement de cœur dure 0,8 seconde, un clignement d'œil 0,1 s. Une journée fait 86 400 secondes, une année 31,5 millions de secondes. L'espérance de vie humaine est d'environ 2,5 milliards de secondes (80 ans). L'univers existe depuis 13,8 milliards d'années (4,4 × 10¹⁷ secondes). Les dinosaures ont régné pendant 165 millions d'années, Homo sapiens n'existe que depuis 300 000 ans. La civilisation humaine (agriculture, villes) n'a que 10 000 ans !",
  
  "superficie": "🗺️ <strong>Surfaces terrestres :</strong> La Terre a une superficie de 510 millions de km², dont 71% d'océans (361 M km²) et 29% de terres émergées (149 M km²). La Russie est le plus grand pays (17 M km²), le Vatican le plus petit (0,44 km²). La France métropolitaine fait 551 695 km², Paris 105 km². Un terrain de football fait 7 140 m², un appartement moyen 60 m². Pour visualiser : 1 km² = 100 hectares = 10 000 ares = 1 000 000 m².",
  
  "energie": "⚡ <strong>Puissances et énergies :</strong> L'énergie se mesure en joules (J) ou en watts-heure (Wh). Une ampoule LED de 10W consomme 10 J par seconde. Un humain au repos dépense 80W (comme une ampoule !), en effort 400W. Une voiture développe 100 000W (100 kW), une centrale nucléaire 1 000 000 000W (1 GW). Le Soleil émet 3,8 × 10²⁶ W ! Une bombe atomique libère 10¹⁵ J, un tremblement de terre majeur 10¹⁸ J, l'impact qui a tué les dinosaures 4 × 10²³ J.",
  
  "prix": "💰 <strong>Ordres de grandeur économiques :</strong> Un café coûte 2€, un smartphone 500€, une voiture 25 000€, une maison 250 000€. Le SMIC annuel en France est de 20 000€, le salaire médian 30 000€. Le PIB français est de 2 800 milliards €, le PIB mondial 100 000 milliards $. Les plus grandes fortunes dépassent 200 milliards $. Pour visualiser : 1 million de secondes = 11,5 jours, 1 milliard de secondes = 31,7 ans, 1 000 milliards de secondes = 31 700 ans !"
};

function startJustePrixGame(game) {
  stopPolling();
  justePrixState.game = game;
  justePrixState.answers = [];
  justePrixState.index = 0;
  showMultiPage('challengePlayPage');
  renderJustePrixQuestion();
}

function renderJustePrixQuestion() {
  const g = justePrixState.game;
  const page = document.getElementById('challengePlayPage');
  const q = g.questions[justePrixState.index];
  const total = g.questions.length;
  const pct = Math.round((justePrixState.index / total) * 100);

  page.innerHTML = `
    <div class="quiz-layout"><div class="quiz-main"><div class="card">
      <div class="between">
        <button class="btn small ghost" onclick="window.abandonGame()">✕ Quitter</button>
        <div class="badge">${gameIconHtml('juste_prix')} Le Juste Prix</div>
      </div>
      <div class="meta" style="margin-top:12px">
        <div class="badge">Question ${justePrixState.index + 1}/${total}</div>
      </div>
      <div class="progress"><span style="width:${pct}%"></span></div>
      <div class="question">${escHtml(q.question)}</div>
      <p class="muted" style="font-size:.9rem;margin:12px 0">💡 Entre ta meilleure estimation. Le plus proche gagne !</p>
      <div style="margin:20px 0">
        <input id="justePrixInput" type="number" class="input" placeholder="Ta réponse..." 
          style="font-size:1.5rem;text-align:center;padding:16px;border:3px solid var(--accent);border-radius:12px" 
          autofocus />
      </div>
      <button class="btn primary" id="justePrixSubmitBtn" style="width:100%;font-size:1.1rem;padding:14px" onclick="window.submitJustePrixAnswer()">Valider ma réponse →</button>
    </div></div></div>`;

  setTimeout(() => {
    const inp = document.getElementById('justePrixInput');
    if (inp) {
      inp.focus();
      inp.addEventListener('keydown', e => { if (e.key === 'Enter') window.submitJustePrixAnswer(); });
    }
  }, 100);
}

function scoreJustePrixEstimate(estimate, correct) {
  const diff = Math.abs(estimate - correct);
  const rawDiffPct = correct > 0 ? (diff / correct) * 100 : (estimate === 0 ? 0 : 100);
  const diffPct = Math.round(rawDiffPct);
  const precision = Math.max(0, Math.round(100 - rawDiffPct));
  const points = Math.max(0, Math.min(20, Math.round(precision / 5)));
  return { diff, diffPct, precision, points };
}

function submitJustePrixAnswer() {
  const input = document.getElementById('justePrixInput');
  const val = parseFloat(input ? input.value : '');
  if (isNaN(val) || val < 0) { alert('Entre un nombre valide (positif)'); return; }

  const q = justePrixState.game.questions[justePrixState.index];
  const correct = parseFloat(q.answer);
  const { diffPct, precision, points: pts } = scoreJustePrixEstimate(val, correct);

  justePrixState.answers.push({ question_index: justePrixState.index, estimate: val, points: pts, diff_pct: diffPct, precision });

  const page = document.getElementById('challengePlayPage');
  const total = justePrixState.game.questions.length;
  const isLast = justePrixState.index >= total - 1;

  const pointIcon = pts >= 20 ? reactionIconHtml('👏') : pts >= 18 ? reactionIconHtml('🔥') : pts >= 15 ? reactionIconHtml('🎉') : pts >= 10 ? reactionIconHtml('🤯') : reactionIconHtml('😤');
  
  // Message de feedback selon la précision
  let feedbackMsg = '';
  if (pts >= 20) {
    feedbackMsg = `${reactionIconHtml('👏')} <strong>PARFAIT !</strong> Tu as trouvé le juste prix !`;
  } else if (pts >= 18) {
    feedbackMsg = `${reactionIconHtml('🔥')} <strong>Excellent !</strong> Tu gardes au moins 90% de précision.`;
  } else if (pts >= 15) {
    feedbackMsg = `${reactionIconHtml('🎉')} <strong>Bien joué !</strong> Ton estimation reste très proche.`;
  } else if (pts >= 10) {
    feedbackMsg = `${reactionIconHtml('🤯')} <strong>Pas mal !</strong> Tu marques encore la moitié des points.`;
  } else {
    feedbackMsg = `${reactionIconHtml('😤')} <strong>Dommage !</strong> L'écart est trop grand, mais l'ordre de grandeur rentre.`;
  }
  
  // Enrichir l'explication si elle est courte
  let fullExplanation = q.explanation || '';
  
  // Si l'explication est courte (moins de 100 caractères), essayer de l'enrichir
  if (fullExplanation.length < 100) {
    // Chercher une explication enrichie basée sur des mots-clés dans la question
    const questionLower = q.question.toLowerCase();
    for (const [keyword, enrichedExpl] of Object.entries(JUSTE_PRIX_EXPLANATIONS)) {
      if (questionLower.includes(keyword)) {
        fullExplanation = (fullExplanation ? fullExplanation + '<br><br>' : '') + enrichedExpl;
        break;
      }
    }
  }
  
  // Si toujours pas d'explication, ajouter une explication générique basée sur l'ordre de grandeur
  if (!fullExplanation) {
    if (correct >= 1000000000) {
      fullExplanation = '<strong>Ordre de grandeur :</strong> On parle ici de milliards ! Pour visualiser : 1 milliard de secondes représente environ 31,7 années. Ces chiffres astronomiques sont difficiles à appréhender pour notre cerveau habitué aux quantités quotidiennes.';
    } else if (correct >= 1000000) {
      fullExplanation = '<strong>Ordre de grandeur :</strong> On parle ici de millions ! Un million de secondes représente environ 11,5 jours. Ces grandes quantités dépassent notre expérience quotidienne et nécessitent des comparaisons pour être comprises.';
    } else if (correct >= 1000) {
      fullExplanation = '<strong>Ordre de grandeur :</strong> On parle ici de milliers. C\'est une échelle que nous pouvons encore visualiser : un stade de football contient des milliers de spectateurs, une ville moyenne compte des dizaines de milliers d\'habitants.';
    } else if (correct >= 100) {
      fullExplanation = '<strong>Ordre de grandeur :</strong> On parle ici de centaines. C\'est une échelle familière : le nombre de pages d\'un livre, le nombre d\'élèves dans une école, la vitesse d\'une voiture sur autoroute en km/h.';
    } else {
      fullExplanation = '<strong>Ordre de grandeur :</strong> On parle ici de dizaines ou d\'unités. C\'est l\'échelle la plus intuitive pour nous, celle de notre vie quotidienne et de notre environnement immédiat.';
    }
  }

  page.innerHTML = `
    <div class="quiz-layout"><div class="quiz-main"><div class="card">
      <div class="question" style="margin-bottom:20px">${escHtml(q.question)}</div>
      
      <div style="background:var(--bg2);border-radius:12px;padding:20px;margin:16px 0">
        <div class="g-estimate-result">
          <div class="g-estimate-row"><span>Ta réponse</span><strong style="font-size:1.3rem">${val.toLocaleString('fr-FR')}</strong></div>
          <div class="g-estimate-row correct"><span>Juste prix</span><strong style="font-size:1.3rem;color:var(--accent)">${correct.toLocaleString('fr-FR')}</strong></div>
          <div class="g-estimate-row"><span>Écart</span><strong>${diffPct}%</strong></div>
          <div class="g-estimate-row"><span>Précision</span><strong>${precision}%</strong></div>
          <div class="g-estimate-row" style="border-top:2px solid var(--line);padding-top:12px;margin-top:12px">
            <span>${pointIcon} Points</span><strong style="font-size:1.4rem;color:var(--accent)">+${pts}/20</strong>
          </div>
        </div>
        <div class="g-score-rule" style="margin-top:14px;padding:12px;border-radius:10px;background:rgba(255,255,255,.06);border:1px solid var(--line);line-height:1.5">
          <strong>Calcul :</strong> précision ${precision}% ÷ 5 = <strong>${pts} point${pts > 1 ? 's' : ''}</strong>.
          <span class="muted" style="display:block;margin-top:4px">Barème : exact = 20/20, puis on perd des points selon l'écart.</span>
        </div>
      </div>
      
      <div style="background:${pts >= 5 ? 'rgba(0,200,100,0.1)' : 'rgba(255,100,100,0.1)'};border-radius:10px;padding:14px;margin:16px 0;border-left:4px solid ${pts >= 5 ? 'var(--accent)' : '#ff6b6b'}">
        <p style="margin:0;font-weight:600;font-size:1rem">${feedbackMsg}</p>
      </div>
      
      <div style="background:var(--card-bg);border-radius:10px;padding:18px;margin:16px 0;border:2px solid var(--line)">
        <div style="font-weight:700;margin-bottom:10px;color:var(--accent);font-size:1.05rem">${miniIcon('review')}Le savais-tu ?</div>
        <div style="margin:0;line-height:1.7;font-size:0.95rem">${fullExplanation}</div>
      </div>
      
      <button class="btn primary" id="justePrixNextBtn" style="width:100%;margin-top:16px;font-size:1.1rem;padding:14px" onclick="window.nextJustePrixQuestion()">
        ${isLast ? 'Voir les résultats 🏆' : 'Question suivante →'}
      </button>
    </div></div></div>`;

  justePrixState.index++;
}

async function nextJustePrixQuestion() {
  if (justePrixState.index >= justePrixState.game.questions.length) {
    if (justePrixState.game && justePrixState.game.localSolo) {
      const score = justePrixState.answers.reduce((sum, answer) => sum + (answer.points || 0), 0);
      const good = justePrixState.answers.filter(answer => (answer.points || 0) >= 15).length;
      const bad = justePrixState.answers.length - good;
      showSoloGameResults(justePrixState.game, score, good, bad, `
        <p class="muted" style="text-align:center;margin-top:14px">
          Score : chaque question vaut 20 pts. Points = précision en % ÷ 5.
        </p>
      `);
      return;
    }
    try {
      const data = await api.request('/games.php?action=submit_juste_prix', {
        method: 'POST',
        body: JSON.stringify({ game_id: justePrixState.game.id, answers: justePrixState.answers })
      });
      const gameData = await api.request(`/games.php?action=get_game&game_id=${justePrixState.game.id}`);
      showGameResults(gameData.game);
    } catch(e) { alert(e.message || 'Erreur'); }
  } else {
    renderJustePrixQuestion();
  }
}

// Patch showPage : reset modeSelector quand on revient sur homePage
window.addEventListener('load', function() {
  var _orig = window.showPage;
  window.showPage = function(id) {
    stopPolling();
    if (typeof _orig === 'function') _orig(id);
    if (id === 'homePage') {
      window.quizModeActive = false;
      var selector = document.getElementById('modeSelector');
      var soloContent = document.getElementById('soloContent');
      var playContent = document.getElementById('playContent');
      var nav = document.querySelector('.nav');
      if (selector) selector.style.display = 'grid';
      if (soloContent) soloContent.style.display = 'none';
      if (playContent) playContent.style.display = 'none';
      if (nav) nav.style.display = 'none';
      if (typeof renderStats === 'function') renderStats();
    }
  };
});


// Exposer les fonctions globalement pour les onclick
// enterMultiMode et exitMultiMode sont déjà exposés dans script.js
window.showGroupsPage = showGroupsPage;
window.showGroupDetail = showGroupDetail;
window.backToGroups = backToGroups;
window.showCreateGroup = showCreateGroup;
window.showJoinGroup = showJoinGroup;
window.submitJustePrixAnswer = submitJustePrixAnswer;
window.nextJustePrixQuestion = nextJustePrixQuestion;
window.answerGeo = answerGeo;
window.nextGeoQuestion = nextGeoQuestion;
window.startSoloGeoGame = startSoloGeoGame;
window.nextSoloGeoQuestion = nextSoloGeoQuestion;
window.showGroupOptions = showGroupOptions;
window.showNewChallenge = showNewChallenge;
window.launchChallenge = launchChallenge;
window.openChallenge = openChallenge;
window.abandonChallenge = abandonChallenge;
window.answerDailyBtn = answerDailyBtn;
window.sendChatMessage = sendChatMessage;
window.sendReaction = sendReaction;
window.abandonGame = abandonGame;
window.updateSondageFromSlider = updateSondageFromSlider;
window.updateSondageFromInput = updateSondageFromInput;
window.startGame = startGame;
window.startMemoryGame = startMemoryGame;
window.submitMemoryGame = submitMemoryGame;
window.startTimelineGame = startTimelineGame;
window.moveTimelineItem = moveTimelineItem;
window.checkTimelineGame = checkTimelineGame;
window.submitTimelineGame = submitTimelineGame;
window.startPopcornGame = startPopcornGame;
window.showMorePopcornHint = showMorePopcornHint;
window.answerPopcorn = answerPopcorn;
window.nextPopcornQuestion = nextPopcornQuestion;
window.startGeoGame = startGeoGame;
window.startSondageGame = startSondageGame;
window.startJustePrixGame = startJustePrixGame;
window.submitSondageAnswer = submitSondageAnswer;
window.nextSondageQuestion = nextSondageQuestion;
window.answerGame = answerGame;
window.nextGameQuestion = nextGameQuestion;
window.openGame = openGame;
window.launchGame = launchGame;
window.voteImposteur = voteImposteur;
window.showLeague = showLeague;
window.backToGroupDetail = backToGroupDetail;


// ═══════════════════════════════════════════════════════════════════════════
// JEU SONDAGE - Version simple et fonctionnelle
// ═══════════════════════════════════════════════════════════════════════════

// sondageState déjà déclaré plus haut (ligne ~1408)
// Réinitialisation des propriétés supplémentaires si nécessaire
if (typeof sondageState !== 'undefined') {
  sondageState.currentQuestionIndex = sondageState.currentQuestionIndex || 0;
  sondageState.totalScore = sondageState.totalScore || 0;
}

// Fonction pour démarrer le jeu sondage
function startSondageGame(game) {
  console.log('Démarrage du jeu sondage:', game);
  sondageState.game = game;
  sondageState.currentQuestionIndex = 0;
  sondageState.answers = [];
  sondageState.totalScore = 0;
  
  showMultiPage('challengePlayPage');
  renderSondageQuestion();
}

// Fonction pour afficher une question
function renderSondageQuestion() {
  const page = document.getElementById('challengePlayPage');
  const q = sondageState.game.questions[sondageState.currentQuestionIndex];
  const total = sondageState.game.questions.length;
  const progress = Math.round((sondageState.currentQuestionIndex / total) * 100);
  
  page.innerHTML = `
    <div class="card" style="max-width:600px;margin:20px auto;padding:30px">
      <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px">
        <button class="btn small ghost" onclick="quitSondageGame()">✕ Quitter</button>
        <div class="badge">${gameIconHtml('sondage')} Sondage</div>
      </div>
      
      <div style="margin-bottom:20px">
        <div class="badge">Question ${sondageState.currentQuestionIndex + 1}/${total}</div>
        <div class="badge">Score: ${sondageState.totalScore} pts</div>
      </div>
      
      <div class="progress" style="margin-bottom:20px">
        <span style="width:${progress}%;background:var(--accent);height:100%;display:block;border-radius:999px;transition:width 0.3s"></span>
      </div>
      
      <h3 style="margin:20px 0;font-size:1.2rem">${escHtml(q.question)}</h3>
      <p class="muted" style="margin-bottom:30px">Estime un pourcentage entre 0 et 100</p>
      
      <div id="sondageSliderContainer"></div>
      
      <button class="btn primary" style="width:100%;margin-top:30px" onclick="submitSondageAnswer()">
        Valider ma réponse →
      </button>
    </div>
  `;
  
  // Initialiser le slider après le rendu
  setTimeout(() => initSondageSlider(), 50);
}

// Exposer les fonctions globalement
window.startSondageGame = startSondageGame;
window.quitSondageGame = quitSondageGame;
window.submitSondageAnswer = submitSondageAnswer;


// Fonction pour initialiser le slider
function initSondageSlider() {
  const container = document.getElementById('sondageSliderContainer');
  if (!container) return;
  
  let currentValue = 50;
  
  container.innerHTML = `
    <div style="text-align:center;margin-bottom:30px">
      <div id="sondageValue" style="font-size:3rem;font-weight:900;color:var(--accent);margin-bottom:20px">
        50%
      </div>
      
      <div id="sliderTrack" style="position:relative;width:100%;height:20px;background:var(--bg3,#2a2a3e);border-radius:999px;cursor:pointer;border:2px solid var(--line,#444)">
        <div id="sliderFill" style="position:absolute;left:0;top:0;bottom:0;width:50%;background:var(--accent);border-radius:999px;pointer-events:none;transition:width 0.1s"></div>
        <div id="sliderThumb" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:36px;height:36px;background:var(--accent);border-radius:50%;cursor:grab;border:4px solid #fff;box-shadow:0 2px 12px rgba(0,0,0,0.4);z-index:10;transition:left 0.1s"></div>
      </div>
      
      <div style="display:flex;justify-content:space-between;margin-top:10px;font-size:0.85rem;opacity:0.6">
        <span>0%</span>
        <span>50%</span>
        <span>100%</span>
      </div>
    </div>
  `;
  
  const track = document.getElementById('sliderTrack');
  const thumb = document.getElementById('sliderThumb');
  const fill = document.getElementById('sliderFill');
  const valueDisplay = document.getElementById('sondageValue');
  
  let isDragging = false;
  
  function updateSlider(value) {
    currentValue = Math.max(0, Math.min(100, Math.round(value)));
    const percent = currentValue;
    thumb.style.left = percent + '%';
    fill.style.width = percent + '%';
    valueDisplay.textContent = currentValue + '%';
    
    // Stocker la valeur pour la soumission
    sondageState.currentAnswer = currentValue;
  }
  
  function getValueFromEvent(e) {
    const rect = track.getBoundingClientRect();
    const clientX = e.clientX || (e.touches && e.touches[0].clientX);
    const x = clientX - rect.left;
    const percent = (x / rect.width) * 100;
    return Math.max(0, Math.min(100, percent));
  }
  
  // Événements souris
  track.addEventListener('mousedown', (e) => {
    isDragging = true;
    thumb.style.cursor = 'grabbing';
    updateSlider(getValueFromEvent(e));
  });
  
  document.addEventListener('mousemove', (e) => {
    if (isDragging) {
      updateSlider(getValueFromEvent(e));
    }
  });
  
  document.addEventListener('mouseup', () => {
    if (isDragging) {
      isDragging = false;
      thumb.style.cursor = 'grab';
    }
  });
  
  // Événements tactiles
  track.addEventListener('touchstart', (e) => {
    isDragging = true;
    updateSlider(getValueFromEvent(e));
    e.preventDefault();
  });
  
  document.addEventListener('touchmove', (e) => {
    if (isDragging) {
      updateSlider(getValueFromEvent(e));
      e.preventDefault();
    }
  });
  
  document.addEventListener('touchend', () => {
    isDragging = false;
  });
  
  // Initialiser à 50%
  updateSlider(50);
}


// Fonction pour quitter le jeu
async function quitSondageGame() {
  if (!(await confirmChoice('Quitter le jeu ? Ta progression sera perdue.'))) return;
  const isSolo = isSoloMiniGameActive();
  sondageState.game = null;
  sondageState.currentQuestionIndex = 0;
  sondageState.answers = [];
  sondageState.totalScore = 0;
  if (isSolo && typeof backToSoloPlay === 'function') {
    backToSoloPlay();
    return;
  }
  backToGroupDetail();
}

// Fonction pour soumettre une réponse
function submitSondageAnswer() {
  const userAnswer = sondageState.currentAnswer || 50;
  const q = sondageState.game.questions[sondageState.currentQuestionIndex];
  const correctAnswer = parseInt(q.answer);
  const difference = Math.abs(userAnswer - correctAnswer);
  
  // Calcul du score : 10 pts si parfait, diminue avec la différence
  const points = Math.max(0, 10 - Math.floor(difference / 10));
  sondageState.totalScore += points;
  
  // Sauvegarder la réponse
  sondageState.answers.push({
    question_index: sondageState.currentQuestionIndex,
    user_answer: userAnswer,
    correct_answer: correctAnswer,
    points: points
  });
  
  // Afficher le résultat avec explications
  showSondageResult(userAnswer, correctAnswer, difference, points, q.explanation);
}

// Fonction pour afficher le résultat d'une question
function showSondageResult(userAnswer, correctAnswer, difference, points, explanation) {
  const page = document.getElementById('challengePlayPage');
  const isLast = sondageState.currentQuestionIndex >= sondageState.game.questions.length - 1;
  
  page.innerHTML = `
    <div class="card" style="max-width:600px;margin:20px auto;padding:30px">
      <h3 style="margin:0 0 30px;text-align:center">${miniIcon('stats')}Résultat</h3>
      
      <div style="background:var(--bg2);border-radius:12px;padding:20px;margin-bottom:20px">
        <div style="display:flex;justify-content:space-between;margin-bottom:15px;padding-bottom:15px;border-bottom:1px solid var(--line)">
          <span style="opacity:0.7">Ta réponse</span>
          <strong style="font-size:1.3rem">${userAnswer}%</strong>
        </div>
        
        <div style="display:flex;justify-content:space-between;margin-bottom:15px;padding-bottom:15px;border-bottom:1px solid var(--line)">
          <span style="opacity:0.7">Bonne réponse</span>
          <strong style="font-size:1.3rem;color:#10b981">${correctAnswer}%</strong>
        </div>
        
        <div style="display:flex;justify-content:space-between;margin-bottom:15px;padding-bottom:15px;border-bottom:1px solid var(--line)">
          <span style="opacity:0.7">Écart</span>
          <strong style="font-size:1.3rem;color:${difference <= 10 ? '#10b981' : '#ef4444'}">${difference}%</strong>
        </div>
        
        <div style="display:flex;justify-content:space-between">
          <span style="opacity:0.7">Points gagnés</span>
          <strong style="font-size:1.5rem;color:var(--accent)">+${points} pts</strong>
        </div>
      </div>
      
      ${explanation ? `
        <div style="background:var(--bg3);border-radius:12px;padding:20px;margin-bottom:20px;border-left:4px solid var(--accent)">
          <div style="font-weight:700;margin-bottom:10px;color:var(--accent)">💡 Explication</div>
          <p style="margin:0;line-height:1.6">${escHtml(explanation)}</p>
        </div>
      ` : ''}
      
      <button class="btn primary" style="width:100%;font-size:1.1rem" onclick="nextSondageQuestion()">
        ${isLast ? '🏆 Voir les résultats finaux' : 'Question suivante →'}
      </button>
    </div>
  `;
}

// Fonction pour passer à la question suivante
function nextSondageQuestion() {
  sondageState.currentQuestionIndex++;
  
  if (sondageState.currentQuestionIndex >= sondageState.game.questions.length) {
    // Fin du jeu - afficher les résultats finaux
    showSondageFinalResults();
  } else {
    // Question suivante
    renderSondageQuestion();
  }
}

// Fonction pour afficher les résultats finaux
function showSondageFinalResults() {
  const page = document.getElementById('challengePlayPage');
  
  page.innerHTML = `
    <div class="card" style="max-width:600px;margin:20px auto;padding:30px;text-align:center">
      <div style="margin-bottom:20px">${iconImg(GROUP_ICON_ASSETS.quiz, 'ui-mark ui-mark-quiz')}</div>
      <h2 style="margin:0 0 10px">Jeu terminé !</h2>
      <p style="font-size:2rem;font-weight:900;color:var(--accent);margin:20px 0">
        ${sondageState.totalScore} points
      </p>
      <p class="muted" style="margin-bottom:30px">
        ${sondageState.answers.length} questions répondues
      </p>
      <button class="btn primary" style="width:100%" onclick="backToGroupDetail()">
        ← Retour au groupe
      </button>
    </div>
  `;
}

// Exposer les nouvelles fonctions
window.nextSondageQuestion = nextSondageQuestion;
