Slide 1 : blocage de la navigation tant que le nom du stagiaire/groupe n'est pas renseigné, et choix du formateur via boutons Nathan/Gauthier. Slide 3 : étiquettes renommées « Rubrique 1…16 » (au lieu de « R… ») et élargies. Slide 5 : suppression du tag « Partie 2/3 — FT ». Slide 7 : suppression du tag « Partie 3/3 — Étiquette » ; l'encadré de réponse s'affiche à côté de l'étiquette (plus en superposition) ; le récapitulatif fait glisser l'étiquette à gauche et affiche le tableau de réponses à sa droite.
1729 lines
72 KiB
HTML
1729 lines
72 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>FDS, FT & Étiquettes — Gauthier Chombart & Nathan Chauwin</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="../header-style.css"/>
|
||
<style>
|
||
/* SLIDES */
|
||
.slide {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: none;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 2rem;
|
||
padding-top: calc(70px + 2rem);
|
||
padding-bottom: calc(52px + 2rem);
|
||
opacity: 0;
|
||
transition: opacity 0.4s ease;
|
||
overflow-y: auto;
|
||
min-height: calc(100vh - 70px);
|
||
}
|
||
.slide.active { display: flex; opacity: 1; }
|
||
.slide.fade-in { animation: fadeIn 0.5s ease forwards; }
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(16px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.progress-fill { background: var(--amber); 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;
|
||
background: rgba(250, 238, 218, 0.98);
|
||
border-top: 2px solid var(--amber);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 20px;
|
||
z-index: 100;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
.slide-counter { font-size: 12px; font-family: 'Syne', sans-serif; font-weight: 600; color: var(--amber-dark); }
|
||
.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;
|
||
}
|
||
.back-btn:hover { background: rgba(186,117,23,0.08); }
|
||
|
||
/* BUTTONS */
|
||
.btn {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
padding: 12px 28px;
|
||
border-radius: 40px;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
text-decoration: none;
|
||
}
|
||
.btn:hover { transform: translateY(-1px); }
|
||
.btn:active { transform: scale(0.98); }
|
||
.btn-amber { background: var(--amber); color: white; }
|
||
.btn-coral { background: var(--coral-mid); color: white; }
|
||
.btn-teal { background: var(--teal-mid); color: white; }
|
||
.btn-outline { background: transparent; border: 1.5px solid var(--amber); color: var(--amber-dark); }
|
||
|
||
/* HERO / TITLES */
|
||
.hero-label {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--amber);
|
||
margin-bottom: 8px;
|
||
}
|
||
.hero-title {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: clamp(32px, 5.5vw, 58px);
|
||
font-weight: 800;
|
||
line-height: 1.02;
|
||
color: var(--text);
|
||
margin-bottom: 12px;
|
||
}
|
||
.hero-sub {
|
||
font-size: 16px;
|
||
color: var(--text-muted);
|
||
max-width: 480px;
|
||
text-align: center;
|
||
line-height: 1.6;
|
||
margin-bottom: 2rem;
|
||
}
|
||
.section-tag {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
margin-bottom: 10px;
|
||
display: inline-block;
|
||
background: var(--amber-light);
|
||
color: var(--amber-dark);
|
||
}
|
||
.slide-title {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: clamp(22px, 3.5vw, 34px);
|
||
font-weight: 800;
|
||
line-height: 1.15;
|
||
margin-bottom: 0.5rem;
|
||
text-align: center;
|
||
}
|
||
.slide-intro {
|
||
font-size: 14px;
|
||
color: var(--text-muted);
|
||
text-align: center;
|
||
max-width: 580px;
|
||
line-height: 1.6;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
.actions-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: center;
|
||
margin-top: 1.5rem;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* OVERVIEW CARDS */
|
||
.overview-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 14px;
|
||
max-width: 680px;
|
||
width: 100%;
|
||
margin: 1.5rem 0;
|
||
}
|
||
.overview-card {
|
||
background: white;
|
||
border: 0.5px solid var(--border);
|
||
border-radius: 14px;
|
||
padding: 1.25rem;
|
||
text-align: center;
|
||
}
|
||
.overview-icon { font-size: 30px; margin-bottom: 8px; }
|
||
.overview-title { font-family: 'Syne', sans-serif; font-size: 13px; font-weight: 700; margin-bottom: 4px; }
|
||
.overview-sub { font-size: 12px; color: var(--text-muted); line-height: 1.4; }
|
||
|
||
.info-card {
|
||
border-radius: 12px;
|
||
padding: 16px 20px;
|
||
max-width: 620px;
|
||
width: 100%;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
display: flex;
|
||
gap: 10px;
|
||
align-items: flex-start;
|
||
background: var(--amber-light);
|
||
color: var(--amber-dark);
|
||
}
|
||
|
||
/* QUIZ ROWS */
|
||
.quiz-list { width: 100%; max-width: 720px; display: flex; flex-direction: column; gap: 12px; }
|
||
.quiz-list.quiz-grid {
|
||
max-width: 1080px;
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
@media (max-width: 900px) {
|
||
.quiz-list.quiz-grid { grid-template-columns: 1fr; max-width: 720px; }
|
||
}
|
||
|
||
/* SLIDE 3 — DRAG & DROP */
|
||
#slide-3 {
|
||
justify-content: flex-start;
|
||
padding-top: calc(70px + 0.9rem);
|
||
padding-bottom: calc(52px + 0.6rem);
|
||
}
|
||
#slide-3 .slide-title { margin: 0.1rem 0 0.15rem; font-size: clamp(19px, 2.4vw, 26px); }
|
||
.dnd-hint { font-size: 12.5px; color: var(--text-muted); text-align: center; margin: 0 0 10px; }
|
||
.dnd-layout {
|
||
display: grid;
|
||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||
align-items: start;
|
||
gap: 14px;
|
||
width: 100%;
|
||
max-width: 1200px;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
#slide-3 .quiz-grid { gap: 8px; }
|
||
#slide-3 .verify-btn { padding: 9px 26px; }
|
||
#slide-3 .actions-row { margin-top: 0.8rem; }
|
||
.dnd-bank { display: grid; grid-template-columns: 1fr; gap: 6px; align-content: start; }
|
||
.dnd-tile {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 12px; font-weight: 800; white-space: nowrap;
|
||
padding: 8px 12px; min-width: 92px; text-align: center;
|
||
border-radius: 8px;
|
||
background: var(--amber-light); color: var(--amber-dark);
|
||
border: 1px solid var(--amber);
|
||
cursor: grab; user-select: none;
|
||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||
}
|
||
.dnd-tile:hover { transform: translateY(-1px); box-shadow: 0 4px 10px rgba(186,117,23,0.15); }
|
||
.dnd-tile:active { cursor: grabbing; }
|
||
.dnd-tile.armed { outline: 2px solid var(--amber-dark); box-shadow: 0 0 0 3px rgba(186,117,23,0.18); }
|
||
.dnd-tile.dragging { opacity: 0.4; }
|
||
#slide-3 .answer-row { padding: 6px 12px; gap: 10px; }
|
||
#slide-3 .question-num { min-width: 26px; height: 26px; font-size: 11px; }
|
||
#slide-3 .question-text { font-size: 13px; }
|
||
.drop-slot {
|
||
min-width: 92px; min-height: 36px;
|
||
border: 2px dashed var(--amber); border-radius: 8px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-family: 'Syne', sans-serif; font-size: 12px; font-weight: 800;
|
||
color: var(--amber-dark); background: var(--amber-light);
|
||
cursor: pointer; flex-shrink: 0;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.drop-slot::before { content: 'Déposez ici'; font-weight: 600; font-size: 11px; opacity: 0.55; }
|
||
.drop-slot.filled { border-style: solid; background: white; }
|
||
.drop-slot.filled::before { content: none; }
|
||
.drop-slot.drag-over { border-color: var(--amber-dark); background: white; transform: scale(1.04); }
|
||
.drop-slot.slot-correct { border-color: #2e7d32; color: #2e7d32; }
|
||
.drop-slot.slot-wrong { border-color: #c62828; color: #c62828; }
|
||
@media (max-width: 900px) {
|
||
.dnd-layout { grid-template-columns: 1fr; }
|
||
.dnd-bank { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); }
|
||
}
|
||
/* SLIDE 5 — RELIER (MATCHING) */
|
||
.match-hint { font-size: 12.5px; color: var(--text-muted); text-align: center; margin: -0.4rem 0 0.1rem; max-width: 640px; line-height: 1.5; }
|
||
.match-wrap {
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: 780px;
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
column-gap: clamp(56px, 12vw, 140px);
|
||
align-items: start;
|
||
}
|
||
.match-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; overflow: visible; }
|
||
.match-col { display: flex; flex-direction: column; gap: 14px; }
|
||
.match-right { justify-content: center; }
|
||
.match-item {
|
||
position: relative;
|
||
background: white;
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 12px 16px;
|
||
font-size: 13.5px;
|
||
color: var(--text);
|
||
line-height: 1.4;
|
||
min-height: 46px;
|
||
display: flex; align-items: center;
|
||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||
}
|
||
.match-left .match-item { padding-right: 24px; }
|
||
.match-right .match-item {
|
||
padding-left: 24px;
|
||
justify-content: center; text-align: center;
|
||
font-family: 'Syne', sans-serif; font-weight: 800; font-size: 13px;
|
||
color: var(--coral); background: var(--coral-light); border-color: var(--coral-mid);
|
||
}
|
||
.match-item.connected { border-color: var(--coral); box-shadow: 0 4px 12px rgba(153,60,29,0.10); }
|
||
.match-dot {
|
||
position: absolute; top: 50%; transform: translateY(-50%);
|
||
width: 16px; height: 16px; border-radius: 50%;
|
||
background: white; border: 2.5px solid var(--coral-mid);
|
||
cursor: pointer; z-index: 3; touch-action: none;
|
||
transition: transform 0.15s ease, background 0.15s ease;
|
||
}
|
||
.match-left .match-dot { right: -8px; }
|
||
.match-right .match-dot { left: -8px; }
|
||
.match-dot:hover { transform: translateY(-50%) scale(1.3); background: var(--coral-mid); }
|
||
.match-dot.filled { background: var(--coral-mid); }
|
||
.match-dot.active { background: var(--coral); border-color: var(--coral); transform: translateY(-50%) scale(1.3); }
|
||
@media (max-width: 700px) {
|
||
.match-hint { font-size: 12px; }
|
||
.match-wrap { column-gap: 44px; }
|
||
.match-item { font-size: 12.5px; padding: 10px 12px; }
|
||
.match-left .match-item { padding-right: 20px; }
|
||
.match-right .match-item { padding-left: 20px; }
|
||
}
|
||
|
||
.answer-row {
|
||
background: white;
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 14px 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
transition: all 0.25s ease;
|
||
}
|
||
.answer-row:hover { border-color: var(--amber); box-shadow: 0 6px 16px rgba(186,117,23,0.08); }
|
||
.question-num {
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
min-width: 30px;
|
||
height: 30px;
|
||
border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: var(--amber-light);
|
||
color: var(--amber-dark);
|
||
flex-shrink: 0;
|
||
}
|
||
.question-text { flex-grow: 1; font-size: 14px; color: var(--text); font-weight: 500; line-height: 1.4; }
|
||
.answer-label {
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
color: var(--amber-dark);
|
||
white-space: nowrap;
|
||
padding: 4px 10px;
|
||
background: var(--amber-light);
|
||
border: 1px solid var(--amber);
|
||
border-radius: 6px;
|
||
flex-shrink: 0;
|
||
}
|
||
.answer-icon { font-size: 1.2em; flex-shrink: 0; }
|
||
select {
|
||
font-family: 'DM Sans', sans-serif;
|
||
padding: 9px 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
min-width: 175px;
|
||
flex-shrink: 0;
|
||
}
|
||
select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px rgba(186,117,23,0.1); }
|
||
|
||
.verify-btn {
|
||
font-family: 'Syne', sans-serif;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 30px;
|
||
border-radius: 40px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
transition: all 0.2s ease;
|
||
}
|
||
.verify-btn:hover { transform: translateY(-1px); }
|
||
.verify-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
|
||
|
||
.result-message {
|
||
margin-top: 4px;
|
||
padding: 16px 20px;
|
||
border-radius: 10px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
display: none;
|
||
max-width: 720px;
|
||
width: 100%;
|
||
text-align: center;
|
||
}
|
||
.result-message.show { display: block; }
|
||
.result-message.success { background: var(--teal-light); border: 1.5px solid var(--teal-mid); color: var(--teal-dark); }
|
||
.result-message.error { background: var(--coral-light); border: 1.5px solid var(--coral-mid); color: var(--coral); }
|
||
|
||
/* COMPARISON TABLE */
|
||
.comparison-table {
|
||
width: 100%;
|
||
max-width: 760px;
|
||
border-collapse: collapse;
|
||
background: white;
|
||
border: 0.5px solid var(--border);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
font-size: 13px;
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
.comparison-table th {
|
||
background: var(--amber-light);
|
||
color: var(--amber-dark);
|
||
padding: 12px 16px;
|
||
text-align: left;
|
||
font-family: 'Syne', sans-serif;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.04em;
|
||
text-transform: uppercase;
|
||
border-bottom: 2px solid var(--amber);
|
||
}
|
||
.comparison-table td {
|
||
padding: 12px 16px;
|
||
border-bottom: 0.5px solid var(--border);
|
||
color: var(--text-muted);
|
||
}
|
||
.comparison-table tr:last-child td { border-bottom: none; }
|
||
.comparison-table strong { color: var(--text); }
|
||
|
||
/* TIPS LIST */
|
||
.tips-list { list-style: none; padding: 0; max-width: 680px; width: 100%; display: flex; flex-direction: column; gap: 8px; }
|
||
.tips-list li {
|
||
background: white;
|
||
border: 0.5px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 12px 16px 12px 40px;
|
||
position: relative;
|
||
font-size: 13.5px;
|
||
line-height: 1.5;
|
||
color: var(--text-muted);
|
||
}
|
||
.tips-list li::before { content: '💡'; position: absolute; left: 14px; }
|
||
.tips-list strong { color: var(--text); }
|
||
|
||
/* SCROLL AREA */
|
||
.scroll-area {
|
||
overflow-y: auto;
|
||
max-height: calc(100vh - 200px);
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
padding: 0.5rem 0 4rem;
|
||
}
|
||
.scroll-area::-webkit-scrollbar { width: 4px; }
|
||
.scroll-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
|
||
/* IDENTITY CARD */
|
||
.identity-card {
|
||
background: white;
|
||
border: 0.5px solid var(--border);
|
||
border-radius: 14px;
|
||
padding: 16px 18px;
|
||
max-width: 560px;
|
||
width: 100%;
|
||
margin: 0.5rem 0;
|
||
}
|
||
.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; }
|
||
|
||
/* DECO */
|
||
.deco-circle { position: fixed; border-radius: 50%; pointer-events: none; z-index: 0; }
|
||
.slide > * { position: relative; z-index: 1; }
|
||
|
||
@media (max-width: 700px) {
|
||
.overview-grid { grid-template-columns: 1fr; }
|
||
.answer-row { flex-wrap: wrap; }
|
||
select { width: 100%; min-width: 0; }
|
||
}
|
||
|
||
/* ============ SLIDE 7 — ÉTIQUETTE CLIQUABLE (HOTSPOTS) ============ */
|
||
#slide-7 { justify-content: flex-start; }
|
||
#slide-7 .scroll-area { gap: 0.75rem; }
|
||
|
||
/* Mise en situation */
|
||
.situation-box {
|
||
display: flex; gap: 12px; align-items: flex-start;
|
||
max-width: 680px; width: 100%;
|
||
background: var(--teal-light);
|
||
border: 1px solid var(--teal-mid);
|
||
border-left: 4px solid var(--teal-mid);
|
||
border-radius: 12px;
|
||
padding: 12px 16px;
|
||
color: var(--teal-dark);
|
||
font-size: 13.5px; line-height: 1.55;
|
||
text-align: left;
|
||
}
|
||
.situation-box .sit-ico { font-size: 22px; line-height: 1.1; flex-shrink: 0; }
|
||
.situation-box strong { color: var(--teal-dark); }
|
||
|
||
/* Barre de consigne / question courante */
|
||
.eti-prompt {
|
||
display: flex; align-items: center; gap: 12px;
|
||
flex-wrap: wrap; justify-content: center;
|
||
max-width: 680px; width: 100%;
|
||
background: #fff; border: 1px solid var(--border); border-radius: 12px;
|
||
padding: 10px 16px; text-align: center;
|
||
}
|
||
.eti-progress {
|
||
font-family: 'Syne', sans-serif; font-weight: 800; font-size: 11px;
|
||
letter-spacing: 0.04em; text-transform: uppercase;
|
||
color: var(--teal-dark); background: var(--teal-light);
|
||
padding: 4px 10px; border-radius: 20px; white-space: nowrap;
|
||
}
|
||
.eti-question { font-size: 14.5px; font-weight: 600; color: var(--text); }
|
||
|
||
/* Scène de l'étiquette + popover */
|
||
.eti-stage { position: relative; width: 100%; max-width: 680px; display: flex; justify-content: center; }
|
||
|
||
/* Étiquette reconstituée (DÉS'INFECT — Annexe I FHER) */
|
||
.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; }
|
||
.ghs {
|
||
width: 38px; height: 38px; position: relative;
|
||
border: 2.5px solid #c62828; background: #fff;
|
||
transform: rotate(45deg);
|
||
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||
}
|
||
.ghs span { transform: rotate(-45deg); font-size: 17px; line-height: 1; }
|
||
.ghs.excl span { font-family: 'Syne', sans-serif; font-weight: 800; font-size: 24px; color: #1a1a18; }
|
||
.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; }
|
||
|
||
/* Zones cliquables (hotspots) */
|
||
.hotspot {
|
||
position: relative; cursor: pointer; border-radius: 5px;
|
||
outline: 1.5px dashed transparent; outline-offset: 1px;
|
||
transition: background 0.15s ease, outline-color 0.15s ease;
|
||
}
|
||
span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
|
||
.label-card:not(.recap) .hotspot:hover { background: rgba(43,122,120,0.10); outline-color: var(--teal-mid); }
|
||
.hotspot.hs-correct { background: rgba(46,125,50,0.14); outline: 1.5px solid #2e7d32; }
|
||
.hotspot.hs-wrong { background: rgba(198,40,40,0.16); outline: 1.5px solid #c62828; animation: hsShake 0.4s ease; }
|
||
@keyframes hsShake { 0%,100%{transform:translateX(0);} 25%{transform:translateX(-3px);} 75%{transform:translateX(3px);} }
|
||
.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;
|
||
}
|
||
|
||
/* Info-bulle réglementaire */
|
||
.eti-pop {
|
||
position: absolute; z-index: 20; max-width: 290px; width: max-content;
|
||
background: #fff; border: 1px solid var(--border); border-radius: 10px;
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.18);
|
||
padding: 12px 30px 12px 14px; font-size: 12.5px; line-height: 1.5;
|
||
text-align: left;
|
||
}
|
||
.eti-pop[hidden] { display: none; }
|
||
.eti-pop .pop-status { font-family: 'Syne', sans-serif; font-weight: 800; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; }
|
||
.eti-pop.ok .pop-status { color: #2e7d32; }
|
||
.eti-pop.ko .pop-status { color: #c62828; }
|
||
.eti-pop.info .pop-status { color: var(--teal-dark); }
|
||
.eti-pop .pop-title { font-weight: 700; color: var(--text); margin-bottom: 3px; }
|
||
.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; }
|
||
}
|
||
|
||
/* ---- SLIDE 1 — sélection formateur + garde nom ---- */
|
||
.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; }
|
||
.identity-error { color: var(--coral); font-size: 12px; margin-top: 8px; line-height: 1.4; }
|
||
.identity-error[hidden] { display: none; }
|
||
|
||
/* ---- SLIDE 7 — encadré de réponse sur le côté de l'étiquette ---- */
|
||
#eti-stage {
|
||
max-width: 980px;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) 300px;
|
||
align-items: start;
|
||
gap: 16px;
|
||
justify-content: center;
|
||
}
|
||
#eti-stage .label-card { grid-column: 1; grid-row: 1; }
|
||
#eti-stage .eti-pop,
|
||
#eti-stage .eti-side-hint { grid-column: 2; grid-row: 1; align-self: start; }
|
||
#eti-stage .eti-pop { position: relative; inset: auto; left: auto; top: auto; max-width: none; width: 100%; }
|
||
.eti-side-hint {
|
||
font-size: 12.5px; color: var(--teal-dark); line-height: 1.5;
|
||
background: var(--teal-light); border: 1px dashed var(--teal-mid);
|
||
border-radius: 10px; padding: 12px 14px;
|
||
}
|
||
.eti-side-hint[hidden] { display: none; }
|
||
|
||
/* ---- 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: 820px) {
|
||
#eti-stage { grid-template-columns: 1fr; max-width: 680px; }
|
||
#eti-stage .eti-pop,
|
||
#eti-stage .eti-side-hint { grid-column: 1; grid-row: auto; }
|
||
#eti-stage .label-card { grid-row: auto; }
|
||
}
|
||
@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>
|
||
|
||
<!-- HEADER -->
|
||
<header>
|
||
<div class="logo">Gauthier Chombart & Nathan Chauwin</div>
|
||
<nav>
|
||
<a href="../index.html">Accueil</a>
|
||
<a href="../index.html#formations">Exercices</a>
|
||
<a href="../index.html#documentation">Documentation</a>
|
||
<a href="../index.html#rapports">Rapports</a>
|
||
<a href="../index.html#about">À propos</a>
|
||
<a href="../index.html#contact">Contact</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<!-- 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>
|
||
|
||
<!-- ============ SLIDE 0 — ACCUEIL ============ -->
|
||
<div class="slide active fade-in" id="slide-0">
|
||
<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(--coral-light);bottom:60px;left:-60px;opacity:0.5;"></div>
|
||
<div style="text-align:center;">
|
||
<div class="hero-label">Certibiocide — Travaux dirigés</div>
|
||
<div class="hero-title">FDS, FT<br>& Étiquettes</div>
|
||
<p class="hero-sub">Maîtrisez les trois documents essentiels des produits biocides : fiche de sécurité, fiche technique et étiquetage réglementaire.</p>
|
||
<button class="btn btn-amber" onclick="goTo(1)">Démarrer le TD →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 1 — DÉROULÉ ============ -->
|
||
<div class="slide" id="slide-1">
|
||
<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">Déroulé du TD</div>
|
||
<div class="slide-title">Trois documents, trois usages</div>
|
||
<p class="slide-intro">Vous allez apprendre à localiser une information dans chacun des documents de référence d'un produit biocide.</p>
|
||
<div class="overview-grid">
|
||
<div class="overview-card">
|
||
<div class="overview-icon">📄</div>
|
||
<div class="overview-title">Partie 1 — FDS</div>
|
||
<div class="overview-sub">Fiche de Données de Sécurité — 16 rubriques</div>
|
||
</div>
|
||
<div class="overview-card">
|
||
<div class="overview-icon">📋</div>
|
||
<div class="overview-title">Partie 2 — FT</div>
|
||
<div class="overview-sub">Fiche Technique — mode d'emploi produit</div>
|
||
</div>
|
||
<div class="overview-card">
|
||
<div class="overview-icon">🏷️</div>
|
||
<div class="overview-title">Partie 3 — Étiquette</div>
|
||
<div class="overview-sub">Interface directe avec l'utilisateur</div>
|
||
</div>
|
||
</div>
|
||
<div class="info-card">
|
||
<span>⏱️</span>
|
||
<div>Durée ≈ 30 min. Pour chaque question, sélectionnez la <strong>zone du document</strong> où trouver l'information, puis vérifiez votre score.</div>
|
||
</div>
|
||
<div class="identity-card">
|
||
<div class="identity-title">👥 Identification du groupe</div>
|
||
<div class="identity-fields">
|
||
<input type="text" id="idGroupe" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off" oninput="clearGroupeError()">
|
||
<input type="date" id="idDate">
|
||
<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 class="identity-error" id="idGroupe-error" hidden>⚠️ Renseignez le nom du stagiaire ou du groupe pour continuer.</div>
|
||
</div>
|
||
<button class="btn btn-amber" onclick="startPart1()" style="margin-top:1.5rem;">Commencer la partie 1 →</button>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 2 — FDS INTRO ============ -->
|
||
<div class="slide" id="slide-2">
|
||
<div class="deco-circle" style="width:280px;height:280px;background:var(--amber-light);top:-40px;right:-40px;opacity:0.5;"></div>
|
||
<div class="section-tag">Partie 1 / 3 — 10 min</div>
|
||
<div class="slide-title">📄 Fiche de Données de Sécurité</div>
|
||
<p class="slide-intro">La FDS est le document de référence pour connaître les <strong>dangers</strong>, les <strong>EPI</strong>, les <strong>premiers secours</strong> et l'<strong>élimination</strong> d'un produit biocide. Elle contient 16 rubriques standardisées (règlement REACH).</p>
|
||
<div class="info-card">
|
||
<span>🎯</span>
|
||
<div>Pour chaque question, indiquez dans quelle <strong>rubrique (1 à 16)</strong> trouver la réponse — ou « Absent » si l'information n'y figure pas. Les questions sont mélangées à chaque chargement.</div>
|
||
</div>
|
||
<button class="btn btn-amber" onclick="goTo(3)" style="margin-top:1.5rem;">Répondre aux questions →</button>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 3 — FDS QUIZ (DRAG & DROP) ============ -->
|
||
<div class="slide" id="slide-3">
|
||
<div class="slide-title">Où trouver l'information ?</div>
|
||
<p class="dnd-hint">Glissez-déposez la bonne rubrique dans chaque case — ou cliquez une étiquette puis une case.</p>
|
||
<div class="dnd-layout">
|
||
<div class="dnd-bank" id="fds-bank-left"></div>
|
||
<div class="quiz-list quiz-grid" id="fds-questions"></div>
|
||
<div class="dnd-bank" id="fds-bank-right"></div>
|
||
</div>
|
||
<button class="verify-btn btn-amber" id="fds-verify" onclick="verifyFDS()" disabled>✓ Vérifier mes réponses</button>
|
||
<div id="fds-result" class="result-message"></div>
|
||
<div class="actions-row">
|
||
<button class="btn btn-outline" onclick="resetFDS()">↺ Recommencer</button>
|
||
<button class="btn btn-coral" onclick="goTo(4)">Passer à la partie 2 →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 4 — FT INTRO ============ -->
|
||
<div class="slide" id="slide-4">
|
||
<div class="deco-circle" style="width:280px;height:280px;background:var(--coral-light);top:-40px;left:-40px;opacity:0.5;"></div>
|
||
<div class="section-tag" style="background:var(--coral-light);color:var(--coral);">Partie 2 / 3 — 10 min</div>
|
||
<div class="slide-title">📋 Fiche Technique</div>
|
||
<p class="slide-intro">La FT complète la FDS : elle fournit le <strong>mode d'emploi</strong>, les <strong>utilisations</strong>, les <strong>conditionnements</strong> et les <strong>compatibilités matières</strong>. C'est le document pratique de l'utilisateur.</p>
|
||
<div class="info-card" style="background:var(--coral-light);color:var(--coral);">
|
||
<span>🎯</span>
|
||
<div>Indiquez la <strong>section</strong> de la FT où trouver chaque information : Description, Propriétés biocides, Conditionnements, Utilisations — ou « Absent ».</div>
|
||
</div>
|
||
<button class="btn btn-coral" onclick="goTo(5)" style="margin-top:1.5rem;">Répondre aux questions →</button>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 5 — FT : RELIER ============ -->
|
||
<div class="slide" id="slide-5">
|
||
<div class="scroll-area">
|
||
<div class="slide-title">Sections de la fiche technique</div>
|
||
<p class="match-hint">Reliez chaque question à la bonne section : cliquez sur un point à gauche, maintenez, puis glissez jusqu'au point de la section correspondante à droite.</p>
|
||
<div class="match-wrap" id="ft-match">
|
||
<svg class="match-svg" id="ft-svg"></svg>
|
||
<div class="match-col match-left" id="ft-left"></div>
|
||
<div class="match-col match-right" id="ft-right"></div>
|
||
</div>
|
||
<button class="verify-btn btn-coral" id="ft-verify" onclick="verifyFT()" disabled>✓ Vérifier mes réponses</button>
|
||
<div id="ft-result" class="result-message"></div>
|
||
<div class="actions-row">
|
||
<button class="btn btn-outline" onclick="resetFT()">↺ Recommencer</button>
|
||
<button class="btn btn-teal" onclick="goTo(6)">Passer à la partie 3 →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 6 — ÉTIQUETTE INTRO ============ -->
|
||
<div class="slide" id="slide-6">
|
||
<div class="deco-circle" style="width:280px;height:280px;background:var(--teal-light);bottom:-40px;right:-40px;opacity:0.6;"></div>
|
||
<div class="section-tag" style="background:var(--teal-light);color:var(--teal-dark);">Partie 3 / 3 — 10 min</div>
|
||
<div class="slide-title">🏷️ Étiquetage</div>
|
||
<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>
|
||
<button class="btn btn-teal" onclick="goTo(7)" style="margin-top:1.5rem;">Répondre aux questions →</button>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 7 — ÉTIQUETTE CLIQUABLE (HOTSPOTS) ============ -->
|
||
<div class="slide" id="slide-7">
|
||
<div class="scroll-area">
|
||
|
||
<!-- ---- PANNEAU A : exercice ---- -->
|
||
<div id="eti-exercise" style="width:100%;display:flex;flex-direction:column;align-items:center;gap:0.75rem;">
|
||
<div class="slide-title">Zones de l'étiquette</div>
|
||
|
||
<div class="situation-box">
|
||
<span class="sit-ico">💬</span>
|
||
<div><strong>Mise en situation —</strong> Vous intervenez chez un client avec un désinfectant de surfaces <strong>à usage exclusivement professionnel</strong>. Avant l'application, il vous interroge sur les dangers et les précautions du produit. Bidon en main, retrouvez rapidement sur l'étiquette chaque information demandée.</div>
|
||
</div>
|
||
|
||
<div class="eti-prompt">
|
||
<span class="eti-progress" id="eti-progress">Question 1 / 9</span>
|
||
<span class="eti-question" id="eti-question">—</span>
|
||
</div>
|
||
|
||
<div class="eti-stage" id="eti-stage">
|
||
<div class="label-card" id="labelCard">
|
||
<!-- Titre / nom commercial -->
|
||
<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">
|
||
<!-- Colonne gauche (contexte, non cliquable) -->
|
||
<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>
|
||
|
||
<!-- Colonne droite (précautions, cliquable) -->
|
||
<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>
|
||
|
||
<!-- Bas : contenu net, code-barres, pictogrammes, mention -->
|
||
<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">
|
||
<span class="ghs"><span>🐟</span></span>
|
||
<span class="ghs"><span>🧪</span></span>
|
||
<span class="ghs excl"><span>!</span></span>
|
||
</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>
|
||
|
||
<div class="eti-side-hint" id="eti-side-hint">👉 Cliquez une zone de l'étiquette : sa justification réglementaire s'affiche ici, à côté du bidon.</div>
|
||
|
||
<div class="eti-pop" id="eti-pop" hidden>
|
||
<button class="pop-close" onclick="closeEtiPop()" aria-label="Fermer">×</button>
|
||
<div class="pop-status" id="pop-status"></div>
|
||
<div class="pop-title" id="pop-title"></div>
|
||
<div class="pop-reg" id="pop-reg"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="etiquette-result" class="result-message"></div>
|
||
<div class="actions-row">
|
||
<button class="btn btn-outline" onclick="resetEtiquette()">↺ Recommencer</button>
|
||
<button class="btn btn-teal" id="eti-recap-btn" onclick="showEtiRecap()" disabled>Voir le récapitulatif →</button>
|
||
</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 class="actions-row">
|
||
<button class="btn btn-outline" onclick="hideEtiRecap()">← Retour à l'exercice</button>
|
||
<button class="btn btn-amber" onclick="goTo(8)">Voir la synthèse générale →</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ SLIDE 8 — SYNTHÈSE / FIN ============ -->
|
||
<div class="slide" id="slide-8">
|
||
<div class="scroll-area">
|
||
<div class="deco-circle" style="width:320px;height:320px;background:var(--amber-light);bottom:-80px;right:-60px;opacity:0.5;"></div>
|
||
<div style="text-align:center;">
|
||
<div style="font-size:44px;margin-bottom:0.5rem;">🎉</div>
|
||
<div class="section-tag">TD terminé</div>
|
||
<div class="slide-title" style="margin-top:0.4rem;">Synthèse comparative</div>
|
||
</div>
|
||
<table class="comparison-table">
|
||
<thead>
|
||
<tr><th>Document</th><th>Utilité principale</th><th>Consultation</th><th>Public</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><strong>FDS</strong></td><td>Sécurité, dangers, EPI, premiers secours</td><td>AVANT manipulation</td><td>Professionnels</td></tr>
|
||
<tr><td><strong>FT</strong></td><td>Mode d'emploi, applications, conditionnements</td><td>Pour l'utilisation</td><td>Utilisateurs finaux</td></tr>
|
||
<tr><td><strong>Étiquette</strong></td><td>Dangers essentiels, interface rapide</td><td>À chaque prise du produit</td><td>Tous les utilisateurs</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<ul class="tips-list">
|
||
<li><strong>FDS = Sécurité et dangers</strong> → à consulter AVANT manipulation.</li>
|
||
<li><strong>FT = Mode d'emploi et applications</strong> → à consulter pour l'utilisation.</li>
|
||
<li><strong>Étiquette = Interface rapide</strong> → lecture obligatoire avant ouverture du produit.</li>
|
||
<li>Les trois documents sont <strong>complémentaires</strong>, jamais redondants — chacun cible un besoin et un public différents.</li>
|
||
</ul>
|
||
<div class="actions-row">
|
||
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
|
||
<a href="correction-fds.html" class="btn btn-outline">✔ Corriger un rendu</a>
|
||
</div>
|
||
<div class="actions-row" style="margin-top:0.5rem;">
|
||
<button class="btn btn-outline" onclick="restartAll()">↺ Recommencer</button>
|
||
<a href="../index.html#formations" class="btn btn-outline">Retour aux exercices</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let currentSlide = 0;
|
||
let history = [];
|
||
const totalSlides = 8;
|
||
|
||
/* ======================== QUESTIONS ======================== */
|
||
const fdsQuestions = [
|
||
{ id: 1, text: "Quel est le nom du produit et qui est le fabricant ?", answer: "1" },
|
||
{ id: 2, text: "Quels sont les dangers du produit ?", answer: "2" },
|
||
{ id: 3, text: "Quelles sont les molécules actives et leurs concentrations ?", answer: "3" },
|
||
{ id: 4, text: "Quels gestes adopter en premiers secours ?", answer: "4" },
|
||
{ id: 5, text: "En cas d'incendie, quels sont les moyens d'extinction conseillés ?", answer: "5" },
|
||
{ id: 6, text: "Que faire en cas de dispersion accidentelle ?", answer: "6" },
|
||
{ id: 7, text: "Dans quelles conditions stocker mon produit ?", answer: "7" },
|
||
{ id: 8, text: "Quels équipements de protection individuelle (EPI) dois-je porter ?", answer: "8" },
|
||
{ id: 9, text: "Quel est le pH de ce produit ?", answer: "9" },
|
||
{ id: 10, text: "Le produit est-il stable dans des conditions normales ?", answer: "10" },
|
||
{ id: 11, text: "Quels sont les effets potentiels sur la santé ?", answer: "11" },
|
||
{ id: 12, text: "Quel est le degré de toxicité pour les poissons ? Potentiel de bioaccumulation ?", answer: "12" },
|
||
{ id: 13, text: "Comment éliminer le produit ?", answer: "13" },
|
||
{ id: 14, text: "Est-ce que mon produit est classé dangereux pour le transport ?", answer: "14" },
|
||
{ id: 15, text: "En cas d'ingestion du produit, que faire ?", answer: "4,11" },
|
||
{ id: 16, text: "Où trouver le mode d'emploi du produit ?", answer: "absent" }
|
||
];
|
||
|
||
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: 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" }
|
||
];
|
||
const ftOptions = ["Description", "Propriétés biocides", "Conditionnements", "Utilisations"];
|
||
|
||
/* 9 zones réglementaires de l'étiquette (ordre canonique = check-list FHER p.15).
|
||
zone : identifiant du hotspot correspondant dans l'étiquette
|
||
text : consigne affichée pendant l'exercice
|
||
label : intitulé check-list (réutilisé dans la synthèse et le PDF)
|
||
reg : justification réglementaire (info-bulle) — Guide FHER, Partie 3, p.10-12 */
|
||
const etiZones = [
|
||
{ zone: "z-nom", text: "Retrouvez le nom commercial du produit.",
|
||
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", text: "Cliquez sur les pictogrammes de danger.",
|
||
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", text: "Où figure la mention d'avertissement ?",
|
||
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", text: "Repérez les mentions de danger (phrases 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", text: "Où sont indiqués les EPI à porter (phrases P) ?",
|
||
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", text: "Où trouver les instructions de premiers 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", text: "Où sont les consignes d'élimination de l'emballage ?",
|
||
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", text: "Retrouvez le contenu net du produit.",
|
||
label: "Contenu net",
|
||
reg: "Quantité nette de produit contenue dans l'emballage — mention générale du pavé d'information produit." },
|
||
{ zone: "z-antipoison", text: "Où figurent les coordonnées du centre 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)." }
|
||
];
|
||
function etiZoneById(id) { return etiZones.find(z => z.zone === id); }
|
||
|
||
function shuffleArray(array) {
|
||
const a = [...array];
|
||
for (let i = a.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[a[i], a[j]] = [a[j], a[i]];
|
||
}
|
||
return a;
|
||
}
|
||
|
||
function generateQuizzes() {
|
||
// FDS — glisser-déposer : banques d'étiquettes + cases de dépôt
|
||
buildFdsDnd();
|
||
|
||
// FT — exercice « relier » (questions à gauche, sections à droite)
|
||
buildFtMatch();
|
||
|
||
// Étiquette — exercice à zones cliquables (hotspots)
|
||
buildEtiquette();
|
||
}
|
||
|
||
/* ---- Étiquette : zones cliquables ---- */
|
||
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
|
||
|
||
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'));
|
||
|
||
closeEtiPop();
|
||
const r = document.getElementById('etiquette-result');
|
||
r.className = 'result-message'; r.innerHTML = '';
|
||
document.getElementById('eti-recap-btn').disabled = true;
|
||
document.getElementById('eti-exercise').style.display = 'flex';
|
||
document.getElementById('eti-recap').style.display = 'none';
|
||
|
||
if (!etiInit) {
|
||
card.addEventListener('click', onEtiClick);
|
||
etiInit = true;
|
||
}
|
||
renderEtiQuestion();
|
||
}
|
||
|
||
function renderEtiQuestion() {
|
||
const prog = document.getElementById('eti-progress');
|
||
const ques = document.getElementById('eti-question');
|
||
if (etiPos >= etiOrder.length) {
|
||
prog.textContent = 'Terminé';
|
||
ques.textContent = '✓ Les 9 zones ont été identifiées.';
|
||
return;
|
||
}
|
||
const z = etiZones[etiOrder[etiPos]];
|
||
prog.textContent = 'Question ' + (etiPos + 1) + ' / ' + etiZones.length;
|
||
ques.textContent = z.text;
|
||
}
|
||
|
||
function onEtiClick(e) {
|
||
const hs = e.target.closest('.hotspot');
|
||
if (!hs) return;
|
||
const clicked = hs.dataset.zone;
|
||
|
||
// Exploration libre une fois l'exercice terminé
|
||
if (etiPos >= etiOrder.length) {
|
||
openEtiPop(hs, 'info', clicked);
|
||
return;
|
||
}
|
||
|
||
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;
|
||
openEtiPop(hs, 'ok', clicked);
|
||
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);
|
||
openEtiPop(hs, 'ko', clicked);
|
||
}
|
||
}
|
||
|
||
function finishEtiquette() {
|
||
const correct = etiZones.filter(z => etiFirstTry[z.zone]).length;
|
||
showScore(document.getElementById('etiquette-result'), correct, etiZones.length);
|
||
document.getElementById('eti-recap-btn').disabled = false;
|
||
}
|
||
|
||
/* Info-bulle réglementaire positionnée près de la zone cliquée */
|
||
function openEtiPop(hs, kind, clickedZone) {
|
||
const pop = document.getElementById('eti-pop');
|
||
const z = etiZoneById(clickedZone);
|
||
|
||
const statusEl = document.getElementById('pop-status');
|
||
pop.className = 'eti-pop ' + kind;
|
||
if (kind === 'ok') statusEl.innerHTML = '✅ Bonne zone';
|
||
else if (kind === 'ko') statusEl.innerHTML = '❌ Ce n\'est pas la bonne zone';
|
||
else statusEl.innerHTML = '🔎 ' + z.label;
|
||
|
||
document.getElementById('pop-title').textContent = z.label;
|
||
document.getElementById('pop-reg').textContent = z.reg;
|
||
|
||
// L'encadré s'affiche dans la colonne latérale, à côté de l'étiquette (plus de superposition).
|
||
const hint = document.getElementById('eti-side-hint');
|
||
if (hint) hint.hidden = true;
|
||
pop.hidden = false;
|
||
}
|
||
|
||
function closeEtiPop() {
|
||
const pop = document.getElementById('eti-pop');
|
||
if (pop) pop.hidden = true;
|
||
const hint = document.getElementById('eti-side-hint');
|
||
if (hint) hint.hidden = false;
|
||
}
|
||
|
||
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';
|
||
|
||
// 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';
|
||
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
|
||
let ftVerified = false;
|
||
const SVGNS = 'http://www.w3.org/2000/svg';
|
||
const FT_COLORS = { line: '#D85A30', temp: '#993C1D', ok: '#2e7d32', ko: '#c62828' };
|
||
|
||
function qById(id) { return ftQuestions.find(q => String(q.id) === String(id)); }
|
||
|
||
function buildFtMatch() {
|
||
Object.keys(ftConnections).forEach(k => delete ftConnections[k]);
|
||
ftDrag = null; ftVerified = false;
|
||
|
||
const leftC = document.getElementById('ft-left');
|
||
const rightC = document.getElementById('ft-right');
|
||
leftC.innerHTML = '';
|
||
rightC.innerHTML = '';
|
||
|
||
ftQuestions.forEach(q => {
|
||
const item = document.createElement('div');
|
||
item.className = 'match-item';
|
||
item.dataset.qid = q.id;
|
||
item.innerHTML = `<span>${q.text}</span><div class="match-dot" data-side="left" data-qid="${q.id}"></div>`;
|
||
leftC.appendChild(item);
|
||
});
|
||
|
||
const sections = shuffleArray([...new Set(ftQuestions.map(q => q.answer))]);
|
||
sections.forEach(sec => {
|
||
const item = document.createElement('div');
|
||
item.className = 'match-item';
|
||
item.dataset.answer = sec;
|
||
item.innerHTML = `<div class="match-dot" data-side="right" data-answer="${sec}"></div><span>${sec}</span>`;
|
||
rightC.appendChild(item);
|
||
});
|
||
|
||
initFtMatch();
|
||
document.getElementById('ft-svg').innerHTML = '';
|
||
document.getElementById('ft-verify').disabled = true;
|
||
}
|
||
|
||
function resetFT() {
|
||
const r = document.getElementById('ft-result');
|
||
r.className = 'result-message'; r.innerHTML = '';
|
||
buildFtMatch();
|
||
}
|
||
|
||
function ftSvgRect() { return document.getElementById('ft-svg').getBoundingClientRect(); }
|
||
function ftPointerPos(e) { const r = ftSvgRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; }
|
||
function ftDotCenter(dot) {
|
||
const r = dot.getBoundingClientRect(); const s = ftSvgRect();
|
||
return { x: r.left + r.width / 2 - s.left, y: r.top + r.height / 2 - s.top };
|
||
}
|
||
|
||
function ftDrawLine(svg, a, b, color, dashed) {
|
||
const line = document.createElementNS(SVGNS, 'line');
|
||
line.setAttribute('x1', a.x); line.setAttribute('y1', a.y);
|
||
line.setAttribute('x2', b.x); line.setAttribute('y2', b.y);
|
||
line.setAttribute('stroke', color);
|
||
line.setAttribute('stroke-width', '3');
|
||
line.setAttribute('stroke-linecap', 'round');
|
||
if (dashed) line.setAttribute('stroke-dasharray', '6 5');
|
||
svg.appendChild(line);
|
||
}
|
||
|
||
function drawFtLines(tempEnd) {
|
||
const svg = document.getElementById('ft-svg');
|
||
if (!svg) return;
|
||
svg.innerHTML = '';
|
||
Object.entries(ftConnections).forEach(([qid, ans]) => {
|
||
const from = document.querySelector('.match-dot[data-side="left"][data-qid="' + qid + '"]');
|
||
const to = document.querySelector('.match-dot[data-side="right"][data-answer="' + CSS.escape(ans) + '"]');
|
||
if (!from || !to) return;
|
||
const color = ftVerified ? (ans === qById(qid).answer ? FT_COLORS.ok : FT_COLORS.ko) : FT_COLORS.line;
|
||
ftDrawLine(svg, ftDotCenter(from), ftDotCenter(to), color);
|
||
});
|
||
if (ftDrag && tempEnd) {
|
||
const from = document.querySelector('.match-dot[data-side="left"][data-qid="' + ftDrag.qid + '"]');
|
||
if (from) ftDrawLine(svg, ftDotCenter(from), tempEnd, FT_COLORS.temp, true);
|
||
}
|
||
}
|
||
|
||
function refreshFtState() {
|
||
ftQuestions.forEach(q => {
|
||
const dot = document.querySelector('.match-dot[data-side="left"][data-qid="' + q.id + '"]');
|
||
if (!dot) return;
|
||
const has = ftConnections[q.id] != null;
|
||
dot.classList.toggle('filled', has);
|
||
dot.closest('.match-item').classList.toggle('connected', has);
|
||
});
|
||
document.querySelectorAll('.match-dot[data-side="right"]').forEach(dot => {
|
||
const used = Object.values(ftConnections).includes(dot.dataset.answer);
|
||
dot.classList.toggle('filled', used);
|
||
dot.closest('.match-item').classList.toggle('connected', used);
|
||
});
|
||
const all = ftQuestions.every(q => ftConnections[q.id] != null);
|
||
document.getElementById('ft-verify').disabled = !all;
|
||
}
|
||
|
||
function initFtMatch() {
|
||
const wrap = document.getElementById('ft-match');
|
||
wrap.querySelectorAll('.match-dot[data-side="left"]').forEach(dot => {
|
||
dot.addEventListener('pointerdown', e => {
|
||
e.preventDefault();
|
||
const qid = dot.dataset.qid;
|
||
delete ftConnections[qid]; // repartir d'une question efface son lien
|
||
ftVerified = false;
|
||
document.getElementById('ft-result').className = 'result-message';
|
||
ftDrag = { qid };
|
||
dot.classList.add('active');
|
||
refreshFtState();
|
||
drawFtLines(ftPointerPos(e));
|
||
});
|
||
});
|
||
}
|
||
|
||
// Gestionnaires globaux (le glissement peut sortir de la zone)
|
||
document.addEventListener('pointermove', e => {
|
||
if (!ftDrag) return;
|
||
drawFtLines(ftPointerPos(e));
|
||
});
|
||
document.addEventListener('pointerup', e => {
|
||
if (!ftDrag) return;
|
||
const target = document.elementFromPoint(e.clientX, e.clientY);
|
||
const answerDot = target && target.closest('.match-dot[data-side="right"]');
|
||
if (answerDot) ftConnections[ftDrag.qid] = answerDot.dataset.answer;
|
||
document.querySelectorAll('.match-dot.active').forEach(d => d.classList.remove('active'));
|
||
ftDrag = null;
|
||
refreshFtState();
|
||
drawFtLines();
|
||
});
|
||
window.addEventListener('resize', () => { if (currentSlide === 5) drawFtLines(); });
|
||
|
||
/* ---- FDS : glisser-déposer ---- */
|
||
function buildFdsDnd() {
|
||
const tile = (v, l) => `<div class="dnd-tile" draggable="true" data-value="${v}" data-label="${l}">${l}</div>`;
|
||
let left = '', right = '';
|
||
for (let i = 1; i <= 8; i++) left += tile(String(i), 'Rubrique ' + i);
|
||
for (let i = 9; i <= 16; i++) right += tile(String(i), 'Rubrique ' + i);
|
||
right += tile('absent', 'Absent');
|
||
document.getElementById('fds-bank-left').innerHTML = left;
|
||
document.getElementById('fds-bank-right').innerHTML = right;
|
||
|
||
const fdsC = document.getElementById('fds-questions');
|
||
fdsC.innerHTML = '';
|
||
shuffleArray(fdsQuestions).forEach((q, i) => {
|
||
fdsC.innerHTML += `
|
||
<div class="answer-row">
|
||
<div class="question-num" style="background:var(--amber-light);color:var(--amber-dark);">Q${i + 1}</div>
|
||
<div class="question-text">${q.text}</div>
|
||
<div class="drop-slot" id="fds${q.id}" data-answer="${q.answer}" data-value=""></div>
|
||
</div>`;
|
||
});
|
||
|
||
initFdsDnd();
|
||
}
|
||
|
||
function resetFDS() {
|
||
const r = document.getElementById('fds-result');
|
||
r.className = 'result-message'; r.innerHTML = '';
|
||
buildFdsDnd();
|
||
}
|
||
|
||
function initFdsDnd() {
|
||
const slide = document.getElementById('slide-3');
|
||
const tiles = slide.querySelectorAll('.dnd-tile');
|
||
const slots = slide.querySelectorAll('.drop-slot');
|
||
let armed = null;
|
||
|
||
const resetResults = () => {
|
||
slide.querySelectorAll('.answer-label, .answer-icon').forEach(e => e.remove());
|
||
slide.querySelectorAll('.drop-slot').forEach(s => s.classList.remove('slot-correct', 'slot-wrong'));
|
||
const r = document.getElementById('fds-result');
|
||
r.className = 'result-message'; r.innerHTML = '';
|
||
};
|
||
const fill = (slot, v, l) => {
|
||
slot.dataset.value = v; slot.textContent = l; slot.classList.add('filled');
|
||
resetResults(); updateFdsGate();
|
||
};
|
||
const clear = (slot) => {
|
||
slot.dataset.value = ''; slot.textContent = ''; slot.classList.remove('filled');
|
||
resetResults(); updateFdsGate();
|
||
};
|
||
|
||
tiles.forEach(t => {
|
||
t.addEventListener('dragstart', e => {
|
||
e.dataTransfer.setData('text/plain', JSON.stringify({ v: t.dataset.value, l: t.dataset.label }));
|
||
t.classList.add('dragging');
|
||
});
|
||
t.addEventListener('dragend', () => t.classList.remove('dragging'));
|
||
t.addEventListener('click', () => {
|
||
if (armed === t) { t.classList.remove('armed'); armed = null; return; }
|
||
if (armed) armed.classList.remove('armed');
|
||
armed = t; t.classList.add('armed');
|
||
});
|
||
});
|
||
|
||
slots.forEach(s => {
|
||
s.addEventListener('dragover', e => { e.preventDefault(); s.classList.add('drag-over'); });
|
||
s.addEventListener('dragleave', () => s.classList.remove('drag-over'));
|
||
s.addEventListener('drop', e => {
|
||
e.preventDefault(); s.classList.remove('drag-over');
|
||
try { const d = JSON.parse(e.dataTransfer.getData('text/plain')); fill(s, d.v, d.l); } catch (_) {}
|
||
});
|
||
s.addEventListener('click', () => {
|
||
if (armed) { fill(s, armed.dataset.value, armed.dataset.label); armed.classList.remove('armed'); armed = null; }
|
||
else if (s.classList.contains('filled')) { clear(s); }
|
||
});
|
||
});
|
||
|
||
updateFdsGate();
|
||
}
|
||
|
||
function updateFdsGate() {
|
||
const slots = document.querySelectorAll('#slide-3 .drop-slot');
|
||
const all = Array.from(slots).every(s => (s.dataset.value || '') !== '');
|
||
document.getElementById('fds-verify').disabled = !all;
|
||
}
|
||
|
||
function wireVerifyGate(containerId, btnId) {
|
||
const container = document.getElementById(containerId);
|
||
const btn = document.getElementById(btnId);
|
||
if (!container || !btn) return;
|
||
const selects = Array.from(container.querySelectorAll('select'));
|
||
const update = () => { btn.disabled = !selects.every(s => s.value !== ''); };
|
||
selects.forEach(s => s.addEventListener('change', update));
|
||
update();
|
||
}
|
||
|
||
/* ======================== VÉRIFICATION ======================== */
|
||
function showScore(resultDiv, correct, total) {
|
||
const p = Math.round((correct / total) * 100);
|
||
if (p === 100) {
|
||
resultDiv.className = 'result-message success show';
|
||
resultDiv.innerHTML = `<strong>🎉 Excellent !</strong> ${correct}/${total} bonnes réponses (${p}%)`;
|
||
} else if (p >= 80) {
|
||
resultDiv.className = 'result-message success show';
|
||
resultDiv.innerHTML = `<strong>✓ Très bien !</strong> ${correct}/${total} bonnes réponses (${p}%)`;
|
||
} else if (p >= 60) {
|
||
resultDiv.className = 'result-message error show';
|
||
resultDiv.innerHTML = `<strong>⚠️ À revoir</strong> — ${correct}/${total} bonnes réponses (${p}%)`;
|
||
} else {
|
||
resultDiv.className = 'result-message error show';
|
||
resultDiv.innerHTML = `<strong>❌ Insuffisant</strong> — ${correct}/${total} bonnes réponses (${p}%)`;
|
||
}
|
||
}
|
||
|
||
function verifyFDS() {
|
||
let correct = 0;
|
||
fdsQuestions.forEach(q => {
|
||
const slot = document.getElementById('fds' + q.id);
|
||
const userAnswer = slot.dataset.value || '';
|
||
const expected = q.answer.split(',');
|
||
const isCorrect = expected.includes(userAnswer);
|
||
const labelText = expected.length > 1
|
||
? 'Rubriques ' + expected.join(' et ')
|
||
: (expected[0] === 'absent' ? 'Absent' : 'Rubrique ' + expected[0]);
|
||
if (isCorrect) correct++;
|
||
showSlotResult(slot, isCorrect, userAnswer === '', labelText);
|
||
});
|
||
showScore(document.getElementById('fds-result'), correct, fdsQuestions.length);
|
||
}
|
||
|
||
function showSlotResult(slot, isCorrect, isUnanswered, correctAnswerText) {
|
||
const row = slot.closest('.answer-row');
|
||
row.querySelector('.answer-label')?.remove();
|
||
row.querySelector('.answer-icon')?.remove();
|
||
slot.classList.remove('slot-correct', 'slot-wrong');
|
||
|
||
const label = document.createElement('div');
|
||
label.className = 'answer-label';
|
||
label.textContent = correctAnswerText;
|
||
slot.before(label);
|
||
|
||
const icon = document.createElement('div');
|
||
icon.className = 'answer-icon';
|
||
icon.textContent = isUnanswered ? '❓' : (isCorrect ? '✅' : '❌');
|
||
slot.before(icon);
|
||
|
||
if (!isUnanswered) slot.classList.add(isCorrect ? 'slot-correct' : 'slot-wrong');
|
||
}
|
||
|
||
function verifyFT() {
|
||
ftVerified = true;
|
||
let correct = 0;
|
||
ftQuestions.forEach(q => {
|
||
if (ftConnections[q.id] === q.answer) correct++;
|
||
});
|
||
drawFtLines();
|
||
showScore(document.getElementById('ft-result'), correct, ftQuestions.length);
|
||
}
|
||
|
||
/* ======================== EXPORT PDF ======================== */
|
||
function escapeHtml(str) {
|
||
if (!str) return '';
|
||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||
}
|
||
function idVal(id) {
|
||
const el = document.getElementById(id);
|
||
return el ? el.value.trim() : '';
|
||
}
|
||
|
||
// Libellé lisible d'une réponse FDS (numéro de rubrique) et de l'attendu
|
||
function fdsAnswerLabel(v) {
|
||
if (!v) return '(non répondu)';
|
||
if (v === 'absent') return 'Absent';
|
||
return 'Rubrique ' + v;
|
||
}
|
||
function fdsExpectedLabel(answer) {
|
||
const parts = answer.split(',');
|
||
if (parts.length > 1) return 'Rubrique ' + parts.join(' ou ');
|
||
return parts[0] === 'absent' ? 'Absent' : 'Rubrique ' + parts[0];
|
||
}
|
||
function textAnswerLabel(v) {
|
||
if (!v) return '(non répondu)';
|
||
return v === 'absent' ? 'Absent' : v;
|
||
}
|
||
|
||
function isFdsCorrect(q, v) { return v !== '' && q.answer.split(',').includes(v); }
|
||
function isTextCorrect(q, v) { return v !== '' && v === q.answer; }
|
||
|
||
function exporterPDF() {
|
||
const groupe = idVal('idGroupe');
|
||
const date = idVal('idDate') || new Date().toISOString().slice(0, 10);
|
||
const formateur = idVal('idFormateur');
|
||
|
||
let scoreTotal = 0, nbTotal = 0;
|
||
|
||
function partHtml(prefix, title, questions, selPrefix, answerLabelFn, expectedLabelFn, correctFn, valueFn) {
|
||
const rows = questions.map((q, i) => {
|
||
let v;
|
||
if (valueFn) { v = valueFn(q); }
|
||
else { const sel = document.getElementById(selPrefix + q.id); v = sel ? sel.value : ''; }
|
||
const ok = correctFn(q, v);
|
||
nbTotal++; if (ok) scoreTotal++;
|
||
const mark = v === '' ? '❓' : (ok ? '✓' : '✗');
|
||
return `<div style="margin-bottom:10px;">
|
||
<div><strong>[${prefix}-Q${i + 1}]</strong> ${escapeHtml(q.text)}</div>
|
||
<div style="margin-top:2px;">Réponse du groupe : <strong>${escapeHtml(answerLabelFn(v))}</strong> — Attendu : ${escapeHtml(expectedLabelFn(q.answer))} — ${mark}</div>
|
||
</div>`;
|
||
}).join('');
|
||
return `<h2>${title}</h2>${rows}`;
|
||
}
|
||
|
||
const p1 = partHtml('P1', 'Partie 1 — FDS (Fiche de Données de Sécurité)', fdsQuestions, 'fds', fdsAnswerLabel, fdsExpectedLabel, isFdsCorrect);
|
||
const p2 = partHtml('P2', 'Partie 2 — FT (Fiche Technique)', ftQuestions, 'ft', textAnswerLabel, a => a, isTextCorrect, q => ftConnections[q.id] || '');
|
||
|
||
// Partie 3 — Étiquette (zones cliquables) : ordre check-list
|
||
const p3rows = etiZones.map((z, i) => {
|
||
const given = etiAnswers[z.zone];
|
||
const ok = given === z.zone;
|
||
nbTotal++; if (ok) scoreTotal++;
|
||
const givenLabel = given === undefined ? '(non répondu)' : (etiZoneById(given) ? etiZoneById(given).label : given);
|
||
const mark = given === undefined ? '❓' : (ok ? '✓' : '✗');
|
||
return `<div style="margin-bottom:10px;">
|
||
<div><strong>[P3-Q${i + 1}]</strong> ${escapeHtml(z.text)}</div>
|
||
<div style="margin-top:2px;">Zone désignée : <strong>${escapeHtml(givenLabel)}</strong> — Attendu : ${escapeHtml(z.label)} — ${mark}</div>
|
||
</div>`;
|
||
}).join('');
|
||
const p3 = `<h2>Partie 3 — Étiquette (zones réglementaires)</h2>${p3rows}`;
|
||
|
||
const w = window.open('', '_blank');
|
||
w.document.write(`
|
||
<html><head><meta charset="utf-8"><title>FDS, FT & Étiquettes — Réponses (${escapeHtml(groupe) || 'sans nom'})</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; padding: 30px; color: #1a1a18; }
|
||
h1 { font-size: 20px; border-bottom: 2px solid #BA7517; padding-bottom: 8px; }
|
||
h2 { font-size: 15px; margin-top: 26px; color: #633806; }
|
||
.meta { font-size: 13px; color: #555; margin: 2px 0; }
|
||
</style></head>
|
||
<body>
|
||
<h1>TD — FDS, FT & Étiquettes</h1>
|
||
<p class="meta"><strong>Groupe / Stagiaire :</strong> ${escapeHtml(groupe) || '—'}</p>
|
||
<p class="meta"><strong>Date de session :</strong> ${escapeHtml(date)}</p>
|
||
<p class="meta"><strong>Formateur :</strong> ${escapeHtml(formateur) || '—'}</p>
|
||
<p class="meta"><strong>Score global :</strong> ${scoreTotal} / ${nbTotal}</p>
|
||
${p1}${p2}${p3}
|
||
</body></html>`);
|
||
w.document.close();
|
||
setTimeout(() => w.print(), 300);
|
||
}
|
||
|
||
/* ======================== SLIDE 1 — IDENTIFICATION ======================== */
|
||
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 clearGroupeError() {
|
||
const g = document.getElementById('idGroupe');
|
||
g.style.borderColor = '';
|
||
document.getElementById('idGroupe-error').hidden = true;
|
||
}
|
||
function startPart1() {
|
||
const g = document.getElementById('idGroupe');
|
||
if (!g.value.trim()) {
|
||
g.style.borderColor = 'var(--coral-mid)';
|
||
document.getElementById('idGroupe-error').hidden = false;
|
||
g.focus();
|
||
return;
|
||
}
|
||
goTo(2);
|
||
}
|
||
|
||
/* ======================== NAVIGATION ======================== */
|
||
function goTo(n) {
|
||
const cur = document.getElementById('slide-' + currentSlide);
|
||
const next = document.getElementById('slide-' + n);
|
||
if (!next) return;
|
||
history.push(currentSlide);
|
||
cur.classList.remove('active', 'fade-in');
|
||
cur.style.display = 'none';
|
||
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;
|
||
updateUI();
|
||
}
|
||
|
||
function goBack() {
|
||
if (history.length === 0) return;
|
||
const prev = history.pop();
|
||
const cur = document.getElementById('slide-' + currentSlide);
|
||
const target = document.getElementById('slide-' + prev);
|
||
cur.classList.remove('active', 'fade-in');
|
||
cur.style.display = 'none';
|
||
currentSlide = prev;
|
||
target.style.display = 'flex';
|
||
setTimeout(() => target.classList.add('active', 'fade-in'), 10);
|
||
updateUI();
|
||
}
|
||
|
||
function updateUI() {
|
||
document.getElementById('progressFill').style.width =
|
||
currentSlide === 0 ? '0%' : ((currentSlide / totalSlides) * 100) + '%';
|
||
document.getElementById('slideCounter').textContent =
|
||
currentSlide === 0 ? '' : currentSlide + ' / ' + totalSlides;
|
||
document.getElementById('bottomBar').style.display = currentSlide === 0 ? 'none' : 'flex';
|
||
document.getElementById('backBtn').style.display = history.length > 0 ? 'block' : 'none';
|
||
|
||
const badge = document.getElementById('phaseBadge');
|
||
const set = (bg, col, txt) => { badge.style.display = 'block'; badge.style.background = bg; badge.style.color = col; badge.textContent = txt; };
|
||
if (currentSlide === 2 || currentSlide === 3) set('var(--amber-light)', 'var(--amber-dark)', 'Partie 1 — FDS');
|
||
else if (currentSlide === 4 || currentSlide === 5) set('var(--coral-light)', 'var(--coral)', 'Partie 2 — FT');
|
||
else if (currentSlide === 6 || currentSlide === 7) set('var(--teal-light)', 'var(--teal-dark)', 'Partie 3 — Étiquette');
|
||
else badge.style.display = 'none';
|
||
}
|
||
|
||
function restartAll() {
|
||
history = [];
|
||
document.querySelectorAll('.slide').forEach(s => { s.classList.remove('active', 'fade-in'); s.style.display = 'none'; });
|
||
document.querySelectorAll('.result-message').forEach(r => { r.className = 'result-message'; });
|
||
generateQuizzes();
|
||
currentSlide = 0;
|
||
const first = document.getElementById('slide-0');
|
||
first.style.display = 'flex';
|
||
setTimeout(() => first.classList.add('active', 'fade-in'), 10);
|
||
updateUI();
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
generateQuizzes();
|
||
updateUI();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|