{"id":23525,"date":"2026-04-22T06:34:53","date_gmt":"2026-04-22T06:34:53","guid":{"rendered":"https:\/\/www.transports-express-caraibes.fr\/blog\/panneaux-code-route\/"},"modified":"2026-04-22T06:34:53","modified_gmt":"2026-04-22T06:34:53","slug":"panneaux-code-route","status":"publish","type":"post","link":"https:\/\/www.transports-express-caraibes.fr\/blog\/panneaux-code-route\/","title":{"rendered":"Signification des panneaux du code de la route : liste compl\u00e8te et explications"},"content":{"rendered":"<ul class=\"wp-block-list\"><li><strong>En bref :<\/strong><\/li><li>Rep\u00e9rer la forme et la couleur d\u2019un panneau permet de deviner sa fonction en un coup d\u2019\u0153il.<\/li><li>Les <strong>panneaux de danger<\/strong> sont triangulaires, les <strong>panneaux d&rsquo;interdiction<\/strong> ronds \u00e0 liser\u00e9 rouge, les <strong>panneaux d&rsquo;obligation<\/strong> ronds \u00e0 fond bleu et les panneaux d\u2019indication carr\u00e9s.<\/li><li>La signalisation temporaire (fond <strong>jaune<\/strong>) pr\u00e9vaut sur la signalisation permanente.<\/li><li>En agglom\u00e9ration, la taille des panneaux est r\u00e9duite : visibilit\u00e9 ~50 m ; hors agglom\u00e9ration ~150 m.<\/li><li>Des panonceaux compl\u00e8tent souvent un panneau pour pr\u00e9ciser distance, cat\u00e9gorie de v\u00e9hicule ou \u00e9tendue.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\"><strong>Chap\u00f4 :<\/strong> Comprendre les panneaux du <strong>code de la route<\/strong> n\u2019est pas une affaire de m\u00e9moire pure : c\u2019est une m\u00e9thode. Ce guide synth\u00e9tique et pratique explique comment lire rapidement la <strong>signalisation routi\u00e8re<\/strong>, reconna\u00eetre les <strong>panneaux de danger<\/strong>, d\u2019obligation et d\u2019interdiction, et agir en s\u00e9curit\u00e9. Vous trouverez des rep\u00e8res visuels (formes, couleurs), des distances d\u2019alerte (150 m hors agglom\u00e9ration, 50 m en ville), des exemples concrets (zone 30, fin d\u2019interdiction, panonceaux de cat\u00e9gorie) et des exercices pour tester votre lecture des panneaux. Que vous pr\u00e9pariez l\u2019examen th\u00e9orique ou que vous souhaitiez simplement am\u00e9liorer vos r\u00e9actions au volant, les explications panneaux fournies ici visent l\u2019efficacit\u00e9 : savoir quoi faire, quand ralentir, quand c\u00e9der la priorit\u00e9 et comment interpr\u00e9ter un panonceau. Pour les trajets en territoires insulaires ou touristiques, quelques astuces pratiques de mobilit\u00e9 compl\u00e8tent le propos afin d\u2019\u00e9viter les erreurs les plus fr\u00e9quentes sur la route.<\/p>\n\n<h2 class=\"wp-block-heading\">Comment lire rapidement les panneaux de signalisation du code de la route<\/h2>\n\n<p class=\"wp-block-paragraph\">La bonne lecture commence par trois indices visibles en une seconde : la <strong>forme<\/strong>, la <strong>couleur<\/strong> et le <strong>pictogramme<\/strong>. Ces \u00e9l\u00e9ments combin\u00e9s donnent la cat\u00e9gorie du panneau et l\u2019action attendue.<\/p>\n\n<ul class=\"wp-block-list\"><li>Forme : triangle = danger\/priorit\u00e9 ; rond = interdiction ou obligation ; carr\u00e9\/rectangulaire = information ou service.<\/li><li>Couleur : fond bleu = indication\/obligation ; blanc = permanent ; jaune = temporaire (chantier, d\u00e9viation).<\/li><li>Pictogramme : symbole pr\u00e9cis (pi\u00e9ton, camion, fl\u00e8che, chiffre) indique l\u2019objet de la r\u00e8gle.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Exemple pratique : un panneau rond \u00e0 fond blanc, liser\u00e9 rouge avec \u00ab 50 \u00bb signifie <strong>limitation de vitesse<\/strong> \u00e0 partir du panneau jusqu\u2019\u00e0 une intersection ou un panneau de fin.<\/p>\n\n<h2 class=\"wp-block-heading\">Panneaux de danger et de priorit\u00e9 : rep\u00e9rage, distances et r\u00e9actions<\/h2>\n\n<p class=\"wp-block-paragraph\">Les <strong>panneaux de danger<\/strong> sont triangulaires et avertissent d\u2019un risque \u00e0 venir. Leur implantation respecte des distances pour laisser le temps de r\u00e9action : <strong>150 m\u00e8tres<\/strong> hors agglom\u00e9ration et <strong>50 m\u00e8tres<\/strong> en agglom\u00e9ration. Exception : le panneau indiquant la circulation \u00e0 double sens est plac\u00e9 au niveau du changement de chauss\u00e9e.<\/p>\n\n<ul class=\"wp-block-list\"><li>Ralentissez et v\u00e9rifiez la situation ; adaptez la vitesse au profil de la route.<\/li><li>Si un panonceau indique une distance, consid\u00e9rez que le danger commence \u00e0 la distance annonc\u00e9e.<\/li><li>Priorit\u00e9 : le panneau \u00ab c\u00e9dez le passage \u00bb (triangle pointe vers le bas) et le panneau \u00ab stop \u00bb imposent un comportement pr\u00e9cis : marquer l\u2019arr\u00eat et c\u00e9der la priorit\u00e9 si n\u00e9cessaire.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Insight : rep\u00e9rez d\u2019abord la forme, puis confirmiez par la couleur et le pictogramme pour d\u00e9cider d\u2019une action imm\u00e9diate.<\/p>\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Type de panneau<\/th>\n<th>Forme \/ couleur<\/th>\n<th>Action attendue<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Panneaux de danger<\/strong><\/td>\n<td>Triangle, fond blanc\/jaune<\/td>\n<td>Ralentir, \u00eatre pr\u00eat \u00e0 man\u0153uvrer<\/td>\n<\/tr>\n<tr>\n<td><strong>Panneaux d&rsquo;interdiction<\/strong><\/td>\n<td>Ronds, fond blanc, liser\u00e9 rouge<\/td>\n<td>Ne pas entrer \/ respecter l\u2019interdiction<\/td>\n<\/tr>\n<tr>\n<td><strong>Panneaux d&rsquo;obligation<\/strong><\/td>\n<td>Ronds, fond bleu<\/td>\n<td>Appliquer l\u2019ordre (sens, vitesse minimale, allumage feux)<\/td>\n<\/tr>\n<tr>\n<td><strong>Panneaux d&rsquo;indication<\/strong><\/td>\n<td>Carr\u00e9s\/rectangles, fond bleu ou blanc<\/td>\n<td>Informations sur la route\/points de service<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n<h2 class=\"wp-block-heading\">Panneaux d&rsquo;interdiction et panneaux d&rsquo;obligation : d\u00e9but, fin et zones<\/h2>\n\n<p class=\"wp-block-paragraph\">Un <strong>panneau d&rsquo;interdiction<\/strong> commence \u00e0 hauteur du panneau ; il prend fin \u00e0 la prochaine intersection ou \u00e0 la rencontre d\u2019un panneau de fin d\u2019interdiction. Certains panneaux d\u00e9finissent une zone (ex. zone 30) avec panneaux d\u2019entr\u00e9e et de sortie.<\/p>\n\n<ul class=\"wp-block-list\"><li>Un rond \u00e0 fond blanc barr\u00e9 met fin \u00e0 une interdiction pr\u00e9cise.<\/li><li>Un rond \u00e0 fond bleu barr\u00e9 met fin \u00e0 une obligation.<\/li><li>Les interdictions peuvent viser cat\u00e9gories de v\u00e9hicules : poids-lourds, transports dangereux, etc.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Pour approfondir les images et la typologie, consultez des ressources p\u00e9dagogiques reconnues comme <a href=\"https:\/\/www.ornikar.com\/code\/cours\/panneaux\" rel=\"nofollow noopener\" target=\"_blank\">les cours d\u00e9taill\u00e9s d&rsquo;Ornikar<\/a> et la compilation <a href=\"https:\/\/www.auto-ecoleducentre.com\/userfiles\/files\/TOUS%20LES%20PANNEAUX.pdf\" rel=\"nofollow noopener\" target=\"_blank\">des panneaux en PDF<\/a>. Ces supports aident \u00e0 visualiser chaque pictogramme et sa port\u00e9e.<\/p>\n\n<h3 class=\"wp-block-heading\">Panonceaux : pr\u00e9cision et port\u00e9e des messages<\/h3>\n\n<p class=\"wp-block-paragraph\">Les panonceaux (petits panneaux sous un panneau principal) pr\u00e9cisent souvent la distance, l\u2019\u00e9tendue ou la cat\u00e9gorie de v\u00e9hicules concern\u00e9e. Exemples concrets :<\/p>\n\n<ul class=\"wp-block-list\"><li><strong>Panonceau de distance<\/strong> : \u00ab 50 m \u00bb signifie que l\u2019effet commence dans 50 m\u00e8tres.<\/li><li><strong>Panonceau d&rsquo;\u00e9tendue<\/strong> : \u00ab 200 m \u00bb indique la longueur de l\u2019effet.<\/li><li><strong>Panonceau de cat\u00e9gorie<\/strong> : limite valable uniquement pour les v\u00e9hicules &gt; 5,5 tonnes.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Phrase-cl\u00e9 : toujours lire le panonceau pour ne pas appliquer une r\u00e8gle hors de sa port\u00e9e.<\/p>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@tailwindcss\/browser@4\"><\/script>\n\n<!--\n  QUIZ INTERACTIF (HTML + JS)\n  - Bloc pr\u00eat \u00e0 copier\/coller dans une page existante (pas de <head> ni <body> complets).\n  - Hauteur maximale de l'outil : 2000px (contr\u00f4l\u00e9 par style).\n  - Toutes les cha\u00eenes en fran\u00e7ais et facilement \u00e9ditables.\n  - Accessible : utilisation de fieldset\/legend, boutons clavier, r\u00e9gion ARIA pour retours.\n  - Pas d'API externe utilis\u00e9e (toutes les donn\u00e9es sont embarqu\u00e9es ci-dessous).\n-->\n\n<style>\n  \/* Styles suppl\u00e9mentaires pour am\u00e9liorer l'apparence et l'accessibilit\u00e9 *\/\n  .quiz-container { max-height: 1200px; } \/* <= 2000px demand\u00e9 *\/\n  .choice-btn:focus { outline: 3px solid rgba(59,130,246,0.6); outline-offset: 2px; }\n  .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }\n  [data-state=\"correct\"] { background-color: #ecfdf5; border-color: #10b981; }\n  [data-state=\"wrong\"] { background-color: #fff1f2; border-color: #ef4444; }\n<\/style>\n\n<div id=\"quiz-root\" class=\"quiz-container max-w-3xl mx-auto p-4 bg-white shadow-md rounded-lg border border-gray-200\" style=\"font-family: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;\">\n  <header class=\"mb-4\">\n    <h2 class=\"text-2xl font-semibold text-gray-800\">Quiz : Signification des panneaux du code de la route<\/h2>\n    <p class=\"text-sm text-gray-600 mt-1\">Testez vos connaissances. Questions interactives, retours imm\u00e9diats et score final.<\/p>\n  <\/header>\n\n  <main id=\"quiz-app\" aria-live=\"polite\">\n    <!-- Le contenu dynamique sera inject\u00e9 ici par le script -->\n  <\/main>\n\n  <footer class=\"mt-4 flex items-center justify-between\">\n    <div id=\"progress\" class=\"text-sm text-gray-600\">Question 0 \/ 0<\/div>\n    <div class=\"space-x-2\">\n      <button id=\"prev-btn\" class=\"px-3 py-1 rounded border text-gray-700 hover:bg-gray-50\" aria-label=\"Question pr\u00e9c\u00e9dente\">Pr\u00e9c\u00e9dent<\/button>\n      <button id=\"next-btn\" class=\"px-3 py-1 rounded bg-blue-600 text-white hover:bg-blue-700\" aria-label=\"Question suivante\">Suivant<\/button>\n      <button id=\"reset-btn\" class=\"px-3 py-1 rounded border text-gray-700 hover:bg-gray-50\" aria-label=\"Recommencer\">Recommencer<\/button>\n    <\/div>\n  <\/footer>\n\n  <!-- R\u00e9gion pour annoncer feedback (accessibilit\u00e9) -->\n  <div id=\"a11y-live\" class=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\"><\/div>\n<\/div>\n\n<script>\n\/*\n  Script du Quiz\n  - Toutes les cha\u00eenes sont en fran\u00e7ais et modifiables dans l'objet i18n.\n  - Les questions proviennent des donn\u00e9es fournies dans le fragment after_fragment.\n  - Pas d'API externe. Aucune cl\u00e9 n\u00e9cessaire.\n  - JS lisible et comment\u00e9 pour faciliter la maintenance.\n*\/\n\n\/* ---------- Donn\u00e9es du quiz (modifiables) ----------\n   Source : fragment fourni par l'utilisateur\n   Format : tableau d'objets { question, correctAnswer }\n*\/\nconst rawData = [\n  { question: \"Quelle forme indique un danger?\", correctAnswer: \"Triangle\" },\n  { question: \"Que signifie un panneau rond \u00e0 fond bleu?\", correctAnswer: \"Obligation\" },\n  { question: \"\u00c0 quelle distance est plac\u00e9 un panneau de danger hors agglom\u00e9ration?\", correctAnswer: \"150 m\u00e8tres\" }\n];\n\n\/* ---------- Textes (pour internationalisation\/localisation) ----------\n   Modifier ici pour changer les textes affich\u00e9s dans toute l'UI.\n*\/\nconst i18n = {\n  submit: \"Valider\",\n  next: \"Suivant\",\n  prev: \"Pr\u00e9c\u00e9dent\",\n  retry: \"Recommencer\",\n  scoreText: (score, total) => `Votre score : ${score} \/ ${total}`,\n  correct: \"Bonne r\u00e9ponse\",\n  wrong: \"Mauvaise r\u00e9ponse\",\n  showAnswer: \"R\u00e9ponse :\",\n  explanationHeader: \"Explication\",\n  ariaAnswered: (n, total) => `Question ${n} sur ${total} r\u00e9pondue.`,\n  completed: \"Quiz termin\u00e9\"\n};\n\n\/* ---------- G\u00e9n\u00e9ration de choix (distracteurs) ----------\n   Pour chaque question \u00e0 choix multiple, on g\u00e9n\u00e8re quelques r\u00e9ponses incorrectes plausibles.\n   Ces distracteurs peuvent \u00eatre adapt\u00e9s si vous avez une base de donn\u00e9es plus riche.\n*\/\nfunction generateChoices(correct) {\n  const normalized = (s) => s.trim().toLowerCase();\n  const c = normalized(correct);\n\n  \/\/ Dictionnaire simple de distracteurs par mot-cl\u00e9\n  const pool = [\n    \"Triangle\", \"Cercle\", \"Carr\u00e9\", \"Losange\", \"Rectangle\",\n    \"Obligation\", \"Interdiction\", \"Information\", \"Danger\",\n    \"50 m\u00e8tres\", \"100 m\u00e8tres\", \"150 m\u00e8tres\", \"200 m\u00e8tres\", \"250 m\u00e8tres\"\n  ];\n\n  \/\/ Si la bonne r\u00e9ponse est une distance (contient un chiffre), choisir plusieurs distances\n  if (\/d\/.test(correct)) {\n    const distances = [\"50 m\u00e8tres\", \"100 m\u00e8tres\", \"150 m\u00e8tres\", \"200 m\u00e8tres\", \"250 m\u00e8tres\"];\n    const choices = new Set([correct]);\n    while (choices.size < 4) {\n      const pick = distances[Math.floor(Math.random() * distances.length)];\n      choices.add(pick);\n    }\n    return shuffle(Array.from(choices));\n  }\n\n  \/\/ Sinon, prendre items du pool qui ne sont pas la bonne r\u00e9ponse\n  const candidates = pool.filter(item => normalized(item) !== c);\n  const choices = new Set([correct]);\n  while (choices.size < 4 &#038;&#038; candidates.length > 0) {\n    const idx = Math.floor(Math.random() * candidates.length);\n    choices.add(candidates.splice(idx, 1)[0]);\n  }\n  return shuffle(Array.from(choices));\n}\n\n\/* M\u00e9lange un tableau (Fisher-Yates) *\/\nfunction shuffle(arr) {\n  const a = arr.slice();\n  for (let i = a.length - 1; i > 0; i--) {\n    const j = Math.floor(Math.random() * (i + 1));\n    [a[i], a[j]] = [a[j], a[i]];\n  }\n  return a;\n}\n\n\/* Pr\u00e9parer les questions avec choix *\/\nconst questions = rawData.map(item => {\n  return {\n    question: item.question,\n    correctAnswer: item.correctAnswer,\n    choices: generateChoices(item.correctAnswer),\n    userAnswer: null,\n    \/\/ Explication courte (modifiable) \u2014 par d\u00e9faut on donne une explication g\u00e9n\u00e9rique\n    explanation: defaultExplanationFor(item.question, item.correctAnswer)\n  };\n});\n\n\/* Fournit une explication g\u00e9n\u00e9rique ; personnalisez si n\u00e9cessaire *\/\nfunction defaultExplanationFor(question, answer) {\n  \/\/ Exemples simples; d\u00e9veloppez selon vos besoins.\n  if (answer.toLowerCase().includes(\"triangle\")) {\n    return \"Les panneaux triangulaires indiquent en g\u00e9n\u00e9ral un danger imminent ou potentiel.\";\n  }\n  if (answer.toLowerCase().includes(\"obligation\")) {\n    return \"Un panneau rond \u00e0 fond bleu indique une obligation (ex : direction impos\u00e9e, port de l'\u00e9quipement).\";\n  }\n  if (answer.match(\/d\/)) {\n    return \"Les panneaux de danger sont g\u00e9n\u00e9ralement plac\u00e9s \u00e0 une distance r\u00e9glementaire (souvent 150 m hors agglom\u00e9ration) pour laisser le temps de r\u00e9agir.\";\n  }\n  return \"Explication : se rapporte \u00e0 la signalisation routi\u00e8re et aux conventions graphiques.\";\n}\n\n\/* ---------- \u00c9tat du quiz ---------- *\/\nlet currentIndex = 0;\n\n\/* ---------- R\u00e9f\u00e9rences DOM ---------- *\/\nconst appEl = document.getElementById(\"quiz-app\");\nconst progressEl = document.getElementById(\"progress\");\nconst prevBtn = document.getElementById(\"prev-btn\");\nconst nextBtn = document.getElementById(\"next-btn\");\nconst resetBtn = document.getElementById(\"reset-btn\");\nconst a11yLive = document.getElementById(\"a11y-live\");\n\n\/* Initialisation *\/\nrenderQuestion();\nupdateControls();\n\n\/* ---------- Rendus \/ UI ---------- *\/\n\n\/* Rendu de la question courante *\/\nfunction renderQuestion() {\n  const q = questions[currentIndex];\n  const total = questions.length;\n\n  \/\/ Mettre \u00e0 jour progression\n  progressEl.textContent = `Question ${currentIndex + 1} \/ ${total}`;\n\n  \/\/ Construire le HTML accessible pour la question\n  appEl.innerHTML = `\n    <section aria-labelledby=\"q-title-${currentIndex}\">\n      <h3 id=\"q-title-${currentIndex}\" class=\"text-lg font-medium text-gray-800 mb-2\">${escapeHtml(q.question)}<\/h3>\n\n      <form id=\"question-form\" class=\"space-y-3\" aria-describedby=\"explain-${currentIndex}\">\n        <fieldset class=\"space-y-2 border border-gray-100 p-3 rounded\" role=\"radiogroup\" aria-labelledby=\"q-title-${currentIndex}\">\n          <legend class=\"sr-only\">Choix<\/legend>\n          ${q.choices.map((choice, idx) => {\n            \/\/ D\u00e9termine l'\u00e9tat visuel si r\u00e9pondu\n            const isSelected = q.userAnswer !== null && q.userAnswer === choice;\n            let dataState = \"\";\n            if (q.userAnswer !== null) {\n              dataState = (choice === q.correctAnswer) ? \"data-state-correct\" : (isSelected ? \"data-state-wrong\" : \"\");\n            }\n            return `\n              <label class=\"block\">\n                <input type=\"radio\" name=\"choice\" value=\"${escapeHtml(choice)}\" ${isSelected ? \"checked\" : \"\"} class=\"choice-radio hidden\" \/>\n                <button type=\"button\" class=\"choice-btn w-full text-left px-3 py-2 border rounded flex items-center justify-between ${isSelected ? 'ring-2 ring-blue-200' : 'border-gray-200'}\" data-choice-index=\"${idx}\">\n                  <span>${escapeHtml(choice)}<\/span>\n                  <span class=\"text-sm text-gray-500\">${isSelected ? '\u2713' : ''}<\/span>\n                <\/button>\n              <\/label>\n            `;\n          }).join(\"\")}\n        <\/fieldset>\n\n        <div class=\"flex items-center gap-2\">\n          <button id=\"submit-btn\" type=\"button\" class=\"px-3 py-1 rounded bg-green-600 text-white hover:bg-green-700\">${i18n.submit}<\/button>\n          <button id=\"hint-btn\" type=\"button\" class=\"px-3 py-1 rounded border text-gray-700 hover:bg-gray-50\">Indice<\/button>\n          <div id=\"feedback\" class=\"ml-3 text-sm font-medium\"><\/div>\n        <\/div>\n\n        <div id=\"explain-${currentIndex}\" class=\"mt-3 text-sm text-gray-700\" hidden>\n          <strong>${i18n.explanationHeader} :<\/strong> <span id=\"explain-text\">${escapeHtml(q.explanation)}<\/span>\n        <\/div>\n      <\/form>\n    <\/section>\n  `;\n\n  \/\/ Ajouter gestion des interactions sur choix (boutons) et formulaire\n  const choiceButtons = appEl.querySelectorAll(\".choice-btn\");\n  choiceButtons.forEach(btn => {\n    btn.addEventListener(\"click\", (e) => {\n      \/\/ Simuler la s\u00e9lection du radio correspondant\n      const form = appEl.querySelector(\"#question-form\");\n      const radios = form.querySelectorAll(\".choice-radio\");\n      const index = Number(btn.getAttribute(\"data-choice-index\"));\n      radios[index].checked = true;\n      \/\/ Visuel simple : marquer s\u00e9lection\n      choiceButtons.forEach(b => b.classList.remove(\"ring-2\", \"ring-blue-200\"));\n      btn.classList.add(\"ring-2\", \"ring-blue-200\");\n      \/\/ Nettoyer feedback pr\u00e9c\u00e9dent\n      const feedbackEl = appEl.querySelector(\"#feedback\");\n      feedbackEl.textContent = \"\";\n    });\n    \/\/ Permettre activation par Entr\u00e9e quand le bouton a le focus\n    btn.addEventListener(\"keyup\", (ev) => {\n      if (ev.key === \"Enter\") btn.click();\n    });\n  });\n\n  \/\/ Soumission\n  appEl.querySelector(\"#submit-btn\").addEventListener(\"click\", () => {\n    handleSubmit();\n  });\n\n  \/\/ Indice : affiche la bonne r\u00e9ponse (utile pour apprentissage)\n  appEl.querySelector(\"#hint-btn\").addEventListener(\"click\", () => {\n    const explainEl = appEl.querySelector(`#explain-${currentIndex}`);\n    explainEl.hidden = false;\n    a11yLive.textContent = `${i18n.showAnswer} ${questions[currentIndex].correctAnswer}`;\n  });\n\n  \/\/ Mettre \u00e0 jour ARIA\n  a11yLive.textContent = `Question ${currentIndex + 1} sur ${total}.`;\n}\n\n\/* Gestion de la validation d'une r\u00e9ponse *\/\nfunction handleSubmit() {\n  const form = appEl.querySelector(\"#question-form\");\n  const checked = form.querySelector(\".choice-radio:checked\");\n  const feedbackEl = appEl.querySelector(\"#feedback\");\n  if (!checked) {\n    \/\/ Accessibilit\u00e9 : annoncez qu'il faut choisir une r\u00e9ponse\n    feedbackEl.textContent = \"Veuillez choisir une r\u00e9ponse avant de valider.\";\n    feedbackEl.className = \"ml-3 text-sm font-medium text-yellow-700\";\n    a11yLive.textContent = \"Aucune r\u00e9ponse s\u00e9lectionn\u00e9e.\";\n    return;\n  }\n\n  const answer = checked.value;\n  const q = questions[currentIndex];\n  q.userAnswer = answer;\n\n  const isCorrect = answer.trim().toLowerCase() === q.correctAnswer.trim().toLowerCase();\n\n  \/\/ Afficher feedback visuel et texte\n  if (isCorrect) {\n    feedbackEl.textContent = `${i18n.correct} `;\n    feedbackEl.className = \"ml-3 text-sm font-semibold text-green-700\";\n    a11yLive.textContent = i18n.correct + \". \" + i18n.ariaAnswered(currentIndex + 1, questions.length);\n  } else {\n    feedbackEl.textContent = `${i18n.wrong}  \u2014 ${i18n.showAnswer} ${q.correctAnswer}`;\n    feedbackEl.className = \"ml-3 text-sm font-semibold text-red-700\";\n    a11yLive.textContent = i18n.wrong + \". \" + i18n.ariaAnswered(currentIndex + 1, questions.length);\n  }\n\n  \/\/ Montrer explication\n  const explainEl = appEl.querySelector(`#explain-${currentIndex}`);\n  explainEl.hidden = false;\n\n  \/\/ Mettre \u00e0 jour visuel des choix : ajouter attributs data-state\n  const radioEls = form.querySelectorAll(\".choice-radio\");\n  radioEls.forEach(r => {\n    const labelBtn = r.parentElement.querySelector(\".choice-btn\");\n    labelBtn.removeAttribute(\"data-state\");\n    if (r.value === q.correctAnswer) {\n      labelBtn.setAttribute(\"data-state\", \"correct\");\n      labelBtn.classList.add(\"border-green-500\");\n    } else if (r.checked && !isCorrect) {\n      labelBtn.setAttribute(\"data-state\", \"wrong\");\n      labelBtn.classList.add(\"border-red-400\");\n    } else {\n      labelBtn.classList.remove(\"border-green-500\", \"border-red-400\");\n    }\n  });\n\n  \/\/ Si toutes les questions sont r\u00e9ponses, afficher score sur le footer\n  if (questions.every(qq => qq.userAnswer !== null)) {\n    const score = questions.reduce((acc, qq) => acc + (qq.userAnswer.trim().toLowerCase() === qq.correctAnswer.trim().toLowerCase() ? 1 : 0), 0);\n    \/\/ Mettre \u00e0 jour zone de progression pour afficher score\n    progressEl.textContent = i18n.scoreText(score, questions.length);\n    a11yLive.textContent = `${i18n.completed}. ${i18n.scoreText(score, questions.length)}.`;\n    nextBtn.disabled = true;\n    prevBtn.disabled = false;\n  }\n  updateControls();\n}\n\n\/* Mettre \u00e0 jour l'\u00e9tat des boutons Prev\/Next\/Reset *\/\nfunction updateControls() {\n  prevBtn.disabled = currentIndex === 0;\n  nextBtn.disabled = currentIndex === questions.length - 1;\n}\n\n\/* Navigation *\/\nprevBtn.addEventListener(\"click\", () => {\n  if (currentIndex > 0) {\n    currentIndex--;\n    renderQuestion();\n    updateControls();\n  }\n});\nnextBtn.addEventListener(\"click\", () => {\n  if (currentIndex < questions.length - 1) {\n    currentIndex++;\n    renderQuestion();\n    updateControls();\n  }\n});\nresetBtn.addEventListener(\"click\", () => {\n  \/\/ R\u00e9initialise toutes les r\u00e9ponses\n  questions.forEach(q => q.userAnswer = null);\n  currentIndex = 0;\n  renderQuestion();\n  updateControls();\n  progressEl.textContent = `Question 1 \/ ${questions.length}`;\n  a11yLive.textContent = \"Quiz recommenc\u00e9.\";\n  nextBtn.disabled = false;\n});\n\n\/* Aide utilitaire : \u00e9chappe le HTML pour \u00e9viter injection si vous int\u00e9grez contenu externe *\/\nfunction escapeHtml(str) {\n  if (typeof str !== \"string\") return str;\n  return str\n    .replaceAll(\"&\", \"&amp;\")\n    .replaceAll(\"<\", \"&lt;\")\n    .replaceAll(\">\", \"&gt;\")\n    .replaceAll('\"', \"&quot;\")\n    .replaceAll(\"'\", \"&#039;\");\n}\n\n\/* ----------------- Fin du script ----------------- *\/\n\n\/*\n  Remarques \/ Extensions possibles :\n  - Ajouter minuterie par question.\n  - Charger questions depuis un fichier JSON externe (ex: \/questions.json) \u2014 gratuit si h\u00e9berg\u00e9 sur le m\u00eame domaine.\n  - Stocker progression dans localStorage pour continuer plus tard.\n*\/\n<\/script>\n\n<h2 class=\"wp-block-heading\">Panneaux de direction et de localisation : couleur, forme et lisibilit\u00e9<\/h2>\n\n<p class=\"wp-block-paragraph\">Les panneaux de direction peuvent \u00eatre de diff\u00e9rentes couleurs pour indiquer le type de route : vert\/bleu pour autoroutes, bleu pour routes nationales, blanc pour indications locales. Leur placement est souvent \u00e0 l\u2019intersection pour les fl\u00e8ches et au bord de la chauss\u00e9e pour les distances.<\/p>\n\n<p class=\"wp-block-paragraph\">La taille varie : hors agglom\u00e9ration ils sont g\u00e9n\u00e9ralement deux fois plus grands pour garantir une lecture \u00e0 ~150 m versus ~50 m en ville. Gardez cela en t\u00eate lors des d\u00e9passements ou changements de voie.<\/p>\n\n<p class=\"wp-block-paragraph\">Pour une r\u00e9vision visuelle compl\u00e8te, la <a href=\"https:\/\/www.securite-routiere.gouv.fr\/sites\/default\/files\/2020-02\/apr_panneaux2017.pdf\" rel=\"nofollow noopener\" target=\"_blank\">publication officielle sur la signalisation routi\u00e8re<\/a> rassemble les d\u00e9finitions et les cas d\u2019usage.<\/p>\n\n<h2 class=\"wp-block-heading\">Erreurs fr\u00e9quentes \u00e0 \u00e9viter et conseils terrain<\/h2>\n\n<ul class=\"wp-block-list\"><li><strong>Erreur :<\/strong> lire seulement le pictogramme sans tenir compte du panonceau. Cons\u00e9quence : mauvaise application de la r\u00e8gle.<\/li><li><strong>Erreur :<\/strong> confondre panneau d\u2019obligation et d\u2019information (bleu vs blanc). Cons\u00e9quence : manquement \u00e0 une obligation.<\/li><li><strong>Conseil :<\/strong> en zone touristique (Antilles, \u00eeles) adaptez votre vitesse aux panneaux temporaires jaunes, souvent li\u00e9s \u00e0 des travaux ou d\u00e9viations locales.<\/li><li><strong>Conseil :<\/strong> si vous pr\u00e9parez l\u2019examen, travaillez par familles (danger, interdiction, obligation, direction) plut\u00f4t qu\u2019apprendre panneau par panneau.<\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Phrase-cl\u00e9 : int\u00e9grer la lecture en trois \u00e9tapes (forme, couleur, pictogramme) pour \u00e9liminer les h\u00e9sitations sur la route.<\/p>\n\n<p class=\"wp-block-paragraph\">Pour les trajets en zone insulaire ou pour pr\u00e9parer un road-trip aux Antilles, quelques ressources pratiques locales peuvent s\u2019av\u00e9rer utiles, par exemple des guides pratiques sur les aires de covoiturage et sur les particularit\u00e9s r\u00e9glementaires locales : <a href=\"https:\/\/www.transports-express-caraibes.fr\/blog\/importance-aires-covoiturage-2025\/\" rel=\"nofollow\">article sur l'importance des aires de covoiturage<\/a> et des retours d\u2019exp\u00e9rience sur les itin\u00e9raires touristiques <a href=\"https:\/\/www.transports-express-caraibes.fr\/blog\/secrets-cascade-vis-2025\/\" rel=\"nofollow\">parcourant des cascades et sentiers<\/a>.<\/p>\n\n<h2 class=\"wp-block-heading\">Programme pratique pour m\u00e9moriser les panneaux en 4 semaines<\/h2>\n\n<ol class=\"wp-block-list\"><li>Semaine 1 : apprendre formes et couleurs, 30 minutes par jour.<\/li><li>Semaine 2 : se concentrer sur les panneaux de danger et d\u2019interdiction avec 50 cartes flash et tests chronom\u00e9tr\u00e9s.<\/li><li>Semaine 3 : travailler les panneaux d\u2019obligation et de direction, sorties pratiques en voiture 2x par semaine 45 minutes.<\/li><li>Semaine 4 : simulations d\u2019examen, r\u00e9vision des panonceaux et lecture sur route r\u00e9elle.<\/li><\/ol>\n\n<p class=\"wp-block-paragraph\">Astuce : chronom\u00e9trez vos r\u00e9ponses aux pictogrammes pour gagner en rapidit\u00e9 d\u00e9cisionnelle sur la route.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Synth\u00e8se rapide :<\/strong> ma\u00eetriser les <strong>panneaux de signalisation<\/strong> se fait par m\u00e9thode : identifiez la forme, validez la couleur, lisez le pictogramme et le panonceau. Adaptez votre comportement \u00e0 la cat\u00e9gorie du panneau pour garantir la <strong>s\u00e9curit\u00e9 routi\u00e8re<\/strong>. Commencez par 15\u201330 minutes par jour et testez-vous en conditions r\u00e9elles.<\/p>\n\n<script type=\"application\/ld+json\">\n{\"@context\":\"https:\/\/schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"Quelle diffu00e9rence entre panneau permanent et panneau temporaire?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Un panneau u00e0 fond blanc est permanent ; un panneau u00e0 fond jaune signale une mesure temporaire (travaux, du00e9viation). La signalisation temporaire pru00e9vaut sur la permanente.\"}},{\"@type\":\"Question\",\"name\":\"Ou00f9 commence et ou00f9 finit une interdiction indiquu00e9e par un panneau?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Lu2019interdiction commence u00e0 la hauteur du panneau et cesse u00e0 la prochaine intersection ou au panneau de fin du2019interdiction. Pour une zone, elle est du00e9limitu00e9e par un panneau du2019entru00e9e et un panneau de sortie.\"}},{\"@type\":\"Question\",\"name\":\"Comment lire un panonceau indiquant une distance?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Un panonceau portant u00ab 50 m u00bb signifie que la ru00e8gle du panneau principal prendra effet dans 50 mu00e8tres. Lisez systu00e9matiquement le panonceau pour connau00eetre le point du2019application.\"}},{\"@type\":\"Question\",\"name\":\"Les panneaux hors agglomu00e9ration sont-ils plus grands?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Oui : hors agglomu00e9ration, les panneaux sont gu00e9nu00e9ralement deux fois plus grands, permettant une lecture u00e0 environ 150 m, contre ~50 m en agglomu00e9ration.\"}}]}\n<\/script>\n<h3>Quelle diff\u00e9rence entre panneau permanent et panneau temporaire?<\/h3>\n<p>Un panneau \u00e0 fond blanc est permanent ; un panneau \u00e0 fond jaune signale une mesure temporaire (travaux, d\u00e9viation). La signalisation temporaire pr\u00e9vaut sur la permanente.<\/p>\n<h3>O\u00f9 commence et o\u00f9 finit une interdiction indiqu\u00e9e par un panneau?<\/h3>\n<p>L\u2019interdiction commence \u00e0 la hauteur du panneau et cesse \u00e0 la prochaine intersection ou au panneau de fin d\u2019interdiction. Pour une zone, elle est d\u00e9limit\u00e9e par un panneau d\u2019entr\u00e9e et un panneau de sortie.<\/p>\n<h3>Comment lire un panonceau indiquant une distance?<\/h3>\n<p>Un panonceau portant \u00ab 50 m \u00bb signifie que la r\u00e8gle du panneau principal prendra effet dans 50 m\u00e8tres. Lisez syst\u00e9matiquement le panonceau pour conna\u00eetre le point d\u2019application.<\/p>\n<h3>Les panneaux hors agglom\u00e9ration sont-ils plus grands?<\/h3>\n<p>Oui : hors agglom\u00e9ration, les panneaux sont g\u00e9n\u00e9ralement deux fois plus grands, permettant une lecture \u00e0 environ 150 m, contre ~50 m en agglom\u00e9ration.<\/p>\n\n<!-- Quiz interactif (bloc pr\u00eat \u00e0 coller). Conteneur limit\u00e9 en hauteur pour respecter la contrainte (max 2000px). -->\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@tailwindcss\/browser@4\"><\/script>\n\n<div id=\"quiz-app\" class=\"max-h-[2000px] overflow-auto p-6 bg-slate-50 text-slate-900 rounded-lg shadow-md ring-1 ring-slate-200\" aria-live=\"polite\">\n  <header class=\"mb-4\">\n    <h2 id=\"quiz-title\" class=\"text-2xl font-extrabold text-slate-800\">Quizz : Signification des panneaux du code de la route<\/h2>\n    <p id=\"quiz-desc\" class=\"mt-1 text-sm text-slate-600\">Testez vos connaissances et r\u00e9visez rapidement les panneaux et leurs significations. Toutes les phrases sont en fran\u00e7ais et facilement modifiables.<\/p>\n  <\/header>\n\n  <main class=\"space-y-6\" id=\"quiz-main\">\n    <!-- La UI du quiz est inject\u00e9e par le script ci-dessous -->\n  <\/main>\n\n  <footer class=\"mt-4 flex items-center justify-between\">\n    <div class=\"text-sm text-slate-600\" id=\"status-area\" aria-live=\"polite\"><\/div>\n    <div class=\"space-x-2\">\n      <button id=\"prev-btn\" class=\"px-3 py-1.5 bg-white border border-slate-300 text-slate-700 rounded hover:bg-slate-50 disabled:opacity-50\" aria-label=\"Question pr\u00e9c\u00e9dente\">Pr\u00e9c\u00e9dent<\/button>\n      <button id=\"next-btn\" class=\"px-3 py-1.5 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50\" aria-label=\"Question suivante\">Suivant<\/button>\n      <button id=\"restart-btn\" class=\"px-3 py-1.5 bg-white border border-slate-300 text-slate-700 rounded hover:bg-slate-50\" aria-label=\"Recommencer le quiz\">Recommencer<\/button>\n    <\/div>\n  <\/footer>\n<\/div>\n\n<!--\nOptionnel (exemple d'API publique gratuite) :\nOpen Trivia DB (API gratuite, pas de cl\u00e9) :\nURL : https:\/\/opentdb.com\/api.php?amount=3\nExemple de r\u00e9ponse JSON :\n{\n  \"response_code\":0,\n  \"results\":[\n    {\n      \"category\":\"General Knowledge\",\n      \"type\":\"multiple\",\n      \"difficulty\":\"easy\",\n      \"question\":\"What is the capital of France?\",\n      \"correct_answer\":\"Paris\",\n      \"incorrect_answers\":[\"London\",\"Berlin\",\"Madrid\"]\n    },\n    ...\n  ]\n}\nNote : dans ce composant nous n'utilisons pas cette API mais l'exemple montre comment r\u00e9cup\u00e9rer des questions externes.\n-->\n\n<script>\n\/*\n  Quiz en pur JS + HTML (accessible, comment\u00e9, facilement \u00e9ditable)\n  - Toutes les cha\u00eenes en fran\u00e7ais (faciles \u00e0 modifier dans l'objet i18n ci-dessous).\n  - Questions par d\u00e9faut fournies via la donn\u00e9e <after_fragment> (int\u00e9gr\u00e9e).\n  - Pas de d\u00e9pendances lourdes (Tailwind par CDN pour le style l\u00e9ger).\n  - Performance : DOM minimal, pas de frameworks.\n*\/\n\n\/* ---------- i18n \/ textes (modifier ici si besoin) ---------- *\/\nconst i18n = {\n  next: 'Suivant',\n  prev: 'Pr\u00e9c\u00e9dent',\n  restart: 'Recommencer',\n  submit: 'Valider',\n  yourAnswer: 'Votre r\u00e9ponse',\n  correct: 'Correct',\n  incorrect: 'Incorrect',\n  score: 'Score',\n  reviewMode: 'Mode r\u00e9vision',\n  explanationPrefix: 'Explication :',\n  ariaQuestion: 'Question',\n  answered: 'r\u00e9pondu',\n  unanswered: 'non r\u00e9pondu'\n};\n\n\/* ---------- Donn\u00e9es du quiz (modifiable) ----------\n   Les questions proviennent de votre fragment :\n   Q1: Forme d'un panneau de danger?;A1: Triangle;\n   Q2: Couleur d'une obligation?;A2: Bleu;\n   Q3: Distance d'alerte hors agglom\u00e9ration?;A3: 150 m\u00e8tres\n   Nous construisons des options pour chaque question (1 bonne + distracteurs).\n*\/\nconst questions = [\n  {\n    id: 'q1',\n    q: \"Forme d'un panneau de danger ?\",\n    choices: [\"Triangle\", \"Carr\u00e9\", \"Cercle\", \"Rectangle\"],\n    answerIndex: 0,\n    explanation: \"Les panneaux de danger ont une forme triangulaire (triangle \u00e0 bord rouge en France).\"\n  },\n  {\n    id: 'q2',\n    q: \"Couleur d'un panneau d'obligation ?\",\n    choices: [\"Bleu\", \"Rouge\", \"Jaune\", \"Vert\"],\n    answerIndex: 0,\n    explanation: \"Les panneaux d'obligation sont bleus (ex. obligation de tourner \u00e0 droite).\"\n  },\n  {\n    id: 'q3',\n    q: \"Distance d'alerte hors agglom\u00e9ration (distance typique indiqu\u00e9e par un panneau d'annonce) ?\",\n    choices: [\"50 m\u00e8tres\", \"100 m\u00e8tres\", \"150 m\u00e8tres\", \"250 m\u00e8tres\"],\n    answerIndex: 2,\n    explanation: \"La distance d'alerte hors agglom\u00e9ration mentionn\u00e9e dans l'exemple est 150 m\u00e8tres.\"\n  }\n];\n\n\/* ---------- \u00c9tat du quiz ---------- *\/\nconst state = {\n  currentIndex: 0,\n  answers: Array(questions.length).fill(null), \/\/ index choisi ou null\n  revealed: Array(questions.length).fill(false) \/\/ si r\u00e9ponse r\u00e9v\u00e9l\u00e9e avec explication\n};\n\n\/* ---------- Helpers d'accessibilit\u00e9 et utilitaires ---------- *\/\nfunction el(tag, attrs = {}, children = []) {\n  const e = document.createElement(tag);\n  for (const [k, v] of Object.entries(attrs)) {\n    if (k === 'class') e.className = v;\n    else if (k === 'html') e.innerHTML = v;\n    else if (k.startsWith('aria-')) e.setAttribute(k, v);\n    else e[k] = v;\n  }\n  (Array.isArray(children) ? children : [children]).forEach(c => {\n    if (!c) return;\n    if (typeof c === 'string') e.appendChild(document.createTextNode(c));\n    else e.appendChild(c);\n  });\n  return e;\n}\n\n\/* ---------- Rendu d'une question dans le DOM ---------- *\/\nfunction renderQuestion(index) {\n  const q = questions[index];\n  const main = document.getElementById('quiz-main');\n  main.innerHTML = ''; \/\/ remplace le contenu (une question \u00e0 la fois pour simplicit\u00e9)\n\n  \/\/ Conteneur question\n  const container = el('section', { class: 'bg-white p-4 md:p-6 rounded-lg shadow-sm border border-slate-100', 'aria-labelledby': `q-${q.id}` });\n\n  \/\/ En-t\u00eate question\n  const header = el('div', { class: 'flex items-start justify-between gap-4' }, [\n    el('div', { class: 'flex-1' }, [\n      el('h3', { id: `q-${q.id}`, class: 'text-lg font-semibold text-slate-800' }, q.q),\n      el('p', { class: 'mt-1 text-sm text-slate-500' }, `Question ${index + 1} \/ ${questions.length}`)\n    ]),\n    el('div', { class: 'text-sm text-slate-500' }, [\n      el('span', { class: 'font-medium' }, `${i18n.score}: `),\n      el('span', { id: 'score-inline' }, `${computeScore()} \/ ${questions.length}`)\n    ])\n  ]);\n\n  container.appendChild(header);\n\n  \/\/ Formulaire des choix (boutons radios stylis\u00e9s)\n  const form = el('form', { class: 'mt-4', 'aria-label': `Choix pour ${q.q}` });\n  const fieldset = el('fieldset', { class: 'space-y-3', 'aria-live': 'polite' });\n\n  q.choices.forEach((choice, ci) => {\n    const radioId = `q${index}-c${ci}`;\n    const isChecked = state.answers[index] === ci;\n    const optionLabel = el('label', { class: 'flex items-center gap-3 cursor-pointer select-none' }, [\n      el('input', { type: 'radio', name: `q-${index}`, id: radioId, checked: isChecked, class: 'sr-only', value: ci }),\n      el('span', { class: `w-5 h-5 rounded-full flex-none ring-1 ring-slate-200 grid place-content-center ${isChecked ? 'bg-blue-600 text-white' : 'bg-white text-slate-700'}`, 'aria-hidden': 'true' }, isChecked ? '\u2713' : ''),\n      el('span', { class: 'text-slate-700' }, choice)\n    ]);\n\n    \/\/ Make click anywhere toggle selection\n    optionLabel.addEventListener('click', (ev) => {\n      ev.preventDefault();\n      \/\/ Set state and update UI\n      state.answers[index] = ci;\n      state.revealed[index] = false; \/\/ hide previous explanation if any\n      \/\/ Re-render question to show selected state\n      renderQuestion(index);\n      updateStatus();\n    });\n\n    fieldset.appendChild(optionLabel);\n  });\n\n  form.appendChild(fieldset);\n\n  \/\/ Bouton valider \/ montrer r\u00e9ponse\n  const controls = el('div', { class: 'mt-4 flex flex-wrap gap-3 items-center' });\n  const submitBtn = el('button', { type: 'button', class: 'px-3 py-1.5 bg-emerald-600 text-white rounded hover:bg-emerald-700', 'aria-pressed': 'false' }, i18n.submit);\n\n  submitBtn.addEventListener('click', () => {\n    \/\/ Si aucune r\u00e9ponse s\u00e9lectionn\u00e9e => focus sur premi\u00e8re option\n    if (state.answers[index] === null) {\n      \/\/ Indiquer \u00e0 l'utilisateur qu'il n'a pas r\u00e9pondu\n      statusMessage('Veuillez s\u00e9lectionner une r\u00e9ponse avant de valider.', 'warn');\n      return;\n    }\n    \/\/ Marquer comme r\u00e9v\u00e9l\u00e9 pour afficher feedback\n    state.revealed[index] = true;\n    renderQuestion(index); \/\/ r\u00e9-affiche pour montrer le feedback\n    updateStatus();\n  });\n\n  controls.appendChild(submitBtn);\n\n  \/\/ Review mode toggle (affiche solution)\n  const reviewBtn = el('button', { type: 'button', class: 'px-3 py-1.5 bg-white border border-slate-300 text-slate-700 rounded hover:bg-slate-50' }, i18n.reviewMode);\n  reviewBtn.addEventListener('click', () => {\n    state.revealed[index] = true;\n    state.answers[index] = state.answers[index] ?? questions[index].answerIndex; \/\/ optionnel : montrer aussi la bonne r\u00e9ponse si non r\u00e9pondu\n    renderQuestion(index);\n    updateStatus();\n  });\n  controls.appendChild(reviewBtn);\n\n  container.appendChild(form);\n  container.appendChild(controls);\n\n  \/\/ Feedback area (visible si revealed)\n  if (state.revealed[index]) {\n    const userAnswerIndex = state.answers[index];\n    const isCorrect = userAnswerIndex === q.answerIndex;\n    const feedback = el('div', { class: `mt-4 p-3 rounded border ${isCorrect ? 'bg-emerald-50 border-emerald-200 text-emerald-800' : 'bg-rose-50 border-rose-200 text-rose-800'}`, role: 'status' }, [\n      el('div', { class: 'font-semibold' }, isCorrect ? `${i18n.correct} !` : `${i18n.incorrect}...`),\n      el('div', { class: 'mt-1 text-sm' }, `${i18n.explanationPrefix} ${q.explanation}`)\n    ]);\n    container.appendChild(feedback);\n  }\n\n  main.appendChild(container);\n\n  \/\/ Focus management: focus first radio or submit button\n  setTimeout(() => {\n    const selectedRadio = document.querySelector(`input[name=\"q-${index}\"]:checked`);\n    if (selectedRadio) selectedRadio.focus();\n    else {\n      const firstRadio = document.querySelector(`input[name=\"q-${index}\"]`);\n      if (firstRadio) firstRadio.focus();\n    }\n  }, 50);\n}\n\n\/* ---------- Helpers d'UI ---------- *\/\nfunction statusMessage(msg, type = 'info') {\n  const status = document.getElementById('status-area');\n  status.textContent = msg;\n  status.className = type === 'warn' ? 'text-rose-600 text-sm' : 'text-slate-600 text-sm';\n  \/\/ Clear message after 4s sauf si warn (on laisse)\n  if (type !== 'warn') {\n    setTimeout(() => { status.textContent = ''; }, 4000);\n  }\n}\n\nfunction computeScore() {\n  let s = 0;\n  for (let i = 0; i < questions.length; i++) {\n    if (state.answers[i] === questions[i].answerIndex) s++;\n  }\n  return s;\n}\n\nfunction updateNavigation() {\n  const prevBtn = document.getElementById('prev-btn');\n  const nextBtn = document.getElementById('next-btn');\n  prevBtn.disabled = state.currentIndex === 0;\n  nextBtn.disabled = state.currentIndex === questions.length - 1;\n  \/\/ Afficher score inline\n  const scoreInline = document.getElementById('score-inline');\n  if (scoreInline) scoreInline.textContent = `${computeScore()} \/ ${questions.length}`;\n  \/\/ Mettre labels accessibles\n  document.getElementById('prev-btn').textContent = i18n.prev;\n  document.getElementById('next-btn').textContent = i18n.next;\n  document.getElementById('restart-btn').textContent = i18n.restart;\n}\n\n\/* ---------- Navigation et boutons globaux ---------- *\/\ndocument.getElementById('prev-btn').addEventListener('click', () => {\n  if (state.currentIndex > 0) {\n    state.currentIndex--;\n    renderQuestion(state.currentIndex);\n    updateNavigation();\n  }\n});\ndocument.getElementById('next-btn').addEventListener('click', () => {\n  if (state.currentIndex < questions.length - 1) {\n    state.currentIndex++;\n    renderQuestion(state.currentIndex);\n    updateNavigation();\n  } else {\n    \/\/ Si derni\u00e8re question, afficher score final\n    statusMessage(`Quiz termin\u00e9 \u2014 ${i18n.score} : ${computeScore()} \/ ${questions.length}`);\n  }\n});\ndocument.getElementById('restart-btn').addEventListener('click', () => {\n  \/\/ Reset state\n  state.currentIndex = 0;\n  state.answers = Array(questions.length).fill(null);\n  state.revealed = Array(questions.length).fill(false);\n  renderQuestion(0);\n  updateNavigation();\n  statusMessage('Quiz r\u00e9initialis\u00e9. Bonne chance !');\n});\n\n\/* ---------- Initial render ---------- *\/\nrenderQuestion(state.currentIndex);\nupdateNavigation();\n\n\/* ---------- Notes pour int\u00e9gration et personnalisation ----------\n - Modifier les questions : changer le tableau `questions` ci-dessus.\n - Toutes les cha\u00eenes affich\u00e9es sont dans `i18n` pour faciliter la traduction.\n - Accessibility : labels associ\u00e9s, aria-live pour feedback, focus management.\n - Performance : rendu d'une question \u00e0 la fois pour limiter le DOM.\n - Pas d'images, conforme \u00e0 la contrainte.\n - Si vous voulez charger des questions externes (ex. Open Trivia DB), utilisez fetch() et mettez \u00e0 jour `questions`.\n   Exemple :\n   fetch('https:\/\/opentdb.com\/api.php?amount=3')\n     .then(r => r.json()).then(data => { \/* transformer data.results en notre format *\/ });\n--------------------------------------------------------------- *\/\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>Chap\u00f4 : Comprendre les panneaux du code de la route n\u2019est pas une affaire de m\u00e9moire pure : c\u2019est une m\u00e9thode. Ce guide synth\u00e9tique et pratique explique comment lire rapidement la signalisation routi\u00e8re, reconna\u00eetre les panneaux de danger, d\u2019obligation et d\u2019interdiction, et agir en s\u00e9curit\u00e9. Vous trouverez des rep\u00e8res visuels (formes, couleurs), des distances d\u2019alerte &#8230; <a title=\"Signification des panneaux du code de la route : liste compl\u00e8te et explications\" class=\"read-more\" href=\"https:\/\/www.transports-express-caraibes.fr\/blog\/panneaux-code-route\/\" aria-label=\"En savoir plus sur Signification des panneaux du code de la route : liste compl\u00e8te et explications\">Lire plus<\/a><\/p>\n","protected":false},"author":3,"featured_media":23524,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[82],"tags":[],"class_list":["post-23525","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-transport"],"views":46,"_links":{"self":[{"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/posts\/23525","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/comments?post=23525"}],"version-history":[{"count":0,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/posts\/23525\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/media\/23524"}],"wp:attachment":[{"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/media?parent=23525"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/categories?post=23525"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.transports-express-caraibes.fr\/blog\/wp-json\/wp\/v2\/tags?post=23525"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}