Compare commits

...

6 Commits

Author SHA1 Message Date
f611bdcd7e Recentre les encadres TP1/TP2/TP3 de la slide finale
Passe la marge de .final-grid de 1.5rem 0 a 1.5rem auto pour centrer la
grille des trois travaux pratiques.
2026-07-21 08:22:29 +02:00
f7c846c85c Simplifie les boutons d'action du TP Nuisibles
Slide 1 : retire le bouton "Corriger un rendu", l'ecran de contexte ne
garde que l'identification. Slide 13 : ne conserve que Telecharger le PDF,
Corriger un rendu et Recommencer, aligne sur le style BioCID (PDF plein
ambre, deux boutons contour) et retire le lien biocid-anses.fr.
2026-07-21 08:20:13 +02:00
a74df51201 Migre la navigation du TP Nuisibles dans la bottom bar
Reprend le modele de footer d'Exo_BioCID : tous les boutons d'avancement
et d'aide passent dans la barre du bas, pilotes par un slideConfig
(bouton suivant, libelle, indice par slide). Supprime les boutons
in-slide correspondants, gere la validation du nom de groupe et le
deverrouillage du bouton apres choix du cas, et applique le style
footer-btn (compteur a gauche, actions centrees).
2026-07-21 08:16:05 +02:00
42650ca351 Aligne le TP Nuisibles sur le style des autres exercices
Inverse l'accueil et l'identification (héro en slide 0, contexte et
identification du groupe en slide 1), applique le style BioCID aux deux
premieres slides et supprime le badge d'avancement en haut a droite,
redondant avec la barre de progression.
2026-07-21 08:10:09 +02:00
21a7359057 Retravaille l'exercice FDS : étiquette libre et corrigé déplacé
- Slide 5 (FT) : reformule la question sur le type de produit en « Où se
  trouve la liste des TP (type de produit) ? »
- Slide 7 : l'utilisateur peut désormais cliquer n'importe quelle zone,
  même incorrecte ; le clic est enregistré et l'exercice avance.
- Slide 7 : « Terminer l'exercice » mène directement à la synthèse ; le
  récapitulatif « l'étiquette annotée » quitte le déroulé des slides.
- Déplace ce récapitulatif (étiquette annotée + check-list réglementaire)
  vers corriger-exercice.html, en corrigé de référence du rendu FDS.
2026-07-21 08:01:12 +02:00
6bb7985512 Uniformise l'identification du groupe sur BioCID et TP Nuisibles
Applique le style de carte de référence (Exo_FDS) à ExoBIOCID et au TP
Nuisibles : titre dans la carte, champs nom/date, et sélecteur de
formateur Nathan/Gauthier en boutons remplis à l'état actif, à la place
du champ texte libre (BioCID) et des boutons pilule (TP).
2026-07-21 07:46:15 +02:00
4 changed files with 352 additions and 335 deletions

View File

@@ -406,6 +406,15 @@
box-sizing: border-box;
}
.identity-fields input:focus { outline: none; border-color: var(--amber); background: white; }
.formateur-label { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); margin: 4px 0 2px; }
.formateur-row { display: flex; gap: 8px; }
.formateur-btn {
flex: 1; font-family: 'DM Sans', sans-serif; font-size: 13.5px;
padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px;
background: var(--bg); color: var(--text); cursor: pointer; transition: all 0.2s ease;
}
.formateur-btn:hover { border-color: var(--amber); }
.formateur-btn.active { background: var(--amber); border-color: var(--amber); color: #fff; }
/* DECO */
.deco-circle { position: fixed; border-radius: 50%; pointer-events: none; z-index: 0; }
@@ -492,7 +501,12 @@
<div class="identity-fields">
<input type="text" id="idGroupe" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off" oninput="clearGroupeWarning()">
<input type="date" id="idDate">
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
<input type="hidden" id="idFormateur">
<div class="formateur-label">Formateur de la session</div>
<div class="formateur-row">
<button type="button" class="formateur-btn" data-formateur="Nathan" onclick="selectFormateur(this)">Nathan</button>
<button type="button" class="formateur-btn" data-formateur="Gauthier" onclick="selectFormateur(this)">Gauthier</button>
</div>
</div>
<div id="idWarning" style="display:none;margin-top:8px;font-size:12.5px;color:var(--coral);">⚠ Renseignez le nom du groupe pour continuer.</div>
</div>
@@ -689,6 +703,12 @@ function clearGroupeWarning() {
if (w) w.style.display = 'none';
}
function selectFormateur(btn) {
document.querySelectorAll('.formateur-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.getElementById('idFormateur').value = btn.dataset.formateur;
}
function goNext() {
const cfg = slideConfig[currentSlide];
if (!cfg || cfg.next == null) return;

View File

@@ -647,21 +647,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
.eti-pop .pop-reg { color: var(--text-muted); }
.eti-pop .pop-close { position: absolute; top: 5px; right: 8px; cursor: pointer; color: var(--text-muted); font-size: 16px; line-height: 1; border: none; background: none; padding: 2px; }
/* Check-list de synthèse (modèle FHER p.15) */
.checklist-table {
width: 100%; max-width: 780px; border-collapse: collapse;
background: #fff; border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
font-size: 12.5px; margin: 0.25rem 0 1rem;
}
.checklist-table th { background: var(--teal-light); color: var(--teal-dark); font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; padding: 10px 12px; text-align: left; border-bottom: 2px solid var(--teal-mid); }
.checklist-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--text-muted); vertical-align: top; }
.checklist-table tr:last-child td { border-bottom: none; }
.checklist-table td.cl-num { text-align: center; }
.cl-badge { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 10px; }
.checklist-table strong { color: var(--text); }
.cl-check { color: #2e7d32; font-weight: 700; }
.cl-cross { color: #c62828; font-weight: 700; }
@media (max-width: 700px) {
.lbl-cols { grid-template-columns: 1fr; }
.lbl-title { font-size: 26px; }
@@ -687,20 +672,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
justify-content: center;
}
/* ---- SLIDE 7 — récapitulatif : étiquette à gauche, tableau à droite ---- */
.recap-layout { display: flex; gap: 20px; align-items: flex-start; justify-content: center; width: 100%; max-width: 1120px; }
.recap-layout #recap-stage { flex: 1 1 0; min-width: 0; max-width: 560px; }
.recap-layout .checklist-table { flex: 1 1 0; min-width: 0; margin: 0; }
.recap-layout.animate #recap-stage { animation: recapSlideLeft 0.5s ease both; }
.recap-layout.animate .checklist-table { animation: recapSlideRight 0.5s ease both; }
@keyframes recapSlideLeft { from { opacity: 0; transform: translateX(-40px); } to { opacity: 1; transform: none; } }
@keyframes recapSlideRight { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
@media (max-width: 1000px) {
.recap-layout { flex-direction: column; align-items: center; }
.recap-layout #recap-stage { flex: none; max-width: 560px; width: 100%; }
.recap-layout .checklist-table { flex: none; width: 100%; max-width: 640px; }
}
</style>
</head>
<body>
@@ -843,7 +814,7 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
<p class="slide-intro">L'étiquette est l'interface directe entre le produit et l'utilisateur. Elle communique rapidement les <strong>dangers essentiels</strong>, les <strong>EPI</strong> et les <strong>consignes d'utilisation</strong>.</p>
<div class="info-card" style="background:var(--teal-light);color:var(--teal-dark);">
<span>🎯</span>
<div>Sur une <strong>étiquette réelle</strong> (produit professionnel DÉS'INFECT), <strong>cliquez la zone</strong> qui répond à chaque question : nom commercial, pictogrammes, mention d'avertissement, phrases H, phrases P (EPI, premiers secours, élimination), contenu net et centre antipoison. Chaque clic ouvre la <strong>justification réglementaire</strong> de la zone.</div>
<div>Sur une <strong>étiquette réelle</strong> (produit professionnel DÉS'INFECT), <strong>cliquez la zone</strong> qui répond à chaque question : nom commercial, pictogrammes, mention d'avertissement, phrases H, phrases P (EPI, premiers secours, élimination), contenu net et centre antipoison. La <strong>justification réglementaire</strong> de chaque zone est fournie sur la page de correction de l'exercice.</div>
</div>
</div>
@@ -926,18 +897,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
<div id="etiquette-result" class="result-message"></div>
</div>
<!-- ---- PANNEAU B : synthèse / check-list ---- -->
<div id="eti-recap" hidden style="width:100%;display:flex;flex-direction:column;align-items:center;gap:0.75rem;">
<div class="section-tag" style="background:var(--teal-light);color:var(--teal-dark);">Check-list d'étiquetage</div>
<div class="slide-title">Récapitulatif — l'étiquette annotée</div>
<p class="slide-intro">Les 9 zones réglementaires et leurs bonnes réponses, présentées comme la <strong>check-list officielle d'étiquetage des produits biocides</strong> (Guide FHER, p. 15) — un outil réutilisable en conditions réelles.</p>
<div class="recap-layout" id="recap-layout">
<div class="eti-stage" id="recap-stage"></div>
<table class="checklist-table" id="checklist-table"></table>
</div>
</div>
</div>
</div>
@@ -996,7 +955,7 @@ const fdsQuestions = [
const ftQuestions = [
{ id: 1, text: "Quel est le nom du produit et qui est le fabricant ?", answer: "Description" },
{ id: 2, text: "Sur quelles matières ce produit est-il compatible ?", answer: "Absent" },
{ id: 3, text: "Quel est (sont) le(s) type(s) de ce produit ?", answer: "Propriétés biocides" },
{ id: 3, text: "Où se trouve la liste des TP (type de produit) ?", answer: "Propriétés biocides" },
{ id: 4, text: "Quels sont les types de conditionnements disponibles ?", answer: "Conditionnements" },
{ id: 5, text: "Où retrouver les différents modes d'utilisation de ce produit ?", answer: "Utilisations" }
];
@@ -1063,16 +1022,13 @@ let etiOrder = []; // ordre aléatoire des questions
let etiPos = 0; // question courante
let etiAnswers = {}; // { zoneCible : zoneCliquée en 1re tentative } — pour le PDF
let etiFirstTry = {}; // { zoneCible : true si trouvée sans erreur }
let etiCurError = false; // une erreur a-t-elle eu lieu sur la question courante
let etiInit = false; // écouteur de clic posé une seule fois
let etiRecapShown = false;// panneau récapitulatif affiché (slide 7)
function buildEtiquette() {
etiOrder = shuffleArray(etiZones.map((_, i) => i));
etiPos = 0;
etiAnswers = {};
etiFirstTry = {};
etiCurError = false;
const card = document.getElementById('labelCard');
card.querySelectorAll('.hotspot').forEach(h => h.classList.remove('hs-correct', 'hs-wrong'));
@@ -1080,9 +1036,6 @@ function buildEtiquette() {
closeEtiPop();
const r = document.getElementById('etiquette-result');
r.className = 'result-message'; r.innerHTML = '';
etiRecapShown = false;
document.getElementById('eti-exercise').style.display = 'flex';
document.getElementById('eti-recap').style.display = 'none';
if (currentSlide === 7) renderBarActions();
if (!etiInit) {
@@ -1110,28 +1063,20 @@ function onEtiClick(e) {
if (!hs) return;
const clicked = hs.dataset.zone;
// Exercice terminé : plus d'interaction (les corrections sont dans la correction générale)
// Exercice terminé : plus d'interaction (la correction est sur la page de correction).
if (etiPos >= etiOrder.length) return;
marquerModifie();
const target = etiZones[etiOrder[etiPos]];
if (clicked === target.zone) {
hs.classList.remove('hs-wrong');
hs.classList.add('hs-correct');
if (etiAnswers[target.zone] === undefined) etiAnswers[target.zone] = clicked;
etiFirstTry[target.zone] = !etiCurError;
etiPos++;
etiCurError = false;
renderEtiQuestion();
if (etiPos >= etiOrder.length) finishEtiquette();
} else {
if (etiAnswers[target.zone] === undefined) etiAnswers[target.zone] = clicked; // 1re réponse (erronée) mémorisée
etiCurError = true;
etiFirstTry[target.zone] = false;
hs.classList.remove('hs-wrong'); void hs.offsetWidth; // relance l'animation
hs.classList.add('hs-wrong');
setTimeout(() => hs.classList.remove('hs-wrong'), 700);
}
// La zone cliquée — bonne ou mauvaise — est enregistrée comme réponse, puis on passe
// à la question suivante. Aucun retour de justesse n'est affiché ici : la correction
// (étiquette annotée + check-list) est présentée sur la page de correction de l'exercice.
etiAnswers[target.zone] = clicked;
etiFirstTry[target.zone] = (clicked === target.zone);
etiPos++;
renderEtiQuestion();
if (etiPos >= etiOrder.length) finishEtiquette();
}
function finishEtiquette() {
@@ -1168,70 +1113,6 @@ function closeEtiPop() {
function resetEtiquette() { buildEtiquette(); }
/* ---- Synthèse : étiquette annotée + check-list ---- */
function showEtiRecap() {
closeEtiPop();
const stage = document.getElementById('recap-stage');
stage.innerHTML = '';
// Clone de l'étiquette, toutes zones en « correct » + numéros
const clone = document.getElementById('labelCard').cloneNode(true);
clone.removeAttribute('id');
clone.classList.add('recap');
clone.querySelectorAll('.hotspot').forEach(h => {
h.classList.remove('hs-wrong');
h.classList.add('hs-correct');
const idx = etiZones.findIndex(z => z.zone === h.dataset.zone);
if (idx >= 0) {
const num = document.createElement('b');
num.className = 'hs-num';
num.textContent = idx + 1;
h.insertBefore(num, h.firstChild);
}
});
stage.appendChild(clone);
// Table check-list
const tbl = document.getElementById('checklist-table');
let rows = '<thead><tr><th style="width:34px;">N°</th><th>Élément d\'étiquetage</th><th>Base réglementaire</th><th style="width:70px;">Statut</th></tr></thead><tbody>';
etiZones.forEach((z, i) => {
const ok = etiFirstTry[z.zone];
const answered = etiAnswers[z.zone] !== undefined;
const statut = ok
? '<span class="cl-check">✓ trouvé</span>'
: (answered ? '<span class="cl-cross">✗ à revoir</span>' : '<span class="cl-cross">—</span>');
rows += `<tr>
<td class="cl-num"><span class="cl-badge">${i + 1}</span></td>
<td><strong>${z.label}</strong></td>
<td>${z.reg}</td>
<td>${statut}</td>
</tr>`;
});
rows += '</tbody>';
tbl.innerHTML = rows;
document.getElementById('eti-exercise').style.display = 'none';
document.getElementById('eti-recap').style.display = 'flex';
etiRecapShown = true;
renderBarActions();
// Animation : l'étiquette glisse à gauche, le tableau apparaît à sa droite
const rl = document.getElementById('recap-layout');
if (rl) { rl.classList.remove('animate'); void rl.offsetWidth; rl.classList.add('animate'); }
const sa = document.querySelector('#slide-7 .scroll-area');
if (sa) sa.scrollTop = 0;
}
function hideEtiRecap() {
document.getElementById('eti-recap').style.display = 'none';
document.getElementById('eti-exercise').style.display = 'flex';
etiRecapShown = false;
renderBarActions();
const sa = document.querySelector('#slide-7 .scroll-area');
if (sa) sa.scrollTop = 0;
}
/* ---- FT : exercice « relier » ---- */
const ftConnections = {}; // { qId: valeur de la section reliée }
let ftDrag = null; // { qid } pendant un glissement
@@ -1685,8 +1566,8 @@ function renderBarActions() {
const bar = document.getElementById('barActions');
if (!bar) return;
bar.innerHTML = '';
// Bouton retour intégré au groupe centré (sauf sur le récapitulatif, qui a son propre retour)
if (history.length > 0 && !(currentSlide === 7 && etiRecapShown)) {
// Bouton retour intégré au groupe centré
if (history.length > 0) {
barBtn(bar, '← Retour', 'is-secondary', goBack);
}
switch (currentSlide) {
@@ -1713,14 +1594,9 @@ function renderBarActions() {
barBtn(bar, 'Répondre aux questions →', 'is-primary', () => goTo(7));
break;
case 7:
if (etiRecapShown) {
barBtn(bar, '← Retour à l\'exercice', 'is-secondary', hideEtiRecap);
barBtn(bar, 'Voir la synthèse générale →', 'is-primary', () => goTo(8));
} else {
barBtn(bar, '↺ Recommencer', 'is-secondary', resetEtiquette);
barBtn(bar, 'Terminer l\'exercice →', 'is-primary', showEtiRecap,
{ id: 'eti-recap-btn', disabled: etiPos < etiOrder.length });
}
barBtn(bar, '↺ Recommencer', 'is-secondary', resetEtiquette);
barBtn(bar, 'Terminer l\'exercice', 'is-primary', () => goTo(8),
{ id: 'eti-recap-btn', disabled: etiPos < etiOrder.length });
break;
case 8:
// Boutons placés sous le tableau comparatif, dans la slide (pas dans la bottom bar)

View File

@@ -17,7 +17,7 @@
justify-content: center;
padding: 2rem;
padding-top: calc(70px + 2rem);
padding-bottom: calc(52px + 2rem);
padding-bottom: calc(84px + 2rem);
opacity: 0;
transition: opacity 0.4s ease;
overflow-y: auto;
@@ -38,73 +38,67 @@
transition: width 0.4s ease;
}
/* PHASE INDICATOR */
.phase-badge {
position: fixed;
top: 80px; right: 16px;
font-family: 'Syne', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 4px 12px;
border-radius: 20px;
z-index: 100;
}
/* BOTTOM BAR */
.bottom-bar {
position: fixed;
bottom: 0; left: 0; right: 0;
height: 52px;
height: 84px;
background: rgba(250, 238, 218, 0.98);
border-top: 2px solid var(--amber);
display: flex;
align-items: center;
justify-content: space-between;
justify-content: center;
padding: 0 20px;
z-index: 100;
backdrop-filter: blur(10px);
}
/* SLIDE NAV */
.bottom-actions {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
justify-content: center;
}
.footer-btn {
font-family: 'Syne', sans-serif;
font-size: 13px;
font-weight: 600;
padding: 11px 24px;
border-radius: 40px;
border: 1.5px solid var(--amber);
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
line-height: 1;
transition: all 0.2s;
}
.footer-btn:hover { transform: translateY(-1px); }
.footer-btn:active { transform: scale(0.98); }
.footer-btn.is-primary { background: var(--amber); color: #fff; }
.footer-btn.is-primary:hover { background: var(--amber-dark); }
.footer-btn.is-secondary { background: transparent; color: var(--amber-dark); }
.footer-btn.is-secondary:hover { background: rgba(186,117,23,0.08); }
.slide-counter {
position: absolute;
left: 20px;
font-size: 12px;
font-family: 'Syne', sans-serif;
font-weight: 600;
color: var(--amber-dark);
}
/* BACK BUTTON */
.back-btn {
background: none;
border: 0.5px solid var(--amber);
border-radius: 20px;
padding: 6px 16px;
font-size: 12px;
color: var(--amber-dark);
cursor: pointer;
transition: all 0.2s;
font-family: 'DM Sans', sans-serif;
@media (max-width: 560px) {
.slide-counter { display: none; }
}
.back-btn:hover { background: rgba(186,117,23,0.08); }
/* FORMATEUR SELECTOR */
.formateur-btn {
font-family: 'Syne', sans-serif;
font-size: 14px;
font-weight: 600;
padding: 10px 24px;
border-radius: 30px;
border: 1.5px solid var(--border);
background: white;
color: var(--text-muted);
cursor: pointer;
transition: all 0.2s;
flex: 1;
flex: 1; font-family: 'DM Sans', sans-serif; font-size: 13.5px;
padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px;
background: var(--bg); color: var(--text); cursor: pointer; transition: all 0.2s ease;
}
.formateur-btn:hover { border-color: var(--amber); color: var(--amber); }
.formateur-btn.selected { background: var(--amber-light); border-color: var(--amber); color: var(--amber-dark); font-weight: 700; }
.formateur-btn:hover { border-color: var(--amber); }
.formateur-btn.selected { background: var(--amber); border-color: var(--amber); color: #fff; }
/* CARDS & BUTTONS */
.btn {
@@ -246,52 +240,42 @@
.q-item { flex-direction: column; align-items: stretch; }
.q-item .q-head { display: flex; align-items: flex-start; gap: 12px; }
/* IDENTIFICATION SCREEN */
.id-form {
width: 100%;
max-width: 420px;
display: flex;
flex-direction: column;
gap: 14px;
margin: 1.25rem 0;
}
.id-field label {
display: block;
font-family: 'Syne', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 6px;
text-align: left;
}
.id-field input {
width: 100%;
font-family: 'DM Sans', sans-serif;
font-size: 15px;
padding: 12px 16px;
border: 1.5px solid var(--border);
border-radius: 10px;
background: white;
color: var(--text);
}
.id-field input:focus {
outline: none;
border-color: var(--amber);
}
/* Carte d'identité encadrée (modèle BioCID) */
.id-card {
/* IDENTIFICATION SCREEN — carte modèle BioCID / FDS */
.identity-card {
background: white;
border: 0.5px solid var(--border);
border-radius: 16px;
padding: 20px 22px;
border-radius: 14px;
padding: 16px 18px;
max-width: 460px;
width: 100%;
margin: 1.25rem auto;
box-shadow: 0 8px 30px rgba(0,0,0,0.04);
text-align: left;
}
.id-card .id-form { margin: 0; max-width: none; }
.identity-card .identity-title {
font-family: 'Syne', sans-serif;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--amber-dark);
margin-bottom: 10px;
display: flex; align-items: center; gap: 6px;
}
.identity-fields { display: flex; flex-direction: column; gap: 8px; }
.identity-fields input {
font-family: 'DM Sans', sans-serif;
font-size: 13.5px;
color: var(--text);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 10px;
padding: 9px 13px;
width: 100%;
box-sizing: border-box;
}
.identity-fields input:focus { outline: none; border-color: var(--amber); background: white; }
.formateur-label { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); margin: 4px 0 2px; }
.formateur-row { display: flex; gap: 8px; }
/* TABLE FORM CONTROLS */
.risk-table input, .risk-table textarea {
@@ -599,7 +583,7 @@
gap: 12px;
max-width: 640px;
width: 100%;
margin: 1.5rem 0;
margin: 1.5rem auto;
}
.final-card {
background: white;
@@ -631,7 +615,7 @@
/* SCROLLABLE AREA */
.scroll-area {
overflow-y: auto;
max-height: calc(100vh - 232px);
max-height: calc(100vh - 200px);
width: 100%;
display: flex;
flex-direction: column;
@@ -672,13 +656,14 @@
<!-- Progress bar -->
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
<!-- Phase badge -->
<div class="phase-badge" id="phaseBadge" style="display:none"></div>
<!-- Bottom bar -->
<div class="bottom-bar" id="bottomBar" style="display:none;">
<button class="back-btn" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
<div class="slide-counter" id="slideCounter"></div>
<div class="bottom-actions">
<button class="footer-btn is-secondary" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
<button class="footer-btn is-secondary" id="footerHintBtn" onclick="showHintCurrent()" style="display:none;">💡 Besoin d'aide ?</button>
<button class="footer-btn is-primary" id="footerNextBtn" onclick="goNext()" style="display:none;">Étape suivante →</button>
</div>
</div>
<!-- Modal overlay -->
@@ -691,52 +676,41 @@
</div>
</div>
<!-- ======================== SLIDE 0 — IDENTIFICATION ======================== -->
<!-- ======================== SLIDE 0 — ACCUEIL ======================== -->
<div class="slide active fade-in" id="slide-0">
<div class="deco-circle" style="width:320px;height:320px;background:var(--amber-light);top:-80px;left:-80px;opacity:0.5;"></div>
<div style="text-align:center; z-index:1; width:100%;">
<div class="section-tag" style="background:var(--amber-light);color:var(--amber-dark);">Avant de commencer</div>
<div class="slide-title">Identification du groupe</div>
<p class="slide-intro" style="margin:0.5rem auto 0;max-width:420px;">Ces informations permettront au formateur de retrouver vos réponses.</p>
<div class="id-card">
<div class="id-form">
<div class="id-field">
<label for="inputNom">Nom du stagiaire ou du groupe</label>
<input type="text" id="inputNom" placeholder="Ex : Groupe 3 - Dupont / Martin" autocomplete="off">
</div>
<div class="id-field">
<label for="inputDate">Date de la session</label>
<input type="date" id="inputDate">
</div>
<div class="id-field">
<label>Formateur de la session</label>
<div style="display:flex;gap:10px;margin-top:2px;">
<button type="button" class="formateur-btn" id="btnNathan" onclick="selectFormateur('Nathan')">Nathan</button>
<button type="button" class="formateur-btn" id="btnGauthier" onclick="selectFormateur('Gauthier')">Gauthier</button>
</div>
</div>
</div>
</div>
<div id="idError" style="color:var(--coral);font-size:13px;margin-bottom:0.5rem;display:none;">Merci de renseigner au moins le nom du groupe.</div>
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap;">
<button class="btn btn-teal" onclick="confirmIdentite()">Continuer →</button>
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">Corriger</a>
</div>
<div class="deco-circle" style="width:400px;height:400px;background:var(--amber-light);top:-100px;right:-80px;opacity:0.6;"></div>
<div class="deco-circle" style="width:200px;height:200px;background:var(--amber-light);bottom:60px;left:-60px;opacity:0.5;"></div>
<div style="text-align:center;">
<div class="hero-label">Certibiocide — Travaux pratiques</div>
<div class="hero-title">Étude de cas<br>Nuisibles</div>
<p class="hero-sub">Votre mission, si vous l'acceptez : maîtriser l'identification, le traitement et la sécurité face aux nuisibles.</p>
<button class="btn btn-amber" onclick="goTo(1)">Démarrer la mission →</button>
</div>
</div>
<!-- ======================== SLIDE 1 — ACCUEIL ======================== -->
<!-- ======================== SLIDE 1 — CONTEXTE & IDENTIFICATION ======================== -->
<div class="slide" id="slide-1">
<div class="deco-circle" style="width:400px;height:400px;background:var(--amber-light);top:-100px;right:-80px;opacity:0.6;"></div>
<div class="deco-circle" style="width:200px;height:200px;background:var(--amber-light);bottom:60px;left:-60px;opacity:0.5;"></div>
<div class="deco-circle" style="width:300px;height:300px;background:var(--amber-light);top:-60px;left:-60px;opacity:0.4;"></div>
<div class="section-tag" style="background:var(--amber-light);color:var(--amber-dark);">Avant de commencer</div>
<div class="slide-title">Certibiocide — Travaux pratiques</div>
<p class="slide-intro">Votre mission, si vous l'acceptez : maîtriser l'identification, le traitement et la sécurité face aux nuisibles.</p>
<div class="info-card">
<div class="slide-title">Contexte & accès</div>
<p class="slide-intro">3 travaux pratiques vous attendent : protocole d'intervention, recherche produit sur BioCID, puis analyse de risques. Ces informations permettront au formateur de retrouver vos réponses.</p>
<div class="info-card" style="max-width:620px;">
<span>🎯</span>
<div>3 travaux pratiques vous attendent : définir un <strong>protocole d'intervention</strong>, trouver votre <strong>produit sur BioCID</strong>, puis réaliser l'<strong>analyse de risques</strong>.</div>
<div>Vous allez définir un <strong>protocole d'intervention</strong>, trouver votre <strong>produit sur BioCID</strong>, puis réaliser l'<strong>analyse de risques</strong>.</div>
</div>
<div class="identity-card" style="max-width:620px;margin-top:0.5rem;">
<div class="identity-title">👥 Identification du groupe</div>
<div class="identity-fields">
<input type="text" id="inputNom" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off">
<input type="date" id="inputDate">
<div class="formateur-label">Formateur de la session</div>
<div class="formateur-row">
<button type="button" class="formateur-btn" id="btnNathan" onclick="selectFormateur('Nathan')">Nathan</button>
<button type="button" class="formateur-btn" id="btnGauthier" onclick="selectFormateur('Gauthier')">Gauthier</button>
</div>
</div>
<div id="idError" style="color:var(--coral);font-size:13px;margin-top:8px;display:none;">Merci de renseigner au moins le nom du groupe.</div>
</div>
<button class="btn btn-teal" style="margin-top:1.5rem;" onclick="goTo(2)">Démarrer la mission →</button>
</div>
<!-- ======================== SLIDE 2 — CHOIX DU CAS ======================== -->
@@ -776,9 +750,6 @@
<div class="case-sub">Camping</div>
</div>
</div>
<div class="actions-row">
<button class="btn btn-teal" id="btnConfirmCase" onclick="goTo(3)" style="display:none">Confirmer le choix →</button>
</div>
</div>
<!-- ======================== SLIDE 3 — CONSIGNES ======================== -->
@@ -806,7 +777,6 @@
<span>💡</span>
<div>À tout moment, cliquez sur <strong>« Besoin d'aide ? »</strong> pour accéder à un indice. Le formateur corrigera à l'oral en fin de séance.</div>
</div>
<button class="btn btn-teal" onclick="goTo(4)">Commencer le TP 1 →</button>
</div>
<!-- ======================== SLIDE 4 — TP1 INTRO ======================== -->
@@ -824,7 +794,6 @@
<div class="step-pill">2. Intervention</div>
<div class="step-pill">3. Évaluation</div>
</div>
<button class="btn btn-amber" onclick="goTo(5)">Commencer →</button>
</div>
<!-- ======================== SLIDE 5 — TP1 PREVENTION ======================== -->
@@ -865,10 +834,6 @@
<textarea class="answer-field" id="ans_prevention_4" placeholder="Votre réponse…" data-question="Éléments observés / preuve de présence du nuisible"></textarea>
</li>
</ul>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('prevention')">💡 Besoin d'aide ?</button>
<button class="btn btn-amber" onclick="goTo(6)">Étape suivante →</button>
</div>
</div>
</div>
@@ -903,10 +868,6 @@
<textarea class="answer-field" id="ans_intervention_3" placeholder="Votre réponse…" data-question="Type de produit (TP) et mode de traitement"></textarea>
</li>
</ul>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('intervention')">💡 Besoin d'aide ?</button>
<button class="btn btn-amber" onclick="goTo(7)">Étape suivante →</button>
</div>
</div>
</div>
@@ -937,10 +898,6 @@
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:680px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.75rem 0;">
<strong>TP 1 terminé !</strong> Préparez-vous à identifier le produit adapté à votre cas sur biocid-anses.fr.
</div>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('evaluation')">💡 Besoin d'aide ?</button>
<button class="btn btn-coral" onclick="goTo(8)">Passer au TP 2 →</button>
</div>
</div>
</div>
@@ -962,7 +919,6 @@
</div>
<a href="https://biocid-anses.fr" target="_blank" class="resource-link">Ouvrir le site ↗</a>
</div>
<button class="btn btn-coral" onclick="goTo(9)">Commencer →</button>
</div>
<!-- ======================== SLIDE 9 — TP2 FDS & AMM ======================== -->
@@ -1003,10 +959,6 @@
<textarea class="answer-field" id="ans_fds_4" placeholder="Votre réponse…" data-question="Risques pour la santé"></textarea>
</li>
</ul>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('fds')">💡 Besoin d'aide ?</button>
<button class="btn btn-coral" onclick="goTo(10)">Partie suivante →</button>
</div>
</div>
</div>
@@ -1051,10 +1003,6 @@
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:680px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.75rem 0;">
<strong>TP 2 terminé !</strong> Passez maintenant à l'analyse de risques complète.
</div>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('epi')">💡 Besoin d'aide ?</button>
<button class="btn btn-purple" onclick="goTo(11)">Passer au TP 3 →</button>
</div>
</div>
</div>
@@ -1071,9 +1019,6 @@
<div class="pyr-level" style="width:320px;background:#D85A30;">Communication</div>
<div class="pyr-level" style="width:360px;background:#BA7517;">EPI (dernier recours)</div>
</div>
<div class="actions-row" style="margin-top:1rem;">
<button class="btn btn-purple" onclick="goTo(12)">Remplir le tableau →</button>
</div>
</div>
<!-- ======================== SLIDE 12 — TP3 TABLEAU ======================== -->
@@ -1142,10 +1087,6 @@
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:780px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.25rem 0;">
<strong>TP 3 terminé !</strong> Présentez vos réponses au formateur pour la correction.
</div>
<div class="actions-row">
<button class="hint-btn" onclick="showHint('analyse')">💡 Aide générale</button>
<button class="btn btn-blue" onclick="goTo(13)">Terminer la mission →</button>
</div>
</div>
</div>
@@ -1175,15 +1116,10 @@
<div class="final-card-sub">Analyse de risques</div>
</div>
</div>
<div class="submit-zone">
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;">
<button class="btn btn-teal" onclick="exporterPDF()">⬇ Télécharger en PDF</button>
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">Corriger</a>
</div>
</div>
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:0.75rem;">
<a href="https://biocid-anses.fr" target="_blank" class="btn btn-outline btn-teal" style="font-size:13px;padding:10px 20px;">biocid-anses.fr ↗</a>
<button class="btn btn-teal" onclick="restartAll()">↺ Recommencer depuis le début</button>
<div class="actions-row">
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">✔ Corriger un rendu</a>
<button class="btn btn-outline btn-teal" onclick="restartAll()">↺ Recommencer</button>
</div>
</div>
</div>
@@ -1196,6 +1132,22 @@ const totalSlides = 13;
const selectedCase = { key: '', icon: '', title: '', desc: '' };
const stagiaire = { nom: '', date: '', formateur: '' };
// Configuration des boutons du footer par slide (modèle BioCID)
const slideConfig = {
1: { next: 2, nextLabel: 'Continuer →' },
2: { next: 3, nextLabel: 'Confirmer le choix →' },
3: { next: 4, nextLabel: 'Commencer le TP 1 →' },
4: { next: 5, nextLabel: 'Commencer →' },
5: { next: 6, nextLabel: 'Étape suivante →', hint: 'prevention' },
6: { next: 7, nextLabel: 'Étape suivante →', hint: 'intervention' },
7: { next: 8, nextLabel: 'Passer au TP 2 →', hint: 'evaluation' },
8: { next: 9, nextLabel: 'Commencer →' },
9: { next: 10, nextLabel: 'Partie suivante →', hint: 'fds' },
10: { next: 11, nextLabel: 'Passer au TP 3 →', hint: 'epi' },
11: { next: 12, nextLabel: 'Remplir le tableau →' },
12: { next: 13, nextLabel: 'Terminer la mission →', hint: 'analyse' }
};
const hints = {
prevention: {
icon: '💡',
@@ -1282,11 +1234,28 @@ function goTo(n) {
currentSlide = n;
next.style.display = 'flex';
setTimeout(() => { next.classList.add('active', 'fade-in'); }, 10);
const sa = next.querySelector('.scroll-area');
if (sa) sa.scrollTop = 0;
if (n === 13) setTimeout(launchConfetti, 200);
updateUI();
updateCaseBanners();
}
function goNext() {
const cfg = slideConfig[currentSlide];
if (!cfg || cfg.next == null) return;
// Slide 1 : validation du nom du groupe avant de continuer
if (currentSlide === 1) { confirmIdentite(); return; }
// Slide 2 : un cas doit être sélectionné
if (currentSlide === 2 && !selectedCase.key) return;
goTo(cfg.next);
}
function showHintCurrent() {
const cfg = slideConfig[currentSlide];
if (cfg && cfg.hint) showHint(cfg.hint);
}
function goBack() {
if (history.length === 0) return;
const prev = history.pop();
@@ -1311,26 +1280,19 @@ function updateUI() {
if (bottomBar) bottomBar.style.display = currentSlide === 0 ? 'none' : 'flex';
const backBtn = document.getElementById('backBtn');
backBtn.style.display = history.length > 0 ? 'block' : 'none';
backBtn.style.display = history.length > 0 ? 'inline-flex' : 'none';
const badge = document.getElementById('phaseBadge');
if (currentSlide === 0 || currentSlide === 1 || currentSlide === 2 || currentSlide === 3 || currentSlide === 13) {
badge.style.display = 'none';
} else if (currentSlide >= 4 && currentSlide <= 7) {
badge.style.display = 'block';
badge.style.background = 'var(--amber-light)';
badge.style.color = 'var(--amber-dark)';
badge.textContent = 'TP 1 — Protocole';
} else if (currentSlide >= 8 && currentSlide <= 10) {
badge.style.display = 'block';
badge.style.background = 'var(--coral-light)';
badge.style.color = 'var(--coral)';
badge.textContent = 'TP 2 — Produit';
} else if (currentSlide >= 11 && currentSlide <= 12) {
badge.style.display = 'block';
badge.style.background = 'var(--amber-light)';
badge.style.color = 'var(--amber-dark)';
badge.textContent = 'TP 3 — Risques';
const cfg = slideConfig[currentSlide] || {};
const hintBtn = document.getElementById('footerHintBtn');
const nextBtn = document.getElementById('footerNextBtn');
hintBtn.style.display = cfg.hint ? 'inline-flex' : 'none';
if (cfg.next != null) {
// Slide 2 : le bouton n'apparaît qu'une fois le cas choisi
const gated = currentSlide === 2 && !selectedCase.key;
nextBtn.style.display = gated ? 'none' : 'inline-flex';
nextBtn.textContent = cfg.nextLabel;
} else {
nextBtn.style.display = 'none';
}
}
@@ -1342,7 +1304,7 @@ function selectCase(key, icon, title, desc) {
selectedCase.desc = desc;
document.querySelectorAll('.case-card').forEach(c => c.classList.remove('selected'));
event.currentTarget.classList.add('selected');
document.getElementById('btnConfirmCase').style.display = 'inline-flex';
updateUI();
}
function updateCaseBanners() {
@@ -1412,7 +1374,7 @@ function confirmIdentite() {
document.getElementById('idError').style.display = 'none';
stagiaire.nom = nom;
stagiaire.date = date || new Date().toISOString().slice(0, 10);
goTo(1);
goTo(2);
}
/* ======================== TABLEAU TP3 — LIGNES DYNAMIQUES ======================== */

View File

@@ -319,6 +319,57 @@ h1 {
vertical-align: top;
}
.annex th { background: var(--amber-light); font-family: 'Syne', sans-serif; font-size: 12px; }
/* ======= Corrigé de référence — étiquette annotée (repris du TD FDS) ======= */
.recap-intro { font-size: 13.5px; color: var(--text-muted); line-height: 1.6; margin: 0 0 16px; }
.recap-layout { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.recap-stage { flex: 1 1 320px; min-width: 0; max-width: 560px; }
.recap-layout .checklist-table { flex: 1 1 320px; min-width: 0; margin: 0; }
.label-card {
position: relative;
width: 100%; max-width: 640px;
background: #fff; color: #1a1a18;
border: 1px solid #d9d2c4; border-radius: 6px;
padding: 16px 18px 0; overflow: hidden;
font-family: 'DM Sans', sans-serif; font-size: 10.5px; line-height: 1.42;
box-shadow: 0 8px 30px rgba(0,0,0,0.10);
}
.lbl-top { display: flex; align-items: center; gap: 12px; border-bottom: 2px solid #1a1a18; padding-bottom: 6px; margin-bottom: 10px; }
.lbl-flag { font-size: 20px; }
.lbl-title { font-family: 'Syne', sans-serif; font-weight: 800; font-size: clamp(24px, 5vw, 40px); letter-spacing: -0.01em; margin: 0; }
.lbl-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.lbl-h { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 2px; }
.lbl-block { margin-bottom: 9px; }
.lbl-small { color: #555; font-size: 9.5px; line-height: 1.6; margin-top: 4px; }
.lbl-bottom { display: grid; grid-template-columns: auto 1fr auto; align-items: end; gap: 14px; margin-top: 4px; padding: 10px 0; }
.lbl-net { display: flex; flex-direction: column; gap: 2px; }
.lbl-net span { font-size: 10px; }
.lbl-net strong { font-family: 'Syne', sans-serif; font-size: 22px; line-height: 1; }
.lbl-barcode { height: 42px; align-self: end; min-width: 90px; background: repeating-linear-gradient(90deg, #1a1a18 0 2px, #fff 2px 4px, #1a1a18 4px 5px, #fff 5px 9px); }
.lbl-picto-wrap { display: flex; align-items: center; gap: 8px; }
.pictos { display: flex; gap: 6px; align-items: center; }
.ghs-img { width: 42px; height: 42px; object-fit: contain; display: block; }
.lbl-danger { font-family: 'Syne', sans-serif; font-weight: 800; font-size: 15px; color: #1a1a18; letter-spacing: 0.02em; }
.lbl-footer { margin: 6px -18px 0; padding: 8px 18px; background: #2b2b28; color: #e8e2d5; text-align: center; font-size: 9px; line-height: 1.5; }
.hotspot { position: relative; border-radius: 5px; }
span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
.hotspot.hs-correct { background: rgba(46,125,50,0.14); outline: 1.5px solid #2e7d32; }
.hs-num { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 9px; margin-right: 3px; vertical-align: middle; }
.checklist-table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; font-size: 12.5px; }
.checklist-table th { background: var(--teal-light); color: var(--teal); font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; padding: 10px 12px; text-align: left; border-bottom: 2px solid var(--teal); }
.checklist-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--text-muted); vertical-align: top; }
.checklist-table tr:last-child td { border-bottom: none; }
.checklist-table td.cl-num { text-align: center; }
.cl-badge { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 10px; }
.checklist-table strong { color: var(--text); }
@media (max-width: 640px) {
.lbl-cols { grid-template-columns: 1fr; }
.lbl-title { font-size: 26px; }
}
</style>
</head>
<body>
@@ -618,6 +669,109 @@ function fdsExpectedLabel(part, q) {
return parts[0] === "absent" ? "Absent de la FDS" : "Rubrique " + parts[0];
}
/* ---- Corrigé de référence : l'étiquette annotée + check-list (repris du TD) ---- */
const ETI_ZONES = [
{ zone: "z-nom", label: "Nom commercial",
reg: "Obligation CLP : le nom commercial doit être identique à celui figurant sur la FDS (Guide FHER, Partie 3 — I.2)." },
{ zone: "z-picto", label: "Pictogrammes de danger",
reg: "CLP : pictogrammes aux dimensions requises, regroupés avec la mention d'avertissement et les phrases H/P, se détachant nettement du fond (Partie 3 — I.5)." },
{ zone: "z-mention", label: "Mention d'avertissement",
reg: "CLP : « Danger » ou « Attention », disposée dans l'encart d'étiquetage avec pictogrammes et phrases H/P (Partie 3 — I.5)." },
{ zone: "z-h", label: "Mentions de danger (phrases H)",
reg: "CLP : les phrases H décrivent la nature du danger ; regroupées dans l'encart d'étiquetage (Partie 3 — I.5)." },
{ zone: "z-p-epi", label: "Conseils de prudence — EPI",
reg: "CLP : conseils de prudence P (ici P280). Six conseils P au maximum, sauf si la nature et la gravité des dangers l'exigent (CLP Art. 28-3) — Partie 3, I.5." },
{ zone: "z-p-secours", label: "Premiers secours (phrases P)",
reg: "BPR : indications relatives aux premiers soins (Partie 3 — II.8), traduites par les conseils P 3xx (contact, ingestion, inhalation)." },
{ zone: "z-p-elim", label: "Élimination du produit et de l'emballage",
reg: "BPR : instructions pour l'élimination en toute sécurité du produit et de son emballage, le cas échéant avec interdiction de le réutiliser (Partie 3 — II.10). Ici P501." },
{ zone: "z-net", label: "Contenu net",
reg: "Quantité nette de produit contenue dans l'emballage — mention générale du pavé d'information produit." },
{ zone: "z-antipoison", label: "Centre antipoison / n° d'urgence",
reg: "Coordonnées d'urgence complétant les instructions de premiers soins (Partie 3 — II.8) ; les mélanges dangereux sont déclarés aux centres antipoison (annexe VIII du CLP)." },
];
const ETI_LABEL_CARD = `
<div class="label-card recap">
<div class="lbl-top">
<span class="lbl-flag">🇫🇷</span>
<span class="hotspot" data-zone="z-nom"><span class="lbl-title">DÉS'INFECT</span></span>
</div>
<div class="lbl-cols">
<div class="lbl-col">
<div class="lbl-block"><div class="lbl-h">Propriétés :</div>Désinfecte et désodorise les sanitaires. Produit strictement destiné à un usage professionnel.</div>
<div class="lbl-block"><div class="lbl-h">Mode d'emploi :</div>Concentré à diluer : 5 % pour une activité fongicide, 1 % pour une activité bactéricide. Pulvériser sur les parois, laisser agir 5 à 15 min, rincer à l'eau claire. Type de formulation : concentré soluble (SL). Utilisable en TP2.</div>
<div class="lbl-small">UFI : 1234-1234-1234-1234<br>Date de péremption : MM/AAAA<br>N° LOT : 123456</div>
</div>
<div class="lbl-col">
<div class="lbl-block"><div class="lbl-h">Substances actives :</div>Chlorure de didécyldiméthylammonium (N°CAS 7173-51-5) à 75 g/kg ; acide glycolique (CAS 79-14-1) à 35 g/kg.</div>
<div class="lbl-block">
<div class="lbl-h">Précautions d'emploi :</div>
<span>Produit strictement professionnel (FR) — Lire le mode d'emploi ci-joint avant l'emploi. </span>
<span class="hotspot" data-zone="z-antipoison">Formule déposée au centre anti-poison de Nancy : 03 83 22 50 50.</span>
<span> </span>
<span class="hotspot" data-zone="z-h">H302 Nocif en cas d'ingestion. H314 Provoque de graves brûlures de la peau et des lésions oculaires. H410 Très toxique pour les organismes aquatiques, entraîne des effets néfastes à long terme.</span>
<span> </span>
<span class="hotspot" data-zone="z-p-epi">P280 Porter des gants de protection, des vêtements de protection et un équipement de protection du visage.</span>
<span> </span>
<span class="hotspot" data-zone="z-p-secours">P301+P330+P331 EN CAS D'INGESTION : rincer la bouche, ne pas faire vomir. P305+P351+P338 EN CAS DE CONTACT AVEC LES YEUX : rincer avec précaution à l'eau pendant plusieurs minutes. P310 Appeler immédiatement un CENTRE ANTIPOISON ou un médecin.</span>
<span> </span>
<span class="hotspot" data-zone="z-p-elim">P501 Éliminer le contenu / le récipient dans un point de collecte des déchets spéciaux ou dangereux.</span>
</div>
</div>
</div>
<div class="lbl-bottom">
<span class="hotspot" data-zone="z-net">
<span class="lbl-net"><span>Volume net :</span><strong>5 L</strong></span>
</span>
<div class="lbl-barcode" aria-hidden="true"></div>
<div class="lbl-picto-wrap">
<span class="hotspot" data-zone="z-picto">
<span class="pictos">
<img class="ghs-img" src="../data/ghs09-environnement.jpg" alt="Pictogramme SGH09 — danger pour le milieu aquatique">
<img class="ghs-img" src="../data/ghs05-corrosif.jpg" alt="Pictogramme SGH05 — corrosif">
<img class="ghs-img" src="../data/ghs07-nocif.jpg" alt="Pictogramme SGH07 — nocif / irritant">
</span>
</span>
<span class="hotspot" data-zone="z-mention"><span class="lbl-danger">DANGER</span></span>
</div>
</div>
<div class="lbl-footer">FHER — 2, rue de Sèze, 75009 Paris — Tél. 01 23 45 67 89</div>
</div>`;
// Construit le corrigé de référence : étiquette annotée (zones numérotées) + check-list réglementaire.
function buildEtiquetteRecapHtml() {
const wrap = document.createElement("div");
wrap.innerHTML = ETI_LABEL_CARD;
const card = wrap.querySelector(".label-card");
card.querySelectorAll(".hotspot").forEach(h => {
h.classList.add("hs-correct");
const idx = ETI_ZONES.findIndex(z => z.zone === h.dataset.zone);
if (idx >= 0) {
const num = document.createElement("b");
num.className = "hs-num";
num.textContent = idx + 1;
h.insertBefore(num, h.firstChild);
}
});
let rows = '<thead><tr><th style="width:34px;">N°</th><th>Élément d\'étiquetage</th><th>Base réglementaire</th></tr></thead><tbody>';
ETI_ZONES.forEach((z, i) => {
rows += `<tr>
<td class="cl-num"><span class="cl-badge">${i + 1}</span></td>
<td><strong>${escapeHtml(z.label)}</strong></td>
<td>${escapeHtml(z.reg)}</td>
</tr>`;
});
rows += "</tbody>";
return `<p class="recap-intro">Les 9 zones réglementaires et leurs bonnes réponses, présentées comme la <strong>check-list officielle d'étiquetage des produits biocides</strong> (Guide FHER, p. 15) — un outil réutilisable en conditions réelles.</p>
<div class="recap-layout">
<div class="recap-stage">${card.outerHTML}</div>
<table class="checklist-table">${rows}</table>
</div>`;
}
function parseFds(raw) {
let clean = flatten(raw);
clean = clean.replace(/FDS, FT (&|&amp;) Étiquettes\s*[—–-]\s*Réponses\s*\([^)]*\)/gi, " ").replace(/\s+/g, " ").trim();
@@ -644,7 +798,12 @@ function parseFds(raw) {
}),
}));
return { title: "FDS, FT & Étiquettes — Correction du rendu", meta, groups, scored: true };
const annexes = [{
title: "Récapitulatif — l'étiquette annotée (corrigé de référence)",
html: buildEtiquetteRecapHtml(),
}];
return { title: "FDS, FT & Étiquettes — Correction du rendu", meta, groups, scored: true, annexes };
}
/* ======================= EXERCICE 3 — TP NUISIBLES ======================= */