Enoder commited on
Commit
6e4ecb0
ยท
verified ยท
1 Parent(s): b77dc8b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1237 -0
app.py ADDED
@@ -0,0 +1,1237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GรฉoRรฉvision โ€” Tectonique des Plaques</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&family=Syne:wght@400;700;800&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --bg: #0a0c14;
11
+ --surface: #111420;
12
+ --surface2: #181d2e;
13
+ --accent: #e8440a;
14
+ --accent2: #3a7bd5;
15
+ --accent3: #22c97a;
16
+ --gold: #f5c842;
17
+ --text: #e8e6e0;
18
+ --muted: #6b7090;
19
+ --border: #252a3d;
20
+ --ch3: #e8440a;
21
+ --ch4: #3a7bd5;
22
+ --ch5: #22c97a;
23
+ --synth: #f5c842;
24
+ }
25
+
26
+ * { margin: 0; padding: 0; box-sizing: border-box; }
27
+
28
+ body {
29
+ font-family: 'Space Mono', monospace;
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ min-height: 100vh;
33
+ overflow-x: hidden;
34
+ }
35
+
36
+ /* โ”€โ”€ BACKGROUND โ”€โ”€ */
37
+ body::before {
38
+ content: '';
39
+ position: fixed;
40
+ inset: 0;
41
+ background:
42
+ radial-gradient(ellipse 60% 40% at 15% 20%, rgba(232,68,10,0.08) 0%, transparent 60%),
43
+ radial-gradient(ellipse 50% 50% at 85% 80%, rgba(58,123,213,0.07) 0%, transparent 60%),
44
+ radial-gradient(ellipse 40% 40% at 50% 50%, rgba(34,201,122,0.04) 0%, transparent 60%);
45
+ pointer-events: none;
46
+ z-index: 0;
47
+ }
48
+
49
+ .wrap { position: relative; z-index: 1; max-width: 900px; margin: 0 auto; padding: 0 20px 60px; }
50
+
51
+ /* โ”€โ”€ HEADER โ”€โ”€ */
52
+ header {
53
+ padding: 40px 0 30px;
54
+ border-bottom: 1px solid var(--border);
55
+ margin-bottom: 30px;
56
+ }
57
+ .header-top { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
58
+ header h1 {
59
+ font-family: 'Syne', sans-serif;
60
+ font-weight: 800;
61
+ font-size: clamp(2rem, 5vw, 3.2rem);
62
+ letter-spacing: -2px;
63
+ line-height: 1;
64
+ }
65
+ header h1 span { color: var(--accent); }
66
+ .subtitle { font-size: 0.75rem; color: var(--muted); letter-spacing: 2px; text-transform: uppercase; margin-top: 8px; }
67
+
68
+ .science-tag {
69
+ background: var(--surface2);
70
+ border: 1px solid var(--border);
71
+ border-radius: 4px;
72
+ padding: 4px 10px;
73
+ font-size: 0.65rem;
74
+ color: var(--gold);
75
+ letter-spacing: 1px;
76
+ text-transform: uppercase;
77
+ }
78
+
79
+ /* โ”€โ”€ STATS BAR โ”€โ”€ */
80
+ .stats-bar {
81
+ display: flex; gap: 20px; flex-wrap: wrap;
82
+ background: var(--surface);
83
+ border: 1px solid var(--border);
84
+ border-radius: 8px;
85
+ padding: 16px 20px;
86
+ margin-bottom: 24px;
87
+ }
88
+ .stat { display: flex; flex-direction: column; gap: 2px; }
89
+ .stat-val { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 1.5rem; }
90
+ .stat-label { font-size: 0.62rem; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; }
91
+ .stat-val.green { color: var(--accent3); }
92
+ .stat-val.red { color: var(--accent); }
93
+ .stat-val.blue { color: var(--accent2); }
94
+ .stat-val.gold { color: var(--gold); }
95
+
96
+ /* โ”€โ”€ NAV TABS โ”€โ”€ */
97
+ .tabs {
98
+ display: flex; gap: 4px;
99
+ background: var(--surface);
100
+ border: 1px solid var(--border);
101
+ border-radius: 8px;
102
+ padding: 4px;
103
+ margin-bottom: 24px;
104
+ overflow-x: auto;
105
+ }
106
+ .tab {
107
+ flex: 1; min-width: 80px;
108
+ padding: 10px 14px;
109
+ background: transparent;
110
+ border: none;
111
+ color: var(--muted);
112
+ font-family: 'Space Mono', monospace;
113
+ font-size: 0.7rem;
114
+ cursor: pointer;
115
+ border-radius: 6px;
116
+ transition: all .2s;
117
+ white-space: nowrap;
118
+ text-transform: uppercase;
119
+ letter-spacing: 1px;
120
+ }
121
+ .tab.active {
122
+ background: var(--accent);
123
+ color: white;
124
+ }
125
+ .tab:hover:not(.active) { background: var(--surface2); color: var(--text); }
126
+
127
+ /* โ”€โ”€ SCREENS โ”€โ”€ */
128
+ .screen { display: none; }
129
+ .screen.active { display: block; }
130
+
131
+ /* โ”€โ”€ CHAPTER FILTER โ”€โ”€ */
132
+ .filter-row {
133
+ display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px;
134
+ }
135
+ .filter-btn {
136
+ padding: 6px 14px;
137
+ border-radius: 20px;
138
+ border: 1px solid var(--border);
139
+ background: transparent;
140
+ color: var(--muted);
141
+ font-family: 'Space Mono', monospace;
142
+ font-size: 0.68rem;
143
+ cursor: pointer;
144
+ transition: all .2s;
145
+ }
146
+ .filter-btn.active { color: white; border-color: transparent; }
147
+ .filter-btn[data-ch="all"].active { background: var(--muted); }
148
+ .filter-btn[data-ch="3"].active { background: var(--ch3); }
149
+ .filter-btn[data-ch="4"].active { background: var(--ch4); }
150
+ .filter-btn[data-ch="5"].active { background: var(--ch5); }
151
+ .filter-btn[data-ch="synth"].active { background: var(--synth); color: #000; }
152
+
153
+ /* โ”€โ”€ FLASHCARD โ”€โ”€ */
154
+ .flashcard-zone {
155
+ perspective: 1200px;
156
+ margin-bottom: 20px;
157
+ }
158
+ .card-wrap {
159
+ width: 100%;
160
+ min-height: 260px;
161
+ position: relative;
162
+ transform-style: preserve-3d;
163
+ transition: transform .5s cubic-bezier(.4,0,.2,1);
164
+ cursor: pointer;
165
+ }
166
+ .card-wrap.flipped { transform: rotateY(180deg); }
167
+ .card-face {
168
+ position: absolute; inset: 0;
169
+ backface-visibility: hidden;
170
+ border-radius: 12px;
171
+ padding: 36px 40px;
172
+ display: flex;
173
+ flex-direction: column;
174
+ justify-content: center;
175
+ min-height: 260px;
176
+ }
177
+ .card-front {
178
+ background: var(--surface);
179
+ border: 1px solid var(--border);
180
+ }
181
+ .card-back {
182
+ background: var(--surface2);
183
+ border: 1px solid var(--border);
184
+ transform: rotateY(180deg);
185
+ }
186
+ .card-chapter {
187
+ font-size: 0.6rem;
188
+ text-transform: uppercase;
189
+ letter-spacing: 2px;
190
+ margin-bottom: 16px;
191
+ font-weight: 700;
192
+ }
193
+ .card-number {
194
+ position: absolute;
195
+ top: 16px; right: 20px;
196
+ font-size: 0.65rem;
197
+ color: var(--muted);
198
+ }
199
+ .card-question {
200
+ font-family: 'Syne', sans-serif;
201
+ font-size: clamp(1rem, 2.5vw, 1.3rem);
202
+ font-weight: 700;
203
+ line-height: 1.4;
204
+ color: var(--text);
205
+ }
206
+ .card-hint {
207
+ margin-top: 12px;
208
+ font-size: 0.7rem;
209
+ color: var(--muted);
210
+ font-style: italic;
211
+ }
212
+ .card-answer {
213
+ font-size: clamp(0.85rem, 2vw, 1rem);
214
+ line-height: 1.7;
215
+ color: var(--text);
216
+ }
217
+ .card-answer strong { color: var(--gold); }
218
+ .card-flip-hint {
219
+ position: absolute;
220
+ bottom: 16px; right: 20px;
221
+ font-size: 0.6rem;
222
+ color: var(--muted);
223
+ letter-spacing: 1px;
224
+ }
225
+
226
+ /* โ”€โ”€ PROGRESS RING โ”€โ”€ */
227
+ .progress-area {
228
+ display: flex; align-items: center; gap: 20px;
229
+ background: var(--surface);
230
+ border: 1px solid var(--border);
231
+ border-radius: 8px;
232
+ padding: 16px 20px;
233
+ margin-bottom: 16px;
234
+ }
235
+ .progress-bar-outer {
236
+ flex: 1; height: 6px;
237
+ background: var(--border);
238
+ border-radius: 3px;
239
+ overflow: hidden;
240
+ }
241
+ .progress-bar-inner {
242
+ height: 100%;
243
+ border-radius: 3px;
244
+ background: linear-gradient(90deg, var(--accent), var(--gold));
245
+ transition: width .4s ease;
246
+ }
247
+ .progress-text { font-size: 0.7rem; color: var(--muted); white-space: nowrap; }
248
+
249
+ /* โ”€โ”€ CARD CONTROLS โ”€โ”€ */
250
+ .card-controls {
251
+ display: grid;
252
+ grid-template-columns: 1fr 1fr 1fr;
253
+ gap: 10px;
254
+ margin-bottom: 24px;
255
+ }
256
+ .ctrl-btn {
257
+ padding: 12px;
258
+ border: 1px solid var(--border);
259
+ border-radius: 8px;
260
+ background: var(--surface);
261
+ color: var(--text);
262
+ font-family: 'Space Mono', monospace;
263
+ font-size: 0.72rem;
264
+ cursor: pointer;
265
+ transition: all .2s;
266
+ text-align: center;
267
+ line-height: 1.3;
268
+ }
269
+ .ctrl-btn:hover { transform: translateY(-2px); }
270
+ .ctrl-btn.bad { border-color: var(--accent); }
271
+ .ctrl-btn.bad:hover { background: var(--accent); }
272
+ .ctrl-btn.ok { border-color: var(--gold); }
273
+ .ctrl-btn.ok:hover { background: var(--gold); color: #000; }
274
+ .ctrl-btn.good { border-color: var(--accent3); }
275
+ .ctrl-btn.good:hover { background: var(--accent3); color: #000; }
276
+ .ctrl-btn.nav { border-color: var(--border); }
277
+ .ctrl-btn.nav:hover { background: var(--surface2); }
278
+ .ctrl-btn small { display: block; font-size: 0.58rem; color: var(--muted); margin-top: 2px; }
279
+ .ctrl-btn.bad small { color: rgba(232,68,10,0.7); }
280
+ .ctrl-btn.ok small { color: rgba(245,200,66,0.7); }
281
+ .ctrl-btn.good small { color: rgba(34,201,122,0.7); }
282
+
283
+ /* โ”€โ”€ QUIZ SCREEN โ”€โ”€ */
284
+ .quiz-q {
285
+ background: var(--surface);
286
+ border: 1px solid var(--border);
287
+ border-radius: 12px;
288
+ padding: 28px 32px;
289
+ margin-bottom: 16px;
290
+ }
291
+ .quiz-meta {
292
+ font-size: 0.62rem;
293
+ text-transform: uppercase;
294
+ letter-spacing: 2px;
295
+ color: var(--muted);
296
+ margin-bottom: 12px;
297
+ display: flex; justify-content: space-between; align-items: center;
298
+ }
299
+ .quiz-question {
300
+ font-family: 'Syne', sans-serif;
301
+ font-size: clamp(1rem, 2.5vw, 1.2rem);
302
+ font-weight: 700;
303
+ line-height: 1.5;
304
+ }
305
+ .quiz-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
306
+ .quiz-opt {
307
+ padding: 14px 20px;
308
+ border: 1px solid var(--border);
309
+ border-radius: 8px;
310
+ background: var(--surface);
311
+ color: var(--text);
312
+ font-family: 'Space Mono', monospace;
313
+ font-size: 0.8rem;
314
+ cursor: pointer;
315
+ transition: all .2s;
316
+ text-align: left;
317
+ line-height: 1.4;
318
+ }
319
+ .quiz-opt:hover:not(:disabled) { border-color: var(--accent2); background: var(--surface2); }
320
+ .quiz-opt.correct { border-color: var(--accent3) !important; background: rgba(34,201,122,0.1) !important; color: var(--accent3); }
321
+ .quiz-opt.wrong { border-color: var(--accent) !important; background: rgba(232,68,10,0.1) !important; color: var(--accent); }
322
+ .quiz-opt:disabled { cursor: default; }
323
+ .quiz-feedback {
324
+ padding: 14px 20px;
325
+ border-radius: 8px;
326
+ font-size: 0.78rem;
327
+ line-height: 1.6;
328
+ margin-bottom: 16px;
329
+ display: none;
330
+ }
331
+ .quiz-feedback.show { display: block; }
332
+ .quiz-feedback.correct-fb { background: rgba(34,201,122,0.08); border: 1px solid rgba(34,201,122,0.3); color: var(--accent3); }
333
+ .quiz-feedback.wrong-fb { background: rgba(232,68,10,0.08); border: 1px solid rgba(232,68,10,0.3); color: var(--accent); }
334
+ .quiz-next {
335
+ padding: 12px 28px;
336
+ background: var(--accent);
337
+ border: none;
338
+ border-radius: 8px;
339
+ color: white;
340
+ font-family: 'Space Mono', monospace;
341
+ font-size: 0.78rem;
342
+ cursor: pointer;
343
+ display: none;
344
+ transition: all .2s;
345
+ }
346
+ .quiz-next.show { display: inline-block; }
347
+ .quiz-next:hover { background: #ff5520; transform: translateY(-1px); }
348
+
349
+ /* QUIZ RESULTS */
350
+ .quiz-result {
351
+ text-align: center;
352
+ padding: 50px 20px;
353
+ display: none;
354
+ }
355
+ .quiz-result.show { display: block; }
356
+ .result-score {
357
+ font-family: 'Syne', sans-serif;
358
+ font-size: 5rem;
359
+ font-weight: 800;
360
+ line-height: 1;
361
+ margin-bottom: 8px;
362
+ }
363
+ .result-label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 2px; margin-bottom: 24px; }
364
+ .result-msg { font-size: 1.1rem; margin-bottom: 32px; line-height: 1.6; }
365
+ .restart-btn {
366
+ padding: 14px 32px;
367
+ background: var(--accent);
368
+ border: none;
369
+ border-radius: 8px;
370
+ color: white;
371
+ font-family: 'Space Mono', monospace;
372
+ font-size: 0.82rem;
373
+ cursor: pointer;
374
+ margin: 0 6px;
375
+ transition: all .2s;
376
+ }
377
+ .restart-btn:hover { background: #ff5520; }
378
+ .restart-btn.outline {
379
+ background: transparent;
380
+ border: 1px solid var(--border);
381
+ color: var(--text);
382
+ }
383
+ .restart-btn.outline:hover { background: var(--surface2); }
384
+
385
+ /* โ”€โ”€ REVIEW MAP โ”€โ”€ */
386
+ .review-grid {
387
+ display: grid;
388
+ grid-template-columns: repeat(auto-fill, minmax(42px, 1fr));
389
+ gap: 6px;
390
+ margin-bottom: 24px;
391
+ }
392
+ .review-cell {
393
+ aspect-ratio: 1;
394
+ border-radius: 6px;
395
+ border: 1px solid var(--border);
396
+ background: var(--surface);
397
+ display: flex; align-items: center; justify-content: center;
398
+ font-size: 0.6rem;
399
+ color: var(--muted);
400
+ cursor: pointer;
401
+ transition: all .2s;
402
+ font-weight: 700;
403
+ }
404
+ .review-cell:hover { transform: scale(1.1); border-color: var(--accent2); }
405
+ .review-cell.status-good { background: rgba(34,201,122,0.2); border-color: var(--accent3); color: var(--accent3); }
406
+ .review-cell.status-ok { background: rgba(245,200,66,0.2); border-color: var(--gold); color: var(--gold); }
407
+ .review-cell.status-bad { background: rgba(232,68,10,0.2); border-color: var(--accent); color: var(--accent); }
408
+ .review-cell.status-unseen { opacity: 0.4; }
409
+ .legend { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
410
+ .legend-item { display: flex; align-items: center; gap: 6px; font-size: 0.66rem; color: var(--muted); }
411
+ .legend-dot { width: 10px; height: 10px; border-radius: 2px; }
412
+ .legend-dot.green { background: var(--accent3); }
413
+ .legend-dot.gold { background: var(--gold); }
414
+ .legend-dot.red { background: var(--accent); }
415
+ .legend-dot.grey { background: var(--surface2); border: 1px solid var(--border); }
416
+
417
+ /* โ”€โ”€ RECAP SCREEN โ”€โ”€ */
418
+ .recap-section { margin-bottom: 28px; }
419
+ .recap-title {
420
+ font-family: 'Syne', sans-serif;
421
+ font-size: 0.7rem;
422
+ text-transform: uppercase;
423
+ letter-spacing: 3px;
424
+ font-weight: 700;
425
+ padding: 8px 0;
426
+ border-bottom: 1px solid var(--border);
427
+ margin-bottom: 14px;
428
+ }
429
+ .recap-item {
430
+ padding: 12px 16px;
431
+ border-left: 3px solid var(--border);
432
+ margin-bottom: 8px;
433
+ background: var(--surface);
434
+ border-radius: 0 6px 6px 0;
435
+ font-size: 0.78rem;
436
+ line-height: 1.6;
437
+ transition: border-color .2s;
438
+ }
439
+ .recap-item:hover { border-left-color: var(--accent2); }
440
+ .recap-item .num { color: var(--muted); font-size: 0.62rem; display: block; margin-bottom: 4px; }
441
+ .recap-ch3 { border-left-color: rgba(232,68,10,0.4); }
442
+ .recap-ch4 { border-left-color: rgba(58,123,213,0.4); }
443
+ .recap-ch5 { border-left-color: rgba(34,201,122,0.4); }
444
+ .recap-synth { border-left-color: rgba(245,200,66,0.4); }
445
+
446
+ /* โ”€โ”€ ANIMATIONS โ”€โ”€ */
447
+ @keyframes fadeUp {
448
+ from { opacity: 0; transform: translateY(16px); }
449
+ to { opacity: 1; transform: translateY(0); }
450
+ }
451
+ .screen.active { animation: fadeUp .3s ease; }
452
+
453
+ @keyframes pulse {
454
+ 0%, 100% { opacity: 1; }
455
+ 50% { opacity: 0.5; }
456
+ }
457
+
458
+ /* โ”€โ”€ RESPONSIVE โ”€โ”€ */
459
+ @media (max-width: 600px) {
460
+ .card-controls { grid-template-columns: 1fr 1fr; }
461
+ .stats-bar { gap: 14px; }
462
+ .card-face { padding: 24px 20px; }
463
+ }
464
+ </style>
465
+ </head>
466
+ <body>
467
+ <div class="wrap">
468
+
469
+ <header>
470
+ <div class="header-top">
471
+ <h1>Gร‰O<span>Rร‰VISION</span></h1>
472
+ <span class="science-tag">Mรฉthode Ebbinghaus + Testing Effect</span>
473
+ </div>
474
+ <div class="subtitle">Tectonique des Plaques โ€” Chapitres 3, 4, 5 &amp; Synthรจse ยท 76 notions</div>
475
+ </header>
476
+
477
+ <!-- STATS -->
478
+ <div class="stats-bar">
479
+ <div class="stat"><span class="stat-val gold" id="stat-total">76</span><span class="stat-label">Notions</span></div>
480
+ <div class="stat"><span class="stat-val green" id="stat-maรฎtrisรฉ">0</span><span class="stat-label">Maรฎtrisรฉes</span></div>
481
+ <div class="stat"><span class="stat-val" style="color:var(--gold)" id="stat-appris">0</span><span class="stat-label">En cours</span></div>
482
+ <div class="stat"><span class="stat-val red" id="stat-revoir">0</span><span class="stat-label">ร€ revoir</span></div>
483
+ <div class="stat"><span class="stat-val blue" id="stat-quiz-score">โ€”</span><span class="stat-label">Score Quiz</span></div>
484
+ </div>
485
+
486
+ <!-- TABS -->
487
+ <div class="tabs">
488
+ <button class="tab active" onclick="showScreen('flashcard')">Flashcards</button>
489
+ <button class="tab" onclick="showScreen('quiz')">Quiz</button>
490
+ <button class="tab" onclick="showScreen('map')">Carte</button>
491
+ <button class="tab" onclick="showScreen('recap')">Rรฉcap. complet</button>
492
+ </div>
493
+
494
+ <!-- โ•โ•โ•โ•โ•โ•โ• FLASHCARD SCREEN โ•โ•โ•โ•โ•โ•โ• -->
495
+ <div class="screen active" id="screen-flashcard">
496
+
497
+ <div class="filter-row">
498
+ <button class="filter-btn active" data-ch="all" onclick="setFilter('all', this)">Tout (76)</button>
499
+ <button class="filter-btn" data-ch="3" onclick="setFilter('3', this)">CH.3 Mobilitรฉ (19)</button>
500
+ <button class="filter-btn" data-ch="4" onclick="setFilter('4', this)">CH.4 Divergence (21)</button>
501
+ <button class="filter-btn" data-ch="5" onclick="setFilter('5', this)">CH.5 Convergence (20)</button>
502
+ <button class="filter-btn" data-ch="synth" onclick="setFilter('synth', this)">Synthรจse (5+1)</button>
503
+ <button class="filter-btn" data-ch="weak" onclick="setFilter('weak', this)">โš  ร€ revoir</button>
504
+ </div>
505
+
506
+ <div class="progress-area">
507
+ <div class="progress-bar-outer"><div class="progress-bar-inner" id="fc-progress" style="width:0%"></div></div>
508
+ <span class="progress-text" id="fc-counter">0 / 0</span>
509
+ </div>
510
+
511
+ <div class="flashcard-zone" onclick="flipCard()">
512
+ <div class="card-wrap" id="card-wrap">
513
+ <div class="card-face card-front">
514
+ <span class="card-number" id="card-num"></span>
515
+ <div class="card-chapter" id="card-chap"></div>
516
+ <div class="card-question" id="card-q"></div>
517
+ <div class="card-hint" id="card-hint"></div>
518
+ <div class="card-flip-hint">CLIQUER POUR Rร‰Vร‰LER โ†—</div>
519
+ </div>
520
+ <div class="card-face card-back">
521
+ <span class="card-number" id="card-num2"></span>
522
+ <div class="card-chapter" id="card-chap2"></div>
523
+ <div class="card-answer" id="card-a"></div>
524
+ <div class="card-flip-hint">โ† RETOURNER</div>
525
+ </div>
526
+ </div>
527
+ </div>
528
+
529
+ <div class="card-controls" id="card-controls" style="display:none">
530
+ <button class="ctrl-btn bad" onclick="rateCard(0)">โœ— ร€ revoir<small>Je ne savais pas</small></button>
531
+ <button class="ctrl-btn ok" onclick="rateCard(1)">~ Hรฉsitant<small>Approximatif</small></button>
532
+ <button class="ctrl-btn good" onclick="rateCard(2)">โœ“ Maรฎtrisรฉ<small>Rรฉponse exacte</small></button>
533
+ </div>
534
+ <div class="card-controls" id="card-nav">
535
+ <button class="ctrl-btn nav" onclick="prevCard()">โ† Prรฉcรฉdent</button>
536
+ <button class="ctrl-btn nav" onclick="shuffleCards()">โŸณ Mรฉlanger</button>
537
+ <button class="ctrl-btn nav" onclick="nextCard()">Suivant โ†’</button>
538
+ </div>
539
+
540
+ </div>
541
+
542
+ <!-- โ•โ•โ•โ•โ•โ•โ• QUIZ SCREEN โ•โ•โ•โ•โ•โ•โ• -->
543
+ <div class="screen" id="screen-quiz">
544
+
545
+ <div class="filter-row">
546
+ <button class="filter-btn active" data-ch="all" onclick="setQuizFilter('all', this)">Tout</button>
547
+ <button class="filter-btn" data-ch="3" onclick="setQuizFilter('3', this)">CH.3</button>
548
+ <button class="filter-btn" data-ch="4" onclick="setQuizFilter('4', this)">CH.4</button>
549
+ <button class="filter-btn" data-ch="5" onclick="setQuizFilter('5', this)">CH.5</button>
550
+ <button class="filter-btn" data-ch="weak" onclick="setQuizFilter('weak', this)">โš  Faibles</button>
551
+ </div>
552
+
553
+ <div id="quiz-container">
554
+ <div class="quiz-q">
555
+ <div class="quiz-meta">
556
+ <span id="quiz-chapter-tag"></span>
557
+ <span id="quiz-progress-tag"></span>
558
+ </div>
559
+ <div class="quiz-question" id="quiz-q-text"></div>
560
+ </div>
561
+ <div class="quiz-options" id="quiz-options"></div>
562
+ <div class="quiz-feedback" id="quiz-feedback"></div>
563
+ <button class="quiz-next" id="quiz-next" onclick="nextQuiz()">Question suivante โ†’</button>
564
+ </div>
565
+
566
+ <div class="quiz-result" id="quiz-result">
567
+ <div class="result-score" id="result-score"></div>
568
+ <div class="result-label">score final</div>
569
+ <div class="result-msg" id="result-msg"></div>
570
+ <button class="restart-btn" onclick="restartQuiz()">Recommencer</button>
571
+ <button class="restart-btn outline" onclick="showScreen('flashcard')">Rรฉviser les flashcards</button>
572
+ </div>
573
+
574
+ </div>
575
+
576
+ <!-- โ•โ•โ•โ•โ•โ•โ• MAP SCREEN โ•โ•โ•โ•โ•โ•โ• -->
577
+ <div class="screen" id="screen-map">
578
+ <div class="legend">
579
+ <div class="legend-item"><div class="legend-dot green"></div>Maรฎtrisรฉ</div>
580
+ <div class="legend-item"><div class="legend-dot gold"></div>Hรฉsitant</div>
581
+ <div class="legend-item"><div class="legend-dot red"></div>ร€ revoir</div>
582
+ <div class="legend-item"><div class="legend-dot grey"></div>Non vu</div>
583
+ </div>
584
+ <div class="review-grid" id="review-grid"></div>
585
+ <p style="font-size:0.7rem;color:var(--muted)">Cliquez sur un numรฉro pour aller directement ร  la carte correspondante.</p>
586
+ </div>
587
+
588
+ <!-- โ•โ•โ•โ•โ•โ•โ• RECAP SCREEN โ•โ•โ•โ•โ•โ•โ• -->
589
+ <div class="screen" id="screen-recap">
590
+ <div id="recap-content"></div>
591
+ </div>
592
+
593
+ </div>
594
+
595
+ <script>
596
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
597
+ // DATA โ€” 76 cartes complรจtes
598
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
599
+ const CARDS = [
600
+ // CH.3 โ€” I. Les plaques et leurs frontiรจres
601
+ {id:1, ch:"3", section:"CH.3 โ€” Limites de plaques",
602
+ q:"Quels sont les 3 grands types de limites de plaques ?",
603
+ hint:"Pense : naissance / destruction / collision",
604
+ a:"<strong>Dorsales</strong> (divergence/accrรฉtion), <strong>Fosses ocรฉaniques</strong> (convergence/subduction), <strong>Chaรฎnes de montagnes</strong> (convergence/collision)."},
605
+ {id:2, ch:"3", section:"CH.3 โ€” Dorsales",
606
+ q:"Quelles roches sont produites ร  la dorsale ? Quelle est leur texture ?",
607
+ hint:"Deux roches, deux textures",
608
+ a:"<strong>Basalte</strong> (texture microlithique, refroidissement rapide en surface) et <strong>Gabbro</strong> (texture grenue, refroidissement lent en profondeur)."},
609
+ {id:3, ch:"3", section:"CH.3 โ€” Dorsales",
610
+ q:"Quel type de failles caractรฉrise les dorsales ? Pourquoi ?",
611
+ hint:"Extension ou compression ?",
612
+ a:"<strong>Failles normales</strong>, car la divergence des plaques crรฉe une extension (รฉtirement) de la croรปte."},
613
+ {id:4, ch:"3", section:"CH.3 โ€” Dorsales",
614
+ q:"Quelles sont les caractรฉristiques sismiques et thermiques d'une dorsale ?",
615
+ hint:"Profondeur + intensitรฉ + flux",
616
+ a:"Sismicitรฉ <strong>superficielle et de faible magnitude</strong>. Flux gรฉothermique <strong>รฉlevรฉ</strong> en raison du magmatisme ascendant."},
617
+ {id:5, ch:"3", section:"CH.3 โ€” Fosses",
618
+ q:"Quelles roches plutoniques et volcaniques sont produites aux zones de subduction ?",
619
+ hint:"4 roches, 2 textures diffรฉrentes",
620
+ a:"Plutoniques (grenues) : <strong>diorite, granite</strong>. Volcaniques (microlithiques) : <strong>andรฉsite, rhyolite</strong>."},
621
+ {id:6, ch:"3", section:"CH.3 โ€” Fosses",
622
+ q:"Comment se rรฉpartissent les foyers sismiques en zone de subduction ? Jusqu'oรน ?",
623
+ hint:"Un plan cรฉlรจbre",
624
+ a:"Selon le <strong>plan de Wadati-Benioff</strong> : plan inclinรฉ suivant la plaque plongeante, atteignant <strong>700 km de profondeur</strong>."},
625
+ {id:7, ch:"3", section:"CH.3 โ€” Fosses",
626
+ q:"Dรฉcris le flux gรฉothermique en zone de subduction (deux zones distinctes).",
627
+ hint:"Froid ici, chaud lร -bas",
628
+ a:"<strong>Bas</strong> au niveau de la plaque froide qui plonge. <strong>Fort</strong> au niveau de l'arc volcanique sus-jacent."},
629
+ {id:8, ch:"3", section:"CH.3 โ€” Collision",
630
+ q:"Quelles sont les caractรฉristiques gรฉologiques et sismiques d'une zone de collision ?",
631
+ hint:"Type de failles + profondeur sรฉismes",
632
+ a:"<strong>Failles inverses</strong>, flux gรฉothermique <strong>faible</strong>, sismicitรฉ principalement <strong>superficielle</strong>."},
633
+ {id:9, ch:"3", section:"CH.3 โ€” Points chauds",
634
+ q:"Comment un alignement de volcans inactifs prouve-t-il le mouvement des plaques ?",
635
+ hint:"ร‚ge + distance",
636
+ a:"Le point chaud est <strong>fixe</strong> dans le manteau profond. La plaque se dรฉplace au-dessus : l'รขge des volcans <strong>augmente avec la distance</strong> au volcan actif."},
637
+ {id:10, ch:"3", section:"CH.3 โ€” Anomalies magnรฉtiques",
638
+ q:"Qu'est-ce qu'une anomalie magnรฉtique positive vs nรฉgative ?",
639
+ hint:"Intensitรฉ mesurรฉe vs moyenne",
640
+ a:"<strong>Positive</strong> : intensitรฉ mesurรฉe > moyenne โ†’ pรฉriode de polaritรฉ normale. <strong>Nรฉgative</strong> : intensitรฉ < moyenne โ†’ pรฉriode inverse (pรดles inversรฉs)."},
641
+ {id:11, ch:"3", section:"CH.3 โ€” Anomalies magnรฉtiques",
642
+ q:"Dรฉcris le patron des anomalies magnรฉtiques autour d'une dorsale.",
643
+ hint:"Symรฉtrie + image",
644
+ a:"Bandes <strong>parallรจles et symรฉtriques</strong> par rapport ร  l'axe de la dorsale, comme un <strong>double tapis roulant</strong>. Plus les basaltes sont loin, plus ils sont vieux."},
645
+ {id:12, ch:"3", section:"CH.3 โ€” Anomalies magnรฉtiques",
646
+ q:"Que rรฉvรจle l'รขge et l'รฉpaisseur des sรฉdiments sur le plancher ocรฉanique ?",
647
+ hint:"Loi simple distance/รขge",
648
+ a:"L'รฉpaisseur et l'รขge des sรฉdiments au contact du basalte <strong>augmentent avec la distance</strong> ร  la dorsale : preuve de l'expansion ocรฉanique."},
649
+ {id:13, ch:"3", section:"CH.3 โ€” Gรฉodรฉsie",
650
+ q:"Que permet la gรฉodรฉsie spatiale (GPS) en tectonique ?",
651
+ hint:"Prรฉcision + deux types de vitesses",
652
+ a:"Localisation des stations au <strong>millimรจtre prรจs</strong>, calcul des vitesses <strong>absolues</strong> (par rapport ร  repรจres fixes) et <strong>relatives</strong> (entre deux plaques)."},
653
+ {id:14, ch:"3", section:"CH.3 โ€” Rรฉsumรฉ frontiรจres",
654
+ q:"Quelle frontiรจre prรฉsente une vallรฉe axiale (rift) ? Quel type de mouvement ?",
655
+ hint:"Rift = fossรฉ d'effondrement",
656
+ a:"La <strong>dorsale</strong> : mouvement de <strong>divergence</strong>. La vallรฉe axiale (rift) tรฉmoigne de l'extension."},
657
+ {id:15, ch:"3", section:"CH.3 โ€” Rรฉsumรฉ frontiรจres",
658
+ q:"Lithosphรจre terrestre : structure de base et activitรฉ aux frontiรจres.",
659
+ hint:"Dรฉcoupe + concentration",
660
+ a:"Dรฉcoupรฉe en <strong>plaques lithosphรฉriques rigides</strong>. Les frontiรจres concentrent l'essentiel de l'<strong>activitรฉ sismique et volcanique</strong>."},
661
+ {id:16, ch:"3", section:"CH.3 โ€” Anomalies magnรฉtiques",
662
+ q:"Comment les basaltes enregistrent-ils le champ magnรฉtique ?",
663
+ hint:"Moment clรฉ = refroidissement",
664
+ a:"En se <strong>refroidissant</strong>, les minรฉraux magnรฉtiques (magnรฉtite) se figent dans la direction du champ magnรฉtique terrestre du moment."},
665
+ {id:17, ch:"3", section:"CH.3 โ€” Dorsales",
666
+ q:"Que produit l'accrรฉtion ocรฉanique ร  la dorsale ?",
667
+ hint:"Naรฎt quoi ?",
668
+ a:"La <strong>croรปte ocรฉanique</strong> : basalte en surface + gabbro en profondeur + pรฉridotite mantellique sous-jacente."},
669
+ {id:18, ch:"3", section:"CH.3 โ€” Fosses",
670
+ q:"Qu'est-ce qu'une fosse ocรฉanique ? Quel processus y est associรฉ ?",
671
+ hint:"Plongementโ€ฆ",
672
+ a:"Dรฉpression profonde marquant une <strong>zone de subduction</strong> : plongement d'une plaque ocรฉanique dense dans le manteau."},
673
+ {id:19, ch:"3", section:"CH.3 โ€” Collision",
674
+ q:"Qu'est-ce qu'une chaรฎne de montagnes en termes tectoniques ?",
675
+ hint:"Fermeture d'un ocรฉan",
676
+ a:"Zone de <strong>collision continentale</strong> : rรฉsultat de la convergence de deux plaques continentales aprรจs fermeture d'un ocรฉan."},
677
+
678
+ // CH.4 โ€” I. Dorsales rapides
679
+ {id:20, ch:"4", section:"CH.4 โ€” Dorsales rapides",
680
+ q:"Quelle est la vitesse d'รฉcartement d'une dorsale rapide ? Exemple ?",
681
+ hint:"10โ€“16 cm/an โ€” Quel ocรฉan ?",
682
+ a:"<strong>10 ร  16 cm/an</strong>. Exemple : dorsale du Pacifique Est (Pacifique)."},
683
+ {id:21, ch:"4", section:"CH.4 โ€” Dorsales rapides",
684
+ q:"Quelle est la morphologie d'une dorsale rapide (rift, bombement) ?",
685
+ hint:"Opposรฉ de la dorsale lente",
686
+ a:"<strong>Bombement large</strong> mais <strong>vallรฉe axiale quasi absente</strong> ou peu marquรฉe (magmatisme abondant comble le rift)."},
687
+ {id:22, ch:"4", section:"CH.4 โ€” Dorsales rapides",
688
+ q:"Dรฉcris le processus de fusion partielle sous une dorsale (รฉtapes).",
689
+ hint:"Convection โ†’ dรฉcompression โ†’ solidus",
690
+ a:"<strong>1.</strong> Remontรฉe des pรฉridotites par <strong>convection</strong>. <strong>2.</strong> <strong>Dรฉcompression adiabatique</strong> : le gรฉotherme recoupe le solidus. <strong>3.</strong> <strong>Fusion partielle</strong> (~15%) โ†’ gouttelettes de magma."},
691
+ {id:23, ch:"4", section:"CH.4 โ€” Dorsales rapides",
692
+ q:"Oรน se loge la chambre magmatique sous une dorsale rapide ? Quelles roches en sortent ?",
693
+ hint:"Profondeur + 2 roches",
694
+ a:"Entre <strong>2 et 7 km</strong>. Refroidissement lent โ†’ <strong>gabbros</strong> (grenu). Remontรฉe et refroidissement rapide โ†’ <strong>basaltes</strong> (microlithique)."},
695
+ {id:24, ch:"4", section:"CH.4 โ€” Dorsales rapides",
696
+ q:"Quelle รฉpaisseur de croรปte produit une dorsale rapide ?",
697
+ hint:"Chiffre prรฉcis",
698
+ a:"Croรปte ocรฉanique rรฉguliรจre de <strong>5 ร  7 km</strong> d'รฉpaisseur."},
699
+ {id:25, ch:"4", section:"CH.4 โ€” Dorsales lentes",
700
+ q:"Quelle est la vitesse d'รฉcartement d'une dorsale lente ? Exemple ?",
701
+ hint:"1โ€“5 cm/an โ€” Quel ocรฉan ?",
702
+ a:"<strong>1 ร  5 cm/an</strong>. Exemple : dorsale mรฉdio-atlantique (Atlantique)."},
703
+ {id:26, ch:"4", section:"CH.4 โ€” Dorsales lentes",
704
+ q:"Morphologie d'une dorsale lente : rift, magmatisme, expansion.",
705
+ hint:"Opposรฉ de rapide",
706
+ a:"<strong>Rift trรจs marquรฉ</strong> avec grandes failles de dรฉtachement. Magmatisme <strong>rรฉduit et discontinu</strong>. Expansion parfois assurรฉe par <strong>รฉtirement tectonique</strong>."},
707
+ {id:27, ch:"4", section:"CH.4 โ€” Dorsales lentes",
708
+ q:"Qu'est-ce que la serpentinisation et quand se produit-elle ?",
709
+ hint:"Roche + eau + dorsale lente",
710
+ a:"Hydratation des <strong>pรฉridotites</strong> (manteau) qui remontent directement en surface faute de magmatisme. L'eau transforme l'olivine en <strong>serpentine</strong>."},
711
+ {id:28, ch:"4", section:"CH.4 โ€” Vieillissement",
712
+ q:"Qu'est-ce que le mรฉtamorphisme hydrothermal ? Quand se produit-il ?",
713
+ hint:"Eau + chaleur + minรฉraux",
714
+ a:"Transformation <strong>ร  l'รฉtat solide</strong> des minรฉraux de la croรปte ocรฉanique par infiltration d'eau de mer chauffรฉe. Produit des minรฉraux <strong>hydratรฉs</strong>."},
715
+ {id:29, ch:"4", section:"CH.4 โ€” Vieillissement",
716
+ q:"Quelle sรฉquence de faciรจs mรฉtamorphiques lors du vieillissement ocรฉanique ?",
717
+ hint:"Deux tempรฉratures, deux faciรจs",
718
+ a:"<strong>&gt; 700ยฐC</strong> : faciรจs <strong>amphibolite</strong> (hornblende). Entre <strong>500โ€“700ยฐC</strong> : faciรจs <strong>schiste vert</strong> (chlorite + actinote)."},
719
+ {id:30, ch:"4", section:"CH.4 โ€” Vieillissement",
720
+ q:"Comment รฉvolue la densitรฉ de la lithosphรจre ocรฉanique avec le temps ?",
721
+ hint:"Refroidissement โ†’ รฉpaississement โ†’ densitรฉ",
722
+ a:"La lithosphรจre <strong>se refroidit</strong>, <strong>s'รฉpaissit</strong> (l'isotherme 1300ยฐC s'approfondit), et sa <strong>densitรฉ augmente</strong> jusqu'ร  dรฉpasser celle de l'asthรฉnosphรจre."},
723
+ {id:31, ch:"4", section:"CH.4 โ€” Vieillissement",
724
+ q:"Oรน et comment naissent les fumeurs noirs ?",
725
+ hint:"Cycle eau de mer",
726
+ a:"L'eau de mer s'infiltre dans la croรปte, se <strong>rรฉchauffe</strong> au contact du magma, se charge en minรฉraux et ressort sous forme de <strong>sources hydrothermales chaudes</strong>."},
727
+ {id:32, ch:"4", section:"CH.4 โ€” Dorsales rapides",
728
+ q:"Qu'est-ce que la dรฉcompression adiabatique ? Pourquoi dรฉclenche-t-elle la fusion ?",
729
+ hint:"Pas de perte de chaleur",
730
+ a:"Remontรฉe de matรฉriel <strong>sans รฉchange de chaleur</strong> avec l'environnement. La pression baisse, le point de fusion (solidus) aussi : le gรฉotherme <strong>recoupe le solidus</strong> โ†’ fusion partielle."},
731
+ {id:33, ch:"4", section:"CH.4 โ€” Vieillissement",
732
+ q:"Quelle est la signification de l'isotherme 1300ยฐC pour la lithosphรจre ?",
733
+ hint:"Limite LAB",
734
+ a:"C'est la <strong>limite lithosphรจre-asthรฉnosphรจre (LAB)</strong>. Elle s'approfondit avec le refroidissement de la plaque qui s'รฉloigne de la dorsale."},
735
+ {id:34, ch:"4", section:"CH.4 โ€” Dorsales rapides",
736
+ q:"Basalte en coussin vs basalte en filon : quelle diffรฉrence ?",
737
+ hint:"Eau vs air",
738
+ a:"<strong>Coussin</strong> : refroidissement trรจs rapide au contact de l'eau (lave sous-marine). <strong>Filon</strong> : injection de magma dans des fissures (dykes)."},
739
+ {id:35, ch:"4", section:"CH.4 โ€” Vieillissement",
740
+ q:"Quelle minรฉralogie correspond au faciรจs schiste vert ?",
741
+ hint:"Tempรฉrature < 500ยฐC",
742
+ a:"Prรฉsence de <strong>chlorite</strong> et d'<strong>actinote</strong> (amphibole calcique) : minรฉraux hydratรฉs de basse tempรฉrature."},
743
+ {id:36, ch:"4", section:"CH.4 โ€” Dorsales lentes",
744
+ q:"Pourquoi une dorsale lente a-t-elle un rift trรจs marquรฉ ?",
745
+ hint:"Magma insuffisant pourโ€ฆ",
746
+ a:"Le magmatisme est trop faible pour <strong>combler l'espace crรฉรฉ par l'รฉcartement</strong>. L'extension tectonique forme donc un <strong>fossรฉ d'effondrement profond</strong>."},
747
+ {id:37, ch:"4", section:"CH.4 โ€” Vieillissement",
748
+ q:"Quelle transformation minรฉrale correspond au faciรจs amphibolite ?",
749
+ hint:"Pyroxรจne + Plagioclase โ†’ ?",
750
+ a:"L'assemblage initial <strong>pyroxรจne + plagioclase</strong> (gabbro) se transforme en <strong>hornblende</strong> (amphibole) sous l'action de l'hydrothermalisme > 700ยฐC."},
751
+ {id:38, ch:"4", section:"CH.4 โ€” Dorsales rapides",
752
+ q:"La pรฉridotite fond entiรจrement sous une dorsale ? Quel pourcentage ?",
753
+ hint:"Partielle !",
754
+ a:"Non : seulement <strong>fusion partielle (~15%)</strong>. Le reste reste ร  l'รฉtat solide. C'est le liquide qui monte former la croรปte."},
755
+ {id:39, ch:"4", section:"CH.4 โ€” Dorsales rapides",
756
+ q:"Quel est le rรดle de la convection mantellique dans le fonctionnement d'une dorsale ?",
757
+ hint:"Mouvement ascendant",
758
+ a:"La <strong>convection</strong> (mouvements de matiรจre du manteau chaud) assure la <strong>remontรฉe des pรฉridotites</strong> sous la dorsale, dรฉclenchant la dรฉcompression et la fusion."},
759
+ {id:40, ch:"4", section:"CH.4 โ€” Dorsales lentes",
760
+ q:"Qu'est-ce qu'une faille de dรฉtachement aux dorsales lentes ?",
761
+ hint:"Grande faille, faible pendage",
762
+ a:"Grande <strong>faille ร  faible pendage</strong> (presque horizontale) permettant l'exhumation de roches profondes (pรฉridotites) directement sur le plancher ocรฉanique."},
763
+
764
+ // CH.5 โ€” Zones de convergence
765
+ {id:41, ch:"5", section:"CH.5 โ€” Marqueurs de subduction",
766
+ q:"Quels sont les 3 marqueurs principaux d'une zone de subduction ?",
767
+ hint:"1 relief / 1 alignement / 1 sรฉismique",
768
+ a:"<strong>1.</strong> Fosse ocรฉanique profonde. <strong>2.</strong> Alignement volcanique (arc insulaire ou cordillรจre). <strong>3.</strong> Plan de Wadati-Benioff (sismicitรฉ profonde)."},
769
+ {id:42, ch:"5", section:"CH.5 โ€” Marqueurs de subduction",
770
+ q:"Pourquoi la sismicitรฉ suit-elle un plan inclinรฉ en subduction ?",
771
+ hint:"Rigiditรฉ de la plaque",
772
+ a:"La plaque ocรฉanique reste <strong>rigide</strong> en plongeant (elle est froide). Les contraintes gรฉnรจrent des sรฉismes le long du plan de contact : le <strong>plan de Wadati-Benioff</strong>."},
773
+ {id:43, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
774
+ q:"Pourquoi le volcanisme de subduction est-il explosif ?",
775
+ hint:"Composition du magma",
776
+ a:"Le magma est riche en <strong>silice</strong> (trรจs visqueux) et en <strong>gaz dissous</strong>. La viscositรฉ empรชche le dรฉgazage progressif โ†’ explosion violente."},
777
+ {id:44, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
778
+ q:"Quelle est la chaรฎne de rรฉactions menant au volcanisme de subduction ?",
779
+ hint:"Eau โ†’ pรฉridotite โ†’ magma",
780
+ a:"<strong>1.</strong> Mรฉtamorphisme de subduction โ†’ libรฉration d'<strong>eau</strong>. <strong>2.</strong> L'eau monte dans le <strong>coin mantellique</strong>. <strong>3.</strong> Elle abaisse le point de fusion des pรฉridotites. <strong>4.</strong> <strong>Fusion partielle</strong> โ†’ magma โ†’ volcanisme."},
781
+ {id:45, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
782
+ q:"Quelles sont les 4 roches de subduction et leur texture ?",
783
+ hint:"2 volcaniques + 2 plutoniques",
784
+ a:"Volcaniques (microlithique) : <strong>andรฉsite, rhyolite</strong>. Plutoniques (grenue) : <strong>diorite, granite</strong>."},
785
+ {id:46, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
786
+ q:"Pourquoi existe-t-il une telle diversitรฉ de roches en subduction ?",
787
+ hint:"2 processus",
788
+ a:"<strong>Cristallisation fractionnรฉe</strong> (diffรฉrenciation du magma en refroidissant) et <strong>contamination</strong> par la croรปte continentale traversรฉe."},
789
+ {id:47, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
790
+ q:"Quel est le signe minรฉralogique caractรฉristique des roches de subduction ?",
791
+ hint:"OH dans les minรฉraux",
792
+ a:"Prรฉsence de <strong>minรฉraux hydratรฉs</strong> portant des groupements <strong>OH</strong> (ex. : amphibole, biotite, muscovite)."},
793
+ {id:48, ch:"5", section:"CH.5 โ€” Mรฉtamorphisme de subduction",
794
+ q:"Quelle sรฉquence de faciรจs mรฉtamorphiques subit la plaque subduite ?",
795
+ hint:"3 faciรจs successifs",
796
+ a:"<strong>Schiste vert</strong> โ†’ <strong>schiste bleu</strong> (apparition de glaucophane, HP-BT) โ†’ <strong>รฉclogite</strong> (grenat + jadรฉite, > 50 km)."},
797
+ {id:49, ch:"5", section:"CH.5 โ€” Mรฉtamorphisme de subduction",
798
+ q:"Quelle minรฉralogie caractรฉrise le faciรจs schiste bleu ?",
799
+ hint:"Minรฉral bleu, HP-BT",
800
+ a:"Prรฉsence de <strong>glaucophane</strong> (amphibole sodique bleue), indicateur de conditions <strong>Haute Pression โ€“ Basse Tempรฉrature</strong>."},
801
+ {id:50, ch:"5", section:"CH.5 โ€” Mรฉtamorphisme de subduction",
802
+ q:"Quelle minรฉralogie caractรฉrise le faciรจs รฉclogite et ร  quelle profondeur ?",
803
+ hint:"> 50 km",
804
+ a:"<strong>Grenat</strong> + <strong>jadรฉite</strong> (pyroxรจne sodique), formรฉs ร  partir de > <strong>50 km</strong> de profondeur sous trรจs haute pression."},
805
+ {id:51, ch:"5", section:"CH.5 โ€” Mรฉtamorphisme de subduction",
806
+ q:"En quoi le contexte P-T de la subduction est-il unique ?",
807
+ hint:"Comparer avec mรฉtamorphisme classique",
808
+ a:"<strong>Haute Pression / Basse Tempรฉrature</strong> : la plaque s'enfonce vite et reste froide โ€” contraire du mรฉtamorphisme de contact (BT/BP) ou de collision."},
809
+ {id:52, ch:"5", section:"CH.5 โ€” Moteur de la tectonique",
810
+ q:"Quel est le moteur principal du mouvement des plaques ?",
811
+ hint:"Force de tirage",
812
+ a:"La <strong>traction de la plaque subduite</strong> (froide et dense) : elle tire la plaque par gravitรฉ, entraรฎnant les mouvements descendants de la <strong>convection mantellique</strong>."},
813
+ {id:53, ch:"5", section:"CH.5 โ€” Moteur de la tectonique",
814
+ q:"Quel lien y a-t-il entre subduction et convection mantellique ?",
815
+ hint:"Cause-consรฉquence",
816
+ a:"La subduction <strong>entretient</strong> les courants descendants froids de la convection. Les plaques ne sont pas portรฉes passivement : elles <strong>tirent activement</strong> sur le reste de la lithosphรจre."},
817
+ {id:54, ch:"5", section:"CH.5 โ€” Collision",
818
+ q:"Comment se produit une collision continentale ?",
819
+ hint:"Aprรจs quoi ?",
820
+ a:"Aprรจs la <strong>fermeture complรจte d'un ocรฉan</strong> par subduction : les deux marges continentales entrent en contact et ne peuvent plus plonger (croรปte trop lรฉgรจre)."},
821
+ {id:55, ch:"5", section:"CH.5 โ€” Collision",
822
+ q:"Quelles structures gรฉologiques tรฉmoignent d'une collision en surface ?",
823
+ hint:"3 structures",
824
+ a:"<strong>Plis</strong>, <strong>failles inverses</strong> et <strong>nappes de charriage</strong> (grandes masses de roches dรฉplacรฉes horizontalement)."},
825
+ {id:56, ch:"5", section:"CH.5 โ€” Collision",
826
+ q:"Qu'est-ce qu'une racine crustale ? ร€ quelle profondeur descend le Moho ?",
827
+ hint:"Iceberg crustal",
828
+ a:"ร‰paississement de la croรปte vers le bas : le <strong>Moho</strong> peut descendre jusqu'ร  <strong>70 km</strong> (normal : ~35 km). C'est le pendant souterrain des reliefs รฉlevรฉs."},
829
+ {id:57, ch:"5", section:"CH.5 โ€” Collision",
830
+ q:"Quels types de roches mรฉtamorphiques se forment en contexte de collision ?",
831
+ hint:"Roches foliรฉes",
832
+ a:"Roches <strong>foliรฉes</strong> : <strong>micaschistes</strong> (faible ร  modรฉrรฉ mรฉtamorphisme) et <strong>gneiss</strong> (fort mรฉtamorphisme)."},
833
+ {id:58, ch:"5", section:"CH.5 โ€” Marqueurs de subduction",
834
+ q:"Quelle diffรฉrence entre arc insulaire et cordillรจre en subduction ?",
835
+ hint:"Ocรฉanโ€“Ocรฉan vs Ocรฉanโ€“Continent",
836
+ a:"<strong>Arc insulaire</strong> : subduction Ocรฉan/Ocรฉan โ†’ volcanisme en mer. <strong>Cordillรจre</strong> : subduction Ocรฉan/Continent โ†’ volcanisme en bordure de continent."},
837
+ {id:59, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
838
+ q:"Qu'est-ce que le coin mantellique et quel est son rรดle ?",
839
+ hint:"Wedge",
840
+ a:"Portion du <strong>manteau de la plaque chevauchante</strong> entre les deux plaques. Il reรงoit l'<strong>eau libรฉrรฉe</strong> par la plaque subduite โ†’ sa pรฉridotite fond partiellement."},
841
+ {id:60, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
842
+ q:"Risque volcanique caractรฉristique des zones de subduction ?",
843
+ hint:"Nuage mortel",
844
+ a:"Les <strong>nuรฉes ardentes</strong> (coulรฉes pyroclastiques) : mรฉlanges brรปlants de gaz, cendres et dรฉbris ร  trรจs grande vitesse. Liรฉ ร  la viscositรฉ รฉlevรฉe du magma."},
845
+
846
+ // SYNTHรˆSE
847
+ {id:61, ch:"synth", section:"SYNTHรˆSE โ€” Cycle lithosphรฉrique",
848
+ q:"Rรฉsume en 5 รฉtapes le cycle de la lithosphรจre ocรฉanique.",
849
+ hint:"Naissance โ†’ mort",
850
+ a:"<strong>1.</strong> Naissance ร  la <strong>dorsale</strong> (HT, BP). <strong>2.</strong> Hydratation + refroidissement (schistes verts). <strong>3.</strong> Densification โ†’ <strong>subduction</strong>. <strong>4.</strong> Mรฉtamorphisme HP-BT (schiste bleu โ†’ รฉclogite) + libรฉration d'eau. <strong>5.</strong> Dรฉclenchement du <strong>magmatisme</strong> โ†’ croรปte continentale."},
851
+ {id:62, ch:"synth", section:"SYNTHรˆSE โ€” Moteur",
852
+ q:"Quel processus assure la continuitรฉ de la convection mantellique globale ?",
853
+ hint:"Traction + plongement",
854
+ a:"La <strong>traction de la plaque plongeante</strong> (slab pull) : force gravitaire exercรฉe par la plaque froide et dense, moteur principal du cycle convectif global."},
855
+ {id:63, ch:"synth", section:"SYNTHรˆSE โ€” Eau",
856
+ q:"Quel est le rรดle de l'eau dans le cycle de la lithosphรจre ?",
857
+ hint:"Partout : dorsale โ†’ subduction",
858
+ a:"ร€ la <strong>dorsale</strong> : hydratation hydrothermal (schistes verts). En <strong>subduction</strong> : expulsion par mรฉtamorphisme โ†’ abaissement du solidus des pรฉridotites โ†’ magmatisme."},
859
+ {id:64, ch:"synth", section:"SYNTHรˆSE โ€” Cycle lithosphรฉrique",
860
+ q:"Oรน naรฎt et oรน meurt la lithosphรจre ocรฉanique ?",
861
+ hint:"Deux lieux opposรฉs",
862
+ a:"<strong>Naissance</strong> ร  la <strong>dorsale</strong> (accrรฉtion). <strong>Mort</strong> ร  la <strong>fosse de subduction</strong> (rรฉintรฉgration dans le manteau)."},
863
+ {id:65, ch:"synth", section:"SYNTHรˆSE โ€” Croรปte continentale",
864
+ q:"Comment la croรปte continentale est-elle crรฉรฉe via la subduction ?",
865
+ hint:"Eau โ†’ fusion โ†’ granite",
866
+ a:"L'eau libรฉrรฉe dรฉclenche la fusion du coin mantellique โ†’ magma riche en silice โ†’ volcanisme + intrusions โ†’ <strong>granite et diorite</strong> constituant la croรปte continentale."},
867
+
868
+ // Notions complรฉmentaires extraites du texte
869
+ {id:66, ch:"3", section:"CH.3 โ€” Anomalies magnรฉtiques",
870
+ q:"Qu'est-ce qu'une polaritรฉ magnรฉtique normale vs inverse ?",
871
+ hint:"Pรดles Nord et Sud",
872
+ a:"<strong>Normale</strong> : orientation actuelle (pรดle magnรฉtique Nord โ‰ˆ pรดle gรฉographique Nord). <strong>Inverse</strong> : pรดles <strong>Nord et Sud magnรฉtiques inversรฉs</strong>."},
873
+ {id:67, ch:"4", section:"CH.4 โ€” Vieillissement",
874
+ q:"Pourquoi la lithosphรจre ocรฉanique devient-elle plus dense avec le temps ?",
875
+ hint:"Refroidissement + manteau",
876
+ a:"En se refroidissant, elle intรจgre du manteau refroidi par sa base, et les minรฉraux se contractent. La densitรฉ dรฉpasse finalement celle de l'<strong>asthรฉnosphรจre</strong> sous-jacente (~3,3 g/cmยณ)."},
877
+ {id:68, ch:"5", section:"CH.5 โ€” Mรฉtamorphisme de subduction",
878
+ q:"Pourquoi dit-on que le mรฉtamorphisme de subduction est HP-BT ?",
879
+ hint:"Vitesse d'enfouissement",
880
+ a:"La plaque <strong>s'enfonce rapidement</strong> : la pression augmente vite mais la plaque froide ne se rรฉchauffe pas assez rapidement โ†’ conditions de <strong>Haute Pression, Basse Tempรฉrature</strong>."},
881
+ {id:69, ch:"5", section:"CH.5 โ€” Collision",
882
+ q:"Pourquoi la croรปte continentale ne peut-elle pas subduire ?",
883
+ hint:"Densitรฉ vs manteau",
884
+ a:"La croรปte continentale est <strong>moins dense</strong> (~2,7 g/cmยณ) que le manteau (~3,3 g/cmยณ) : elle flotte et ne peut pas plonger. C'est ce qui provoque la <strong>collision</strong>."},
885
+ {id:70, ch:"4", section:"CH.4 โ€” Dorsales rapides",
886
+ q:"Qu'est-ce que le gรฉotherme et le solidus dans le contexte des dorsales ?",
887
+ hint:"Deux courbes sur un graphique P-T",
888
+ a:"Le <strong>gรฉotherme</strong> dรฉcrit la tempรฉrature en fonction de la profondeur. Le <strong>solidus</strong> est la courbe en dessous de laquelle la roche est solide. Quand le gรฉotherme <strong>recoupe le solidus</strong>, la roche fond."},
889
+ {id:71, ch:"3", section:"CH.3 โ€” Rรฉsumรฉ gรฉnรฉral",
890
+ q:"Quels indices prouvent l'expansion ocรฉanique (3 types) ?",
891
+ hint:"Magnรฉtique + sรฉdimentaire + gรฉodรฉsique",
892
+ a:"<strong>1.</strong> Anomalies magnรฉtiques symรฉtriques. <strong>2.</strong> ร‚ge/รฉpaisseur croissants des sรฉdiments. <strong>3.</strong> Mesures GPS (gรฉodรฉsie spatiale)."},
893
+ {id:72, ch:"4", section:"CH.4 โ€” Dorsales rapides",
894
+ q:"Quelle est la composition minรฉralogique initiale d'un gabbro (avant mรฉtamorphisme) ?",
895
+ hint:"2 minรฉraux principaux",
896
+ a:"<strong>Pyroxรจne</strong> (clinopyroxรจne) + <strong>plagioclase</strong> (feldspath calcique). Ces minรฉraux sont remplacรฉs par hornblende puis chlorite + actinote lors du mรฉtamorphisme hydrothermal."},
897
+ {id:73, ch:"5", section:"CH.5 โ€” Marqueurs de subduction",
898
+ q:"Quelle force initie le mouvement de la plaque vers la fosse ?",
899
+ hint:"Densitรฉ + gravitรฉ",
900
+ a:"La plaque ocรฉanique vieillie est <strong>plus dense que l'asthรฉnosphรจre</strong>. La gravitรฉ provoque son <strong>plongement spontanรฉ</strong> (slab pull), qui tire le reste de la plaque."},
901
+ {id:74, ch:"5", section:"CH.5 โ€” Magmatisme de subduction",
902
+ q:"Quelle roche est l'รฉquivalent plutonique de l'andรฉsite ?",
903
+ hint:"Roche ร  grain visible",
904
+ a:"La <strong>diorite</strong> : mรชme composition mais refroidissement lent en profondeur โ†’ texture <strong>grenue</strong> (cristaux visibles ร  l'ล“il nu)."},
905
+ {id:75, ch:"4", section:"CH.4 โ€” Vieillissement",
906
+ q:"ร€ quelle tempรฉrature se place la limite faciรจs amphibolite / faciรจs schiste vert ?",
907
+ hint:"Deux seuils de tempรฉrature",
908
+ a:"<strong>Amphibolite</strong> : > 700ยฐC. <strong>Schiste vert</strong> : entre 300 et 500ยฐC. La transition se fait autour de <strong>500โ€“700ยฐC</strong>."},
909
+ {id:76, ch:"synth", section:"SYNTHรˆSE โ€” Cycle lithosphรฉrique",
910
+ q:"Cite les 3 faciรจs de mรฉtamorphisme qui jalonnent le parcours d'une plaque ocรฉanique du rifting ร  l'arc volcanique.",
911
+ hint:"Dorsale โ†’ plaque รขgรฉe โ†’ subduction",
912
+ a:"<strong>Schiste vert</strong> (hydrothermalisme dorsale et vieillissement). <strong>Schiste bleu</strong> (dรฉbut subduction, HP-BT). <strong>ร‰clogite</strong> (subduction profonde > 50 km)."},
913
+ ];
914
+
915
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
916
+ // STATE
917
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
918
+ let cardStatus = {}; // id -> 0 (bad) | 1 (ok) | 2 (good)
919
+ let currentFilter = 'all';
920
+ let filteredCards = [];
921
+ let currentIndex = 0;
922
+ let isFlipped = false;
923
+ let ratingMade = false;
924
+
925
+ let quizFilter = 'all';
926
+ let quizCards = [];
927
+ let quizIndex = 0;
928
+ let quizScore = 0;
929
+ let quizAnswered = false;
930
+
931
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
932
+ // NAV
933
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
934
+ function showScreen(name) {
935
+ document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
936
+ document.getElementById('screen-' + name).classList.add('active');
937
+ document.querySelectorAll('.tab').forEach((t, i) => {
938
+ const names = ['flashcard','quiz','map','recap'];
939
+ t.classList.toggle('active', names[i] === name);
940
+ });
941
+ if (name === 'map') renderMap();
942
+ if (name === 'recap') renderRecap();
943
+ if (name === 'quiz') initQuiz();
944
+ }
945
+
946
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
947
+ // FILTER
948
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
949
+ function getFiltered(filter) {
950
+ if (filter === 'all') return [...CARDS];
951
+ if (filter === 'weak') return CARDS.filter(c => cardStatus[c.id] === 0 || cardStatus[c.id] === undefined);
952
+ return CARDS.filter(c => c.ch === filter);
953
+ }
954
+
955
+ function setFilter(ch, btn) {
956
+ currentFilter = ch;
957
+ document.querySelectorAll('#screen-flashcard .filter-btn').forEach(b => b.classList.remove('active'));
958
+ btn.classList.add('active');
959
+ filteredCards = getFiltered(ch);
960
+ currentIndex = 0;
961
+ isFlipped = false;
962
+ ratingMade = false;
963
+ renderCard();
964
+ }
965
+
966
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
967
+ // FLASHCARD
968
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
969
+ function renderCard() {
970
+ if (!filteredCards.length) return;
971
+ const c = filteredCards[currentIndex];
972
+ const wrap = document.getElementById('card-wrap');
973
+ wrap.classList.remove('flipped');
974
+ isFlipped = false;
975
+ ratingMade = false;
976
+ document.getElementById('card-controls').style.display = 'none';
977
+
978
+ // Chapter color
979
+ const colors = {3:'var(--ch3)', 4:'var(--ch4)', 5:'var(--ch5)', synth:'var(--synth)'};
980
+ const col = colors[c.ch] || 'var(--muted)';
981
+
982
+ document.getElementById('card-num').textContent = '#' + c.id;
983
+ document.getElementById('card-num2').textContent = '#' + c.id;
984
+ document.getElementById('card-chap').innerHTML = `<span style="color:${col}">${c.section}</span>`;
985
+ document.getElementById('card-chap2').innerHTML = `<span style="color:${col}">${c.section}</span>`;
986
+ document.getElementById('card-q').textContent = c.q;
987
+ document.getElementById('card-hint').textContent = c.hint ? '๐Ÿ’ก ' + c.hint : '';
988
+ document.getElementById('card-a').innerHTML = c.a;
989
+
990
+ // Progress
991
+ const pct = ((currentIndex + 1) / filteredCards.length * 100).toFixed(0);
992
+ document.getElementById('fc-progress').style.width = pct + '%';
993
+ document.getElementById('fc-counter').textContent = (currentIndex + 1) + ' / ' + filteredCards.length;
994
+ updateStats();
995
+ }
996
+
997
+ function flipCard() {
998
+ const wrap = document.getElementById('card-wrap');
999
+ isFlipped = !isFlipped;
1000
+ wrap.classList.toggle('flipped', isFlipped);
1001
+ if (isFlipped && !ratingMade) {
1002
+ document.getElementById('card-controls').style.display = 'grid';
1003
+ }
1004
+ }
1005
+
1006
+ function rateCard(score) {
1007
+ const c = filteredCards[currentIndex];
1008
+ cardStatus[c.id] = score;
1009
+ ratingMade = true;
1010
+ document.getElementById('card-controls').style.display = 'none';
1011
+ updateStats();
1012
+ setTimeout(() => nextCard(), 400);
1013
+ }
1014
+
1015
+ function nextCard() {
1016
+ if (currentIndex < filteredCards.length - 1) {
1017
+ currentIndex++;
1018
+ } else {
1019
+ currentIndex = 0;
1020
+ }
1021
+ renderCard();
1022
+ }
1023
+
1024
+ function prevCard() {
1025
+ if (currentIndex > 0) currentIndex--;
1026
+ else currentIndex = filteredCards.length - 1;
1027
+ renderCard();
1028
+ }
1029
+
1030
+ function shuffleCards() {
1031
+ for (let i = filteredCards.length - 1; i > 0; i--) {
1032
+ const j = Math.floor(Math.random() * (i + 1));
1033
+ [filteredCards[i], filteredCards[j]] = [filteredCards[j], filteredCards[i]];
1034
+ }
1035
+ currentIndex = 0;
1036
+ renderCard();
1037
+ }
1038
+
1039
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1040
+ // STATS
1041
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1042
+ function updateStats() {
1043
+ let good = 0, ok = 0, bad = 0;
1044
+ CARDS.forEach(c => {
1045
+ if (cardStatus[c.id] === 2) good++;
1046
+ else if (cardStatus[c.id] === 1) ok++;
1047
+ else if (cardStatus[c.id] === 0) bad++;
1048
+ });
1049
+ document.getElementById('stat-maรฎtrisรฉ').textContent = good;
1050
+ document.getElementById('stat-appris').textContent = ok;
1051
+ document.getElementById('stat-revoir').textContent = bad;
1052
+ }
1053
+
1054
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1055
+ // QUIZ
1056
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1057
+ const WRONG_POOL = [
1058
+ "Cristallisation fractionnรฉe","Plan de Wadati-Benioff","Dรฉcompression adiabatique",
1059
+ "Fusion partielle","Serpentinisation","Dorsale mรฉdio-atlantique","Glaucophane",
1060
+ "Faille inverse","Faille normale","Basalte en coussin","Jadรฉite","Grenat",
1061
+ "Hornblende","Chlorite","Actinote","Gabbro","Andรฉsite","Diorite","Rhyolite",
1062
+ "Schiste bleu","Schiste vert","ร‰clogite","Slab pull","Coin mantellique",
1063
+ "Chambre magmatique","Isotherme 1300ยฐC","Pyroxรจne","Plagioclase","Serpentine",
1064
+ "Nappe de charriage","Racine crustale","Moho ร  70 km","Solidus","Gรฉotherme",
1065
+ "Arc insulaire","Cordillรจre","Nuรฉe ardente","5 ร  7 km","10 ร  16 cm/an",
1066
+ "1 ร  5 cm/an","700 km de profondeur","50 km de profondeur","500-700ยฐC",
1067
+ "Fumeurs noirs","Micaschiste","Gneiss","Pรฉridotite","Asthรฉnosphรจre"
1068
+ ];
1069
+
1070
+ function buildQuizQuestion(card) {
1071
+ // Extract a short answer from the card answer (first strong)
1072
+ const div = document.createElement('div');
1073
+ div.innerHTML = card.a;
1074
+ const strongs = div.querySelectorAll('strong');
1075
+ let correctAnswer = strongs.length ? strongs[0].textContent : div.textContent.split('.')[0].trim();
1076
+ correctAnswer = correctAnswer.substring(0, 80);
1077
+
1078
+ // Build wrong answers from pool (different from correct)
1079
+ const wrongs = WRONG_POOL.filter(w => !correctAnswer.toLowerCase().includes(w.toLowerCase()));
1080
+ const shuffled = wrongs.sort(() => Math.random() - 0.5).slice(0, 3);
1081
+ const opts = [...shuffled, correctAnswer].sort(() => Math.random() - 0.5);
1082
+ return { question: card.q, correct: correctAnswer, options: opts, explanation: card.a, section: card.section, ch: card.ch };
1083
+ }
1084
+
1085
+ function setQuizFilter(ch, btn) {
1086
+ quizFilter = ch;
1087
+ document.querySelectorAll('#screen-quiz .filter-btn').forEach(b => b.classList.remove('active'));
1088
+ btn.classList.add('active');
1089
+ initQuiz();
1090
+ }
1091
+
1092
+ function initQuiz() {
1093
+ const pool = getFiltered(quizFilter);
1094
+ quizCards = pool.sort(() => Math.random() - 0.5);
1095
+ quizIndex = 0; quizScore = 0; quizAnswered = false;
1096
+ document.getElementById('quiz-result').classList.remove('show');
1097
+ document.getElementById('quiz-container').style.display = '';
1098
+ showQuizQuestion();
1099
+ }
1100
+
1101
+ function showQuizQuestion() {
1102
+ if (quizIndex >= quizCards.length) { showQuizResult(); return; }
1103
+ const card = quizCards[quizIndex];
1104
+ const qdata = buildQuizQuestion(card);
1105
+ const colors = {3:'var(--ch3)', 4:'var(--ch4)', 5:'var(--ch5)', synth:'var(--synth)'};
1106
+ const col = colors[card.ch] || 'var(--muted)';
1107
+
1108
+ document.getElementById('quiz-chapter-tag').innerHTML = `<span style="color:${col}">${qdata.section}</span>`;
1109
+ document.getElementById('quiz-progress-tag').textContent = `${quizIndex + 1} / ${quizCards.length}`;
1110
+ document.getElementById('quiz-q-text').textContent = qdata.question;
1111
+
1112
+ const opts = document.getElementById('quiz-options');
1113
+ opts.innerHTML = '';
1114
+ qdata.options.forEach(opt => {
1115
+ const btn = document.createElement('button');
1116
+ btn.className = 'quiz-opt';
1117
+ btn.textContent = opt;
1118
+ btn.onclick = () => answerQuiz(opt, qdata.correct, qdata.explanation, btn);
1119
+ opts.appendChild(btn);
1120
+ });
1121
+
1122
+ const fb = document.getElementById('quiz-feedback');
1123
+ fb.className = 'quiz-feedback';
1124
+ fb.innerHTML = '';
1125
+ document.getElementById('quiz-next').classList.remove('show');
1126
+ quizAnswered = false;
1127
+ }
1128
+
1129
+ function answerQuiz(chosen, correct, explanation, btn) {
1130
+ if (quizAnswered) return;
1131
+ quizAnswered = true;
1132
+ const isCorrect = chosen === correct;
1133
+ if (isCorrect) { quizScore++; btn.classList.add('correct'); }
1134
+ else { btn.classList.add('wrong'); }
1135
+
1136
+ document.querySelectorAll('.quiz-opt').forEach(b => {
1137
+ b.disabled = true;
1138
+ if (b.textContent === correct) b.classList.add('correct');
1139
+ });
1140
+
1141
+ const fb = document.getElementById('quiz-feedback');
1142
+ if (isCorrect) {
1143
+ fb.className = 'quiz-feedback correct-fb show';
1144
+ fb.innerHTML = `โœ“ Bonne rรฉponse ! <br><small>${explanation}</small>`;
1145
+ } else {
1146
+ fb.className = 'quiz-feedback wrong-fb show';
1147
+ fb.innerHTML = `โœ— La bonne rรฉponse รฉtait : <strong>${correct}</strong><br><small>${explanation}</small>`;
1148
+ }
1149
+ document.getElementById('quiz-next').classList.add('show');
1150
+ }
1151
+
1152
+ function nextQuiz() {
1153
+ quizIndex++;
1154
+ showQuizQuestion();
1155
+ }
1156
+
1157
+ function showQuizResult() {
1158
+ document.getElementById('quiz-container').style.display = 'none';
1159
+ const result = document.getElementById('quiz-result');
1160
+ result.classList.add('show');
1161
+ const pct = Math.round(quizScore / quizCards.length * 100);
1162
+ const colors = pct >= 80 ? 'var(--accent3)' : pct >= 50 ? 'var(--gold)' : 'var(--accent)';
1163
+ document.getElementById('result-score').style.color = colors;
1164
+ document.getElementById('result-score').textContent = pct + '%';
1165
+ let msg = pct >= 80 ? '๐Ÿ† Excellent ! Tu maรฎtrises ce chapitre.' :
1166
+ pct >= 60 ? '๐Ÿ‘ Bien ! Quelques rรฉvisions restent utiles.' :
1167
+ pct >= 40 ? 'โš  Des lacunes ร  combler โ€” retourne aux flashcards !' :
1168
+ '๐Ÿ“š Reprends les flashcards et rรฉvise les notions clรฉs.';
1169
+ document.getElementById('result-msg').textContent = msg;
1170
+ document.getElementById('stat-quiz-score').textContent = pct + '%';
1171
+ }
1172
+
1173
+ function restartQuiz() { initQuiz(); }
1174
+
1175
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1176
+ // REVIEW MAP
1177
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1178
+ function renderMap() {
1179
+ const grid = document.getElementById('review-grid');
1180
+ grid.innerHTML = '';
1181
+ CARDS.forEach(c => {
1182
+ const div = document.createElement('div');
1183
+ div.className = 'review-cell';
1184
+ const s = cardStatus[c.id];
1185
+ if (s === 2) div.classList.add('status-good');
1186
+ else if (s === 1) div.classList.add('status-ok');
1187
+ else if (s === 0) div.classList.add('status-bad');
1188
+ else div.classList.add('status-unseen');
1189
+ div.textContent = c.id;
1190
+ div.title = c.q;
1191
+ div.onclick = () => {
1192
+ const idx = CARDS.findIndex(x => x.id === c.id);
1193
+ setFilter('all', document.querySelector('#screen-flashcard .filter-btn[data-ch="all"]'));
1194
+ currentIndex = idx;
1195
+ renderCard();
1196
+ showScreen('flashcard');
1197
+ };
1198
+ grid.appendChild(div);
1199
+ });
1200
+ }
1201
+
1202
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1203
+ // RECAP
1204
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1205
+ function renderRecap() {
1206
+ const groups = {
1207
+ '3': { label: 'CHAPITRE 3 โ€” Mobilitรฉ horizontale des plaques', cls: 'recap-ch3', color: 'var(--ch3)' },
1208
+ '4': { label: 'CHAPITRE 4 โ€” Dynamique des zones de divergence', cls: 'recap-ch4', color: 'var(--ch4)' },
1209
+ '5': { label: 'CHAPITRE 5 โ€” Dynamique des zones de convergence', cls: 'recap-ch5', color: 'var(--ch5)' },
1210
+ 'synth': { label: 'SYNTHรˆSE โ€” Cycle de la lithosphรจre ocรฉanique', cls: 'recap-synth', color: 'var(--synth)' },
1211
+ };
1212
+ let html = '';
1213
+ Object.entries(groups).forEach(([ch, g]) => {
1214
+ const cards = CARDS.filter(c => c.ch === ch);
1215
+ html += `<div class="recap-section">
1216
+ <div class="recap-title" style="color:${g.color}">${g.label} (${cards.length} notions)</div>`;
1217
+ cards.forEach(c => {
1218
+ html += `<div class="recap-item ${g.cls}">
1219
+ <span class="num">Notion #${c.id} โ€” ${c.section}</span>
1220
+ <strong>${c.q}</strong><br>
1221
+ <span style="color:var(--muted);font-size:0.73rem">${c.a.replace(/<[^>]*>/g, '')}</span>
1222
+ </div>`;
1223
+ });
1224
+ html += `</div>`;
1225
+ });
1226
+ document.getElementById('recap-content').innerHTML = html;
1227
+ }
1228
+
1229
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1230
+ // INIT
1231
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1232
+ filteredCards = [...CARDS];
1233
+ renderCard();
1234
+ updateStats();
1235
+ </script>
1236
+ </body>
1237
+ </html>