# Kilasinaka : kit de templates HTML autonomes

Maquettes HTML/CSS/JS **statiques et autonomes**, extraites du projet Laravel
`platform-web`, destinées à être réintégrées dans une autre technologie
monolithique (JSP, ASPX, PHP…).

Aucun build, aucun routeur JS, aucun backend : chaque fichier `.html` s'ouvre
directement dans un navigateur.

---

## 1. Point de départ : ce que dit le code source

L'analyse du projet Laravel (279 vues Blade, `routes/web.php` et
`routes/modules/*.php`) donne le tableau suivant. **Il n'existe pas un design
system unique dans le projet, mais trois technologies cohabitantes.**

| Espace Blade | Technologie réelle | Repris ici ? |
|---|---|---|
| `resources/views/student/` | **Tailwind 3.4.18** + Alpine 3.13.3 | Oui, référence du skin |
| `resources/views/teacher/` | **Tailwind** + Alpine | Oui, référence du skin |
| `resources/views/manager/` | **Tailwind** + Alpine + JS vanilla | Oui, **renommé `admin/`** |
| `resources/views/website/` | **Tailwind** (Play CDN auto-hébergé) | Oui, identité publique propre |
| `resources/views/admin/`, `myadmin/` | AdminLTE 3 + Bootstrap 4 | Non, hors périmètre |
| `resources/views/school/`, `logistic/`, `myspace/`, `dev/` | AdminLTE / Bootstrap | Non, hors périmètre |

**Décision métier appliquée :** l'espace `manager` est abandonné. Son squelette
et ses pages sont repris **à l'identique** sous le nom `admin`, de sorte
qu'Admin, Teacher et Student partagent exactement le même skin. L'ancien espace
`resources/views/admin/` (AdminLTE) n'est donc **pas** la référence : il ne
contient aucune classe Tailwind.

Mesure objective de cette séparation : 638 / 434 / 630 occurrences de `slate-*`
dans `student/`, `teacher/`, `manager/`, contre **0** dans `admin/` et
**0** dans `website/`.

---

## 2. Le design system extrait (rien n'a été inventé)

### 2.1 Couleurs

La palette `brand` de `tailwind.config.js` **coïncide exactement** avec la
« Charte Kilasinaka » exposée par la préproduction (bloc `:root` de
https://preprod.kilasinaka.mg, annotée « alignée sur kilasinaka.mg ») :

| Charte preprod | Rôle | Échelle `brand` |
|---|---|---|
| `--brand-tint` `#eff6ff` | surfaces très claires | `brand-50` |
| `--brand-light` `#2563eb` | accents | `brand-600` |
| `--brand` `#1d4ed8` | CTA primaire | `brand-700` |
| `--navy` `#1e3a8a` | couleur de marque, titres | `brand-900` |

Deux rôles de la charte manquaient au `tailwind.config.js` du projet : ils ont
été **ajoutés** dans `shared/js/tailwind.config.js`, en échelles complètes :
`accent` (teal, `--accent #0d9488`) pour la progression et les leçons validées,
`gold` (jaune, `--gold #ca8a04`) pour les quiz et les mises en avant.

L'échelle `brand` elle-même est inchangée : c'est la palette Tailwind `blue` :

```
50 #eff6ff · 100 #dbeafe · 200 #bfdbfe · 300 #93c5fd · 400 #60a5fa
500 #3b82f6 · 600 #2563eb · 700 #1d4ed8 · 800 #1e40af · 900 #1e3a8a
```

Rôles sémantiques relevés dans le code (nombre d'occurrences à l'appui) :

| Rôle | Classes réellement employées |
|---|---|
| Fond de page | `bg-slate-50` (html + `main`) |
| Surface / carte | `bg-white` + `border border-slate-200` + `rounded-2xl` + `shadow-sm` |
| Bordure par défaut | `border-slate-200` (246×) · subtile `border-slate-100` (71×) |
| Texte principal | `text-slate-800` (201×) / `text-slate-900` (35×) |
| Texte secondaire | `text-slate-600` (121×) / `text-slate-700` (69×) |
| Texte discret | `text-slate-500` (283×) · icônes `text-slate-400` (187×) |
| Action primaire | `bg-brand-600 text-white hover:bg-brand-700 shadow-sm` |
| CTA appuyé | `+ shadow-lg shadow-brand-200` |
| État actif de menu | `bg-brand-50 text-brand-700 shadow-sm` |
| Anneau de focus | `focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500` |
| Succès | `green` / `emerald` · `bg-green-100 text-green-700` |
| Avertissement | `amber` · `bg-amber-100 text-amber-700` |
| Danger | `rose` (+ `red` pour les compteurs) · `bg-rose-100 text-rose-700` |
| Voile de modale | `bg-slate-900/50` · `bg-slate-900/60 backdrop-blur-sm` |
| Dégradé signature | `bg-gradient-to-br from-indigo-600 to-brand-600` |

### 2.2 Typographie

- **Inter** 300/400/500/600/700 via Google Fonts ; `fontFamily.sans` dans la config.
- Tailles dominantes : `text-sm` (518×) et `text-xs` (412×), soit ~75 % des usages.
- `text-[10px]` (111×) est le jeton de micro-libellé et de badge.
- Graisse par défaut des titres **et** des labels : `font-bold` (574×).
- Recette d'« eyebrow » : `text-[11px] font-bold uppercase tracking-wide(r) text-slate-400`.
- `font-normal`, `font-light`, `font-extrabold` ne sont **pas** utilisés dans les
  trois espaces connectés.

### 2.3 Formes, ombres, transitions

- Rayons : `rounded-lg` (309×, boutons/inputs/menu) · `rounded-xl` (173×, panneaux)
  · `rounded-full` (165×) · `rounded-2xl` (106×, cartes et modales).
  Aucun `rounded-3xl`, aucun `rounded-none`.
- Ombres : `shadow-sm` (121×) · `shadow-xl` (26×, dropdowns/sidebar/modales)
  · `shadow-lg` (23×, CTA) · `shadow-brand-200` (13×). Aucun `shadow-2xl`.
- Transitions : `transition` nue (261×), sans durée explicite, dans la majorité
  des cas ; `duration-300` réservé au menu latéral.
- Breakpoints réellement utilisés **dans le source Blade** : `sm:` (92×),
  `md:` (112×), `lg:` (54×). `xl:` quasi absent, `2xl:` absent.
  Le kit s'en écarte volontairement : les barres hautes et les menus basculent
  tous à `lg:` (1024 px) au lieu de `md:` (768 px), parce qu'à 768 px les barres
  du source demandaient jusqu'à 903 px de large. Décompte du kit :
  `sm:` 872×, `lg:` 765×, `md:` 335×, `xl:` 82×, `2xl:` 0.

### 2.4 Icônes

**Font Awesome 6.5.2** (cdnjs), préfixes `fa-solid` (338×) et `fa-regular` (99×).
Aucune classe de dimension FA : la taille passe toujours par une classe Tailwind
`text-*` posée sur le `<i>`. Aucun SVG inline, aucune autre librairie d'icônes.

---

## 2 bis. Le POC : une plateforme d'apprentissage en ligne

Le kit ne se limite plus à reproduire l'existant : il matérialise la cible
produit : **une plateforme d'apprentissage en ligne**, et non un logiciel de
gestion de classe. Le modèle de contenu est repris de la préproduction réelle
**https://preprod.kilasinaka.mg** (page « Référentiel des leçons »),
dont le bloc `:root` fournit aussi la charte officielle.

### Le modèle de contenu (champs réels de la preprod)

```
Matière  ->  Module (niveau)  ->  Chapitre  ->  Leçon
                                                 type : video | cours | quiz
```

Chaque leçon porte les champs du référentiel de production :
`level`, `chapter_number`, `chapter_title`, `lesson_number`, `title`,
`objective`, `learning_objectives`, `key_terms`, `key_takeaways`,
`duree_heures`, `statut` (« Rédigée » / « À rédiger »).

Volume livré : **6 modules · 15 chapitres · 52 leçons · 15 quiz · 24 apprenants**
(`shared/js/learning-data.js`).

### Le parcours apprenant

| Écran | Rôle |
|---|---|
| `student/page/home.html` | Reprise de parcours, indicateurs, activité 7 jours, à faire ensuite |
| `student/page/parcours.html` | Mes modules, avancement, filtres par état |
| `student/page/catalogue.html` | Tous les modules, filtres matière / niveau |
| `student/page/module.html` | Sommaire d'un module : chapitres, leçons, états |
| `student/page/lecon.html` | **Écran central** : sommaire + vidéo + cours + objectifs + termes-clés + points-clés + navigation |
| `student/page/quiz.html` | Quiz de fin de chapitre, correction commentée, score |
| `student/page/certificats.html` · `certificat.html` | Certificats débloqués, attestation imprimable |

### La logique de suivi (`shared/js/progression.js`)

C'est la pièce qui rend le POC fonctionnel : elle **mémorise** ce que fait
l'apprenant et **agrège** cette mémoire en indicateurs, pour les trois espaces.

```js
KILASI.progress.terminer(leconId, minutes)        // valider une leçon
KILASI.progress.enregistrerQuiz(id, score, total, seuil)
KILASI.progress.module(moduleId)   // { faites, total, pct, heures, quizMoyen, prochaine }
KILASI.progress.global()           // { pct, leconsFaites, heures, modulesTermines, serie }
KILASI.progress.reprendre()        // la prochaine leçon à faire
KILASI.progress.certificat(moduleId)
KILASI.progress.cohorte()          // cohorte simulée + apprenant courant réel
KILASI.parcours.lecon(id)          // navigation : module, chapitre, précédente, suivante
```

**Persistance** : `localStorage` (clé `kilasinaka.progression.v1`), avec repli
automatique sur `window.name` puis sur la mémoire, afin que la démonstration
fonctionne aussi dans les navigateurs qui bloquent `localStorage` sur `file://`.

**Cohérence entre espaces** : la progression réelle de l'apprenant remonte
telle quelle dans `teacher/page/suivi.html` (ligne « Tsiry (vous) ») et dans
`admin/page/apprentissage.html`. Validez une leçon côté apprenant, ouvrez l'espace
enseignant : le chiffre a bougé. C'est la même source de vérité.



### L'offre gratuite de l'espace public

La préproduction publie son « Référentiel des leçons » **sans authentification**.
Le kit reprend ce parti : une partie du contenu est consultable librement.

| Écran public | Rôle |
|---|---|
| `public/cours.html` | Catalogue en accès libre : filtres « Toutes classes » / « Toutes matières », regroupement par module, badge « Rédigée », action « Ouvrir → » |
| `public/lecon.html` | Lecture d'une leçon sans compte : vidéo, cours rédigé, **simulation 3D**, objectifs, termes-clés, points-clés |

**Ce qui est gratuit / ce qui demande un compte**

| Gratuit, sans compte | Compte requis (gratuit lui aussi) |
|---|---|
| Lecture de la leçon | Quiz corrigé |
| Lecteur vidéo | Suivi de progression |
| Simulations 3D interactives | Certificat de module |
| Objectifs, termes-clés, points-clés | Reprise de parcours |

Le périmètre est porté par le champ `acces` du référentiel : **tout le
chapitre 1 de chaque module, plus toute leçon portant une simulation**, à
l'exception des quiz. Un quiz note et alimente la progression : il demande donc
toujours un compte. Soit **16 leçons sur 52**, dont 6 vidéos et les 2
simulations. Les leçons réservées apparaissent verrouillées dans le catalogue,
et `lecon.html` refuse d'en afficher le contenu.

> **Point de sécurité, à traiter à l'intégration.** Le filtrage est ici côté
> client : le JSON du référentiel contient tout, seule l'interface masque. En
> production, l'API publique ne doit renvoyer **que** les leçons libres.

L'espace public conserve son identité propre (blue-700 / yellow-400 / gray,
reprise de `website/index.blade.php`). Il ne charge ni `shared/css/theme.css`
ni la config Tailwind des espaces connectés : **la seule ressource partagée est
`shared/js/learning-data.js`**, pour qu'il n'existe qu'une version du contenu.

### Outils repris du dossier `amelioration/`

Trois ressources fournies avec le projet ont été intégrées au kit plutôt que
laissées de côté.

| Source | Devient | Rôle |
|---|---|---|
| `editor_responsive.html` | `teacher/page/editeur.html` | **Éditeur de leçons** : c'est désormais l'entrée « Créer une leçon » du menu enseignant |
| `code2.html` | `shared/simulations/chute-libre.html` | Simulation 3D intégrée à la leçon « Poids et masse » |
| `choc-mou-3d.html` | `shared/simulations/choc-mou.html` | Simulation 3D intégrée à la leçon « Énergie cinétique » |

**L'éditeur porte exactement les champs du référentiel** (`level`, `subject`,
`chapter_num`, `chapter_title`, `lesson_num`, `title`, `summary`, `objectives`,
`keyterms`, `takeaways`) : c'est bien l'outil qui produit ce que l'apprenant
lit dans `student/page/lecon.html`, et son export JSON a la forme des entrées de
`KILASI.learning.modules[].chapitres[].lecons[]`.

#### Harmonisation appliquée aux trois fichiers

À l'import, aucun des trois ne suivait les conventions du kit. Écarts corrigés :

| Écart constaté | Correction |
|---|---|
| `shared/js/tailwind.config.js` non chargé | ajouté : les jetons `brand` / `accent` / `gold` sont disponibles |
| `shared/css/theme.css` non chargé | ajoutée : `custom-scroll`, `fade-in`, styles d'impression |
| 69 emojis servaient d'icônes | convertis en **Font Awesome**, seule iconographie du kit |
| Simulations en thème sombre intégral | passées au thème clair du kit ; **seul le rendu 3D reste sombre** |
| Accents `sky-*` des simulations | remplacés par `brand-*` |
| Nuances 200/300/400 calibrées pour fond sombre | descendues d'un cran, lisibles sur blanc |
| Éditeur : ancienne charte « papier / or » (`--paper #faf7f2`, `--navy #21295c`, `--gold #c8932e`) | 12 variables CSS remappées sur la charte actuelle |
| Éditeur : Fraunces + Work Sans | passés à **Inter** |
| Éditeur : pas de retour vers l'espace | barre de retour ajoutée |

**Aucune fonctionnalité n'a été retirée.**

Deux points méritent d'être connus :

- **L'en-tête des simulations s'efface quand elles sont embarquées.** Une
  simulation sert dans deux contextes : en `<iframe>` dans une leçon, et en
  plein écran. L'en-tête du kit (logo, retour) n'a de sens que dans le second :
  un test `window.self !== window.top` le retire à l'exécution. Pas de double
  chrome dans la leçon, pas de page nue en plein écran.
- **Les pictogrammes de la palette de blocs de l'éditeur sont conservés**
  (`¶`, `"`, `♪`, `★`, `✦`). Ce sont des mnémoniques typographiques cohérents
  entre eux et accompagnés d'un libellé texte ; les convertir en Font Awesome
  aurait cassé un système interne sans rien gagner. Les glyphes qui servaient
  de **contrôles** (flèches, croix, crayon) sont, eux, passés en Font Awesome.

**Le bloc « simulation »** est un nouveau type de contenu de leçon :

```json
"simulation": { "slug": "chute-libre", "titre": "Chute libre", "description": "…" }
```

Le slug pointe vers `shared/simulations/{slug}.html`, intégré en `<iframe>` dans
la page de leçon, avec un bouton « Plein écran ». L'isolation en iframe est
délibérée : **Three.js reste confiné à la simulation** et n'est jamais chargé
par les écrans du kit.

### Contrepartie enseignante et administration

| Écran | Rôle |
|---|---|
| `teacher/page/suivi.html` | Cohorte, avancement par module, alertes de décrochage |
| `admin/page/catalogue.html` | Administration des modules (CRUD sur le référentiel) |
| `admin/page/apprentissage.html` | Complétion par module, volume par matière, modules à surveiller |

### Menus alignés

Les trois espaces partagent désormais la **même structure de menu latéral** :
sections en majuscules, mêmes classes d'état actif, même bloc annexe en pied.

| Student | Teacher | Admin |
|---|---|---|
| Apprentissage | Pilotage | Pilotage |
| Mes ressources | Mes contenus | Contenus |
| Suivi | Organisation | Utilisateurs |
| Échanges | Échanges | Finance · Configuration |

---

## 3. Arborescence livrée

La structure est un **miroir adapté** de l'arborescence Blade.

```
template_v2/
├── index.html                  Sommaire de navigation du kit (ajout maquette)
├── README.md                   Ce document
├── robots.txt                  Exploration : espaces authentifiés exclus
├── sitemap.xml                 Plan de site (espace public uniquement)
│
├── shared/                     Ce que le projet mutualisait côté serveur
│   ├── layout-student.html     Shell de référence  <- student/layout.blade.php
│   ├── layout-teacher.html     Shell de référence  <- teacher/layout.blade.php
│   ├── layout-admin.html       Shell de référence  <- manager/layout.blade.php
│   ├── assets/img/             Logo officiel, favicons, site.webmanifest, images
│   ├── simulations/            chute-libre · choc-mou (iframes des leçons)
│   ├── css/theme.css           <- bloc <style> de static/head.blade.php (verbatim)
│   └── js/
│       ├── tailwind.config.js  <- tailwind.config.js + charte preprod
│       ├── app.js              Transcription vanilla d'Alpine + moteur de tableau
│       ├── mock-data.js        Jeux de données de gestion (≥ 20 lignes/tableau)
│       ├── learning-data.js    Référentiel pédagogique (52 leçons, 15 quiz)
│       └── progression.js      Moteur de suivi de l'apprenant
│
├── public/                     <- resources/views/website/
│   ├── index.html                 route website.home  (GET /)
│   ├── login.html                 route website.login (GET /login)
│   ├── inscription.html           POC · création de compte
│   ├── cours.html                 POC · catalogue en accès libre
│   ├── lecon.html                 POC · leçon en accès libre (?l=IDENTIFIANT)
│   ├── mentions-legales.html · cgu.html · confidentialite.html
│   └── 404.html · 403.html · 500.html
│
├── student/                    <- resources/views/student/
│   ├── login.html · forget-pass.html · reset-password.html · call-support.html
│   └── page/  PARCOURS  parcours · catalogue · module · lecon · quiz ·
│                        certificats · certificat
│              home · cours · subject · chapter · exercises · revisions ·
│              supports · progress · bookmarks · calendrier · events · forum ·
│              messages · notifications · search · new-course · settings
│
├── teacher/                    <- resources/views/teacher/
│   ├── login.html · forget-pass.html · call-support.html
│   ├── auth/  forgot-password.html · reset-password.html
│   └── page/  dashboard · suivi · editeur · classes · corrections · planning ·
│              new-course · messaging · messages · forum · notifications · settings
│
└── admin/                      <- resources/views/manager/  (espace abandonné, repris)
    ├── login.html · forget-pass.html · reset-password.html · call-support.html
    └── page/  PILOTAGE   dashboard · apprentissage
                CONTENUS  catalogue · acces · contenu-public · medias
                COMPTES   eleves · fiche-eleve · professeurs · fiche-professeur ·
                          classes · fiche-classe · roles · suivi-eleve
                FINANCE   paiement · salaires · frais
                RÉGLAGES  sessions · navigation · journal · configuration
                COMPTE    profil · notifications · messagerie
```

L'espace administration porte un **menu unique de 18 entrées** en 5 groupes,
identique sur les 24 pages. Les vues de détail (`fiche-*.html`) surlignent leur
page mère ; `profil`, `notifications` et `messagerie` n'activent aucune entrée
car on y accède par le menu du compte, dans la barre supérieure.

Correspondance des URL d'origine : `/manager/eleves` → `admin/page/eleves.html`,
`/student/cours` → `student/page/cours.html`, etc. Chaque fichier rappelle sa
route d'origine dans son en-tête.

---

## 4. Le shell partagé

Les trois `layout.blade.php` (student, teacher, manager) sont **le même fichier**
au préfixe d'`@include` près. Sa structure, reproduite à l'identique :

```
html.h-full.bg-slate-50
└─ body.h-full.antialiased.text-slate-900.overflow-hidden
   └─ div.h-full.flex.flex-col
      ├─ nav      bg-white border-b border-slate-200 h-16 flex items-center
      │           justify-between px-4 lg:px-6 z-50 flex-shrink-0 relative
      └─ div.flex.flex-1.overflow-hidden.relative
         ├─ #sidebar-overlay   fixed inset-0 bg-slate-900/60 z-30 lg:hidden backdrop-blur-sm
         ├─ aside#sidebar      absolute lg:static inset-y-0 left-0 w-72 bg-white
         │                     border-r border-slate-200 … w-72
         └─ main   flex-1 flex flex-col overflow-y-auto custom-scroll bg-slate-50
            ├─ section  flex-1 px-4 sm:px-8 py-6 max-w-7xl mx-auto w-full
            └─ footer   mt-auto border-t border-slate-200 bg-white py-8
```

Seul `main` défile ; `body` est en `overflow-hidden` et la colonne interne de
l'`aside` défile indépendamment.

### Écarts entre espaces : assumés, car présents dans le source

| Élément | student | teacher | admin (ex-manager) |
|---|---|---|---|
| Item de menu actif | `bg-brand-50 text-brand-700 shadow-sm` | `… border border-brand-100` | `… shadow-sm` |
| Structure d'item | `justify-between` + badge | `justify-between` + badge | `gap-3`, jamais de badge |
| Badge de menu | `bg-red-100 text-red-600 text-[10px]` | `bg-rose-100 text-rose-600 text-[11px]` | absent |
| Zone droite du nav | `gap-2 sm:gap-6` | `gap-2 sm:gap-4` | `gap-2 sm:gap-6` |
| Sélecteur central | classes (`fa-filter`, `w-40`) | classes (`fa-filter`, `w-44`) | sessions (`fa-calendar`, `w-44`) |
| Badge messagerie | `bg-brand-600`, valeur 4 | `bg-emerald-500`, valeur 3 | `bg-brand-600`, valeur 3 |
| Badge notifications | pastille `bg-red-500 animate-pulse` | compteur `bg-rose-500` (5) | pastille `bg-red-500 animate-pulse` |
| Panneau notifications | `w-80` | `w-72 text-xs` | `w-80`, 2 items |
| Logo | 1 ligne | 2 lignes + « espace enseignant » | 1 ligne |
| Sidebar : conteneur | `space-y-8` | `space-y-8` | `space-y-6` |
| Sidebar : blocs annexes | Mes Matières + Conseil du jour | Astuce pédagogique | Départements + Nouveauté |
| Déconnexion | `<a>` | `<form method="GET">` + submit | `<a>` |
| Sous-titre profil | « Connecté » | « Connecté » | « Directeur » |

Ces écarts sont **reproduits tels quels**. Les uniformiser serait une décision
produit, pas une décision d'intégration.

---

## 5. Le JavaScript

Alpine.js 3.13.3 (CDN) a été transcrit en **vanilla ES6+** dans
`shared/js/app.js`. Le menu latéral de `manager` était déjà en vanilla : c'est
cette mécanique qui a été généralisée aux trois espaces.

| Source Blade | Équivalent livré |
|---|---|
| `x-data="{ sidebarOpen: false }"` + `:class` | `#sidebar` / `#sidebar-overlay` / `[data-toggle-sidebar]` |
| `x-data="{ classOpen, notifOpen, userOpen }"` + `@click.outside` | `[data-dropdown]` > `[data-dropdown-trigger]` + `[data-dropdown-menu]` |
| `x-text="selectedClass"` | `[data-dropdown-label]` + `[data-dropdown-value]` |
| `x-data="{ activeTab: 'overview' }"` | `[data-tabs]` > `[data-tab]` / `[data-tab-panel]` |
| `data-tab` / `data-tab-content` (fiche élève) | idem, styles d'onglet paramétrables |
| `data-modal-target` / `data-modal-close` | `KILASI.modal` (mécanique `hidden` ⇄ `flex` conservée) |
| `onsubmit="return confirm(…)"` | modale de confirmation à la charte + retrait animé |

### API exposée

```js
KILASI.toast(message, 'success'|'error'|'info'|'warning')
KILASI.modal.open(id) / KILASI.modal.close(id)
KILASI.utils.ar(150000)            // -> "150 000 Ar"
KILASI.table.init({ key, data, pageSize, searchKeys, filters, row })
KILASI.table.get(key)              // .create() .update() .remove() .selectedIds()
KILASI.data.*                      // jeux mockés
```

Attributs `data-*` reconnus : `data-toggle-sidebar`, `data-dropdown*`,
`data-tabs`/`data-tab`/`data-tab-panel`/`data-tab-goto`, `data-modal-target`/
`data-modal-close`, `data-delete`/`data-id`/`data-label`, `data-edit`/
`data-create`, `data-crud-form`, `data-form*`, `data-table*`, `data-check-all`/
`data-check-row`, `data-bulk-bar`, `data-sort`, `data-accordion`,
`data-file-preview`, `data-toggle-password`, `data-demo`.

---

## 6. Données mockées

`shared/js/mock-data.js` : persistance **en mémoire** (un rechargement
réinitialise tout). Les clés reprennent le vocabulaire métier des modèles
Laravel.

| Jeu | Lignes | Colonnes calquées sur |
|---|---|---|
| `eleves` | 26 | `manager/page/gestion-eleve.blade.php` |
| `professeurs` | 22 | `manager/page/professeurs.blade.php` |
| `classes` | 20 | `manager/page/classes.blade.php` |
| `paiements` | 28 | `manager/page/paiement.blade.php` |
| `salaires` | 22 | `manager/page/salaires.blade.php` |
| `frais` | 20 | `manager/page/frais.blade.php` |
| `sessions` | 20 | `manager/page/sessions.blade.php` |
| `suivi` | 24 | `manager/page/suivi-eleve.blade.php` + fiche-classe |
| `copies` | 24 | `teacher/page/corrections.blade.php` |
| `classesProf` | 20 | `teacher/page/classes.blade.php` |
| `seances` | 24 | `teacher/page/planning.blade.php` |
| `matieresEleve` | 9 | `student/page/cours.blade.php` |

---

## 7. Notes pour l'intégrateur (JSP / ASPX / PHP)

1. **Le shell devient un master page / include.** Seul le bloc entre
   `<!-- ==== CONTENU ==== -->` et `<!-- ==== /CONTENU ==== -->` change d'une
   page à l'autre. Les trois fichiers `shared/layout-*.html` sont là pour ça.
2. **Le rendu des lignes de tableau** est isolé dans une fonction
   `ligneXxx(item)` en bas de chaque page de liste : c'est exactement le
   balisage à recopier dans la boucle serveur (`<c:forEach>`, `Repeater`,
   `foreach`).
3. **Pagination, recherche et filtres** sont côté client dans la maquette. Côté
   serveur ils redeviennent des paramètres de requête. Le projet Laravel
   paginait à **10 lignes** (`nbrLigne = 10`) : la maquette respecte cette valeur.
4. **Les blocs marqués `[SERVEUR]`** dans `app.js` sont ceux à remplacer ; les
   blocs `[UI]` (menu, dropdowns, onglets, modales) restent côté client.
5. **Les formulaires ne soumettent rien** : `preventDefault()` puis retour visuel.
   Les `name` des champs correspondent aux clés des jeux de données, donc aux
   colonnes attendues côté serveur.
6. **Aucune protection CSRF** n'est présente (maquette statique). Les points
   d'ancrage `@csrf` du projet sont signalés en commentaire là où ils existaient.

### Dépendances externes

| Ressource | Version | Pourquoi |
|---|---|---|
| Tailwind Play CDN | 3.4.16 | remplace `@vite` + le build PostCSS |
| Font Awesome | 6.5.2 | **version exacte du projet** : toute l'UI en dépend |
| Google Fonts Inter | 300-700 | police déclarée dans `tailwind.config.js` |
| ui-avatars.com | - | avatars de personnes (déjà utilisé par le projet) |

Trois dépendances supplémentaires sont **confinées à des pages précises**, jamais
chargées par le reste du kit : **Three.js r128** dans les deux simulations,
**KaTeX 0.16.11** et **SheetJS xlsx 0.18.5** dans l'éditeur de leçons.

**Assets locaux** : `shared/assets/img/` contient les fichiers réels du projet :
`kilasinaka-logo.png` (le logo officiel : livre ouvert vert, nuage rouge, accent
bleu), `logo.svg`, `favicon.ico`, `favicon-16/32`, `apple-touch-icon`,
`android-chrome-192/512`, `site.webmanifest`, plus `hero-education.jpg` et
`main-pc.png`. Le logo et le favicon sont posés sur **toutes** les pages ; plus
aucune n'utilise le logo de substitution ui-avatars.

> La consigne « aucune dépendance hors Tailwind CDN » entre en conflit avec la
> consigne de fidélité : sans Font Awesome ni Inter, le rendu n'est plus
> comparable à l'original (toute l'iconographie disparaît). Ces deux ressources
> sont donc conservées, **strictement dans les versions du projet**. Pour un
> déploiement hors ligne, les trois se remplacent par des fichiers locaux sans
> toucher au balisage.

---

## 8. Anomalies relevées dans le projet source

Ces points ont été rencontrés pendant l'extraction. Ils sont documentés ici et
dans l'en-tête des fichiers concernés ; **aucun n'a été « corrigé » en silence**.

| # | Anomalie | Fichier(s) | Traitement dans le kit |
|---|---|---|---|
| 1 | `animate-fadeIn` (7×) n'existe ni dans Tailwind ni dans la config : aucune animation | nav des 3 espaces | Classe **conservée telle quelle** (rendu identique à l'original) |
| 2 | `tailwind-layout.blade.php` redéfinit `brand` en **indigo** (#4f46e5) après `@vite` : deux vérités de marque coexistent | `resources/views/tailwind-layout.blade.php` | Le kit retient la config officielle (bleu) |
| 3 | Classes construites dynamiquement (`bg-{{ $sub['color'] }}-500`) invisibles pour le JIT | sidebar, `default-main`, `cours` | Écrites **en dur** dans le HTML |
| 4 | `@include('student.events')` pointe vers une vue inexistante → page cassée | `student/page/calendrier.blade.php` | Page reconstruite, anomalie documentée |
| 5 | Lien vers `route('student.call-support')` : route inexistante | `student/forget-pass.blade.php` | Lien vers `call-support.html` |
| 6 | Classes **Bootstrap** (`fw-bold`, `form-control`, `min-vh-100`…) alors que Bootstrap n'est pas chargé → pages cassées | `teacher/auth/forgot-password.blade.php`, `reset-password.blade.php` | Transposées en Tailwind au skin de l'espace |
| 7 | Routes `manager.new-course`, `manager.supports`, `manager.subject` appelées mais non déclarées | `manager/static/default-main.blade.php` | Liens réorientés vers des pages existantes |
| 8 | `@extends('website.page')` : `website/page.blade.php` n'existe pas ; les partials `nav`, `nav.v1`, `nav.v2`, `footer`, `category` ne sont inclus nulle part ; code e-commerce (« E-Shop / Tsenanaka ») mort et cassé | `website/acceuil.blade.php`, `website/static/*`, `website/page/product-list.blade.php` | **Non repris.** Seules `index` et `login`, les 2 vues réellement routées, sont livrées |
| 9 | 5 entrées de menu sur 6 (student) et 4 sur 7 (teacher) commentées en PHP | `*/static/side-menu.blade.php` | **Réactivées** : elles pointent vers des routes et des vues qui existent. Décision à confirmer côté produit |
| 10 | `PageViewAdmin` génère du markup **Bootstrap/AdminLTE** (`btn btn-primary btn-sm`, `form-group`) injecté dans des pages Tailwind via `{!! $html[…] !!}` : incohérence visuelle structurelle | `app/Services/View/PageViewAdmin*.php`, `manager/page/*` | Le kit explicite le **markup Tailwind cible** ; c'est le contrat visuel à viser |
| 11 | Aucune pagination `<ul class="pagination">` n'est générée : ce sont des contrôles de formulaire postés sur l'URL courante | `PageViewAdmin` | Pagination reconstruite dans la charte du projet |
| 12 | Attribut `onclick="()"` émis quand aucun JS n'est configuré (JS invalide, silencieux) | `PageViewAdmin` v1 et v2 | Non reproduit |
| 13 | `manager/static/default-main.blade.php` est une copie de la version student (3 lignes changées) | - | Signalé ; l'admin reçoit un tableau de bord de pilotage réel |

---

## 9. Vérification effectuée

Le kit a été contrôlé automatiquement, page par page, en **Chrome headless**
(exécution réelle du JavaScript, styles Tailwind réellement générés), les pages
étant ouvertes en `file://` sans aucun serveur.

```bash
google-chrome --headless --disable-gpu --virtual-time-budget=9000 \
  --screenshot=out.png --window-size=1440,1100 \
  "file:///chemin/vers/template_v2/admin/page/eleves.html"
```

**Rendu et exécution**

| Contrôle | Portée | Résultat |
|---|---|---|
| Erreurs console JavaScript | 87 pages rendues | **0** |
| Scripts valides (`node --check`) | tous les blocs `<script>` + les 5 fichiers `shared/js/` | **0 erreur** |
| Corps de tableau peuplés par le JS | tous | **0 tableau vide** |
| Parcours complet de bout en bout | 1 module (13 leçons + 3 quiz) | **100 % · certificat délivré** |

**Structure du document**

| Contrôle | Portée | Résultat |
|---|---|---|
| Équilibre des délimiteurs `<!--` et `-->` | 90 fichiers | **0 défaut** |
| Exactement un `<h1>` **visible** dans le DOM rendu | 87 pages | **0 écart** |
| Titre de la forme « Intitulé \| Kilasinaka » | 90 fichiers | **90 / 90** |
| `lang="fr"` et `meta name="viewport"` | 90 fichiers | **90 / 90** |

**Navigation**

| Contrôle | Portée | Résultat |
|---|---|---|
| Liens internes cassés | 90 fichiers, hors commentaires | **0** |
| Ancres internes non résolues | 90 fichiers, hors commentaires | **0** |
| Libellé de menu contre titre de la page cible | 40 entrées, 3 espaces | **0 divergence** |
| Sommaire `index.html` contre le disque | 84 entrées | **0 écart** |

**Accessibilité**

| Contrôle | Portée | Résultat |
|---|---|---|
| Lien « Aller au contenu » et sa cible `#contenu` | 90 fichiers | **90 / 90** |
| `meta name="description"` | 90 fichiers | **90 / 90** |
| `<noscript>` | 90 fichiers | **90 / 90** |
| `aria-label` sur le menu latéral | 90 fichiers | **90 / 90** |
| Champs de formulaire sans nom accessible | 90 fichiers | **0** |
| Boutons, liens et images sans nom accessible | 90 fichiers | **0** |
| Lien vers `site.webmanifest` | 90 fichiers | 88, les 2 simulations étant des iframes |

**Responsivité**, mesurée sur `scrollWidth` comparé à `innerWidth`

| Largeur | Portée | Résultat |
|---|---|---|
| 375 px (mesuré dans un cadre) | 87 pages | **0 débordement** |
| 768 px | 87 pages | **0 débordement** |
| 1280 px | 87 pages | **0 débordement** |

**Dépendances et rédaction**

| Contrôle | Portée | Résultat |
|---|---|---|
| Librairies interdites (Alpine, jQuery, Chart.js, Bootstrap, Vue, Moment) | 90 fichiers | **0** |
| Hôtes externes | 90 fichiers | 6, détaillés ci-dessous |
| Cadratins et demi-cadratins | tous les fichiers | **0** |

Les six hôtes externes, et ce que chacun sert :

| Hôte | Ressource | Où |
|---|---|---|
| `cdn.tailwindcss.com` | Tailwind Play CDN 3.4.16 | 90 pages |
| `cdnjs.cloudflare.com` | Font Awesome 6.5.2 · Three.js r128 | 90 pages · les 2 simulations |
| `fonts.googleapis.com` / `fonts.gstatic.com` | Inter (espaces connectés), Poppins (public) | 90 pages |
| `cdn.jsdelivr.net` | KaTeX 0.16.11 · SheetJS 0.18.5 | éditeur de leçons · exports tableur |
| `ui-avatars.com` | avatars des comptes de démonstration | 90 pages |

`ui-avatars.com` est un service tiers appelé pour chaque avatar : hors ligne les
avatars ne s'affichent pas, et chaque affichage de page le contacte. Pour une
mise en production, ces appels doivent être remplacés par les photos réelles ou
par une génération d'initiales côté serveur.

Le débordement se mesure sur `document.documentElement.scrollWidth` comparé à
`innerWidth`, et non sur le rectangle des éléments : un tableau plus large que
l'écran **dans son conteneur `overflow-x-auto`** est le comportement voulu, pas
un défaut. Une première campagne qui comparait les rectangles avait signalé 27
pages en faute, toutes fausses pour cette raison.

Chrome borne `--window-size` à environ 500 px de large : la mesure à 375 px se
fait donc en encapsulant la page dans un cadre de cette largeur, où les media
queries s'évaluent sur la largeur du cadre.

Les seules occurrences de chemins d'origine (`public/assets/...`,
`{{ route(...) }}`) restant dans les fichiers sont **à l'intérieur de
commentaires HTML** documentant le code source : elles ne produisent aucun lien
mort.

### Décompte livré

| Espace | Pages |
|---|---|
| Public | 11 |
| Student | 28 (dont 7 pages de parcours) |
| Teacher | 17 |
| Admin | 28 |
| Simulations (`shared/simulations/`) | 2 |
| Sommaire du kit (`index.html`) | 1 |
| **Total pages** | **87** |
| Shells de référence (`shared/layout-*.html`) | 3 |
| **Total fichiers HTML** | **90** |

Le sommaire `index.html` est vérifié contre le disque : ses 84 entrées
correspondent exactement aux pages présentes, hors `index.html` lui-même et hors
`shared/`.

Les trois `shared/layout-*.html` sont des **fichiers de référence à lire dans un
éditeur** : leurs chemins d'assets (`../../shared/…`) correspondent à la
profondeur d'une page réelle, ils ne s'affichent donc pas correctement ouverts
seuls. C'est volontaire : ils doivent rester copiables tels quels.

---

## 10. Revue rédactionnelle

Une passe éditoriale a été menée sur l'ensemble du kit, avec une consigne
précise : faire disparaître les marqueurs typographiques de rédaction
automatique, et reprendre titres, ponctuation, formulations et descriptions.

### Le tiret cadratin

Il a été purgé partout, texte visible comme commentaires de documentation.

| | Départ | Arrivée |
|---|---|---|
| Cadratins visibles | 605 | **0** |
| Cadratins en commentaires | 516 | **0** |
| Demi-cadratins | 7 | **0** |

Le remplacement n'a jamais été mécanique. Selon le cas : deux-points quand la
suite explique, virgule pour une apposition, parenthèses pour une incise, point
quand il s'agissait de deux phrases, tiret simple dans un commentaire technique,
ou reformulation complète.

### Le signe « et »

104 occurrences de `&amp;` sont devenues « et » : « Frais et éléments »,
« Forum et échanges », « Exercices et tests », « Nom et prénom »…

**Sept occurrences sont conservées volontairement**, et il ne faut pas y toucher :

- six séparateurs de colonnes LaTeX dans l'éditeur : `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` ;
- une fonction d'échappement HTML : `.replace(/&/g, '&amp;')`.

Les opérateurs JavaScript `&&` et les entités (`&nbsp;`, `&copy;`, `&larr;`…)
sont hors sujet.

### Commentaires de balisage

20 commentaires de structure étaient restés en anglais, hérités du Blade source
(`<!-- Submit Button -->`, `<!-- Main layout: sidebar + editor -->`,
`<!-- LEFT SIDEBAR -->`…), alors que tout le reste du kit commente en français.
Ils ont été traduits. Les commentaires qui **citent** le source, eux, gardent
leur libellé d'origine : ils servent de référence.

### Titres de page

Une seule forme, vérifiée sur les **90 fichiers** : `Intitulé de la page | Kilasinaka`.
Plus de tiret long, plus de suffixe `.mg`, plus de double séparateur. Les
intitulés ont été alignés sur le `<h1>` de leur page.

### Vocabulaire

- **« apprenant »** dans les écrans d'apprentissage (parcours, leçon, quiz,
  catalogue, suivi de progression) ;
- **« élève »** dans la gestion scolaire (inscriptions, classes, paiements,
  bulletins) ;
- l'espace public conserve « étudiant », qui est le mot de sa page d'accueil.

### Ce que la mesure a confirmé, et ce qu'elle a infirmé

Un comptage grossier signalait 109 doubles espaces, 15 écrans au vocabulaire
mélangé, de l'anglais résiduel et 48 puces `•`. Vérification faite sur les
nœuds de texte réels :

| Signalement brut | Réalité mesurée |
|---|---|
| 109 doubles espaces | **3**, tous dans des commentaires ou des URL |
| 15 écrans mélangeant le vocabulaire | les occurrences restantes sont dans des commentaires, ou sont le **nom d'une autre page** dans le menu latéral |
| « Preview » en anglais | **0 occurrence visible** : uniquement des identifiants JavaScript et un commentaire CSS |
| 48 puces `•` | 8 masques de mot de passe, 2 séparateurs `aria-hidden`, le reste en séparateurs de méta assumés |
| Espaces avant virgule ou point | **0 dans le texte visible** |
| « 22 leçons libres » contre « 16 » | **faux positif** : le référentiel, le README et le compteur calculé disent tous 16 |

### Contrôles finaux de la passe rédactionnelle

À l'époque de cette passe : 72 pages rendues, **0 erreur console**, 0 tableau
vide, 0 cadratin dans le DOM rendu, 5 fichiers JavaScript valides. Les chiffres
à jour sont ceux du tableau de la section 9.

**Deux régressions de cette passe ont été trouvées et corrigées depuis.** La
substitution `&amp;` → « et » avait été appliquée à l'intérieur de deux
fonctions d'échappement HTML, dans `admin/page/acces.html` et
`admin/page/navigation.html` : `.replace(/&/g, 'et')` transformait tout
« & » d'une donnée en le mot « et », de sorte que « R&D » s'affichait « RetD ».
Les deux fonctions produisent de nouveau l'entité `&amp;`, et les libellés de
menu concernés (« Frais et éléments », « Forum et échanges », « Exercices et
tests ») ont été corrigés dans les **données**, où ils devaient l'être.

---

## 11. Ce que la maquette n'avait pas, et qu'elle a maintenant

Un audit des manques a conduit à cinq chantiers.

### Les pages qui n'existaient pas

11 écrans ont été écrits : `public/inscription.html`, les trois pages d'erreur
(`404`, `403`, `500`), les trois pages légales (`mentions-legales`, `cgu`,
`confidentialite`), `admin/page/profil.html`, `admin/page/journal.html`,
`admin/page/notifications.html`, `admin/page/messagerie.html`, plus
`student/reset-password.html` et `admin/reset-password.html`.

La page d'inscription n'était atteignable depuis nulle part : les appels
« Créer un compte » de `public/cours.html` et `public/lecon.html` pointaient
vers `login.html`, et ni l'accueil ni la page de connexion ne proposaient de
créer un compte. Les trois liens ont été rebranchés.

### Le menu de l'administration

Les pages `acces`, `contenu-public`, `medias`, `roles` et `navigation` ne se
référençaient qu'entre elles : elles formaient un îlot, inatteignable depuis
les 17 autres pages de la console, qui portaient un menu à 12 entrées. Toutes
les pages portent désormais le **même menu de 18 entrées**.

### Fondations d'accessibilité

| Élément | Portée |
|---|---|
| Lien « Aller au contenu » en premier élément focusable | 90 pages |
| Cible `id="contenu"` | 90 pages |
| `meta name="description"` | 90 pages |
| `<noscript>` expliquant que la page a besoin de JavaScript | 90 pages |
| `aria-label="Navigation principale"` sur le menu latéral | 63 pages |
| Champ de recherche global nommé (`id` + `aria-label`) | 58 pages |
| Lien vers `site.webmanifest` | 88 pages (hors les 2 simulations en iframe) |
| Exactement un `<h1>` dans le DOM rendu | 87 pages |
| `lang="fr"` et `meta viewport` | 90 pages |

Le manifeste récupéré de la préproduction déclarait ses icônes en chemin absolu
(`/android-chrome-192x192.png`), ce qui ne résout pas dans un kit statique
maintenant que chaque page le charge : les chemins sont passés en relatif, forme
qui résout dans les deux contextes. Son `theme_color` est passé de `#ffffff`
(valeur sans effet) à `#1d4ed8`, la couleur de marque.

### Exploration et indexation

`robots.txt` autorise l'espace public et exclut `/student`, `/teacher`,
`/admin`, les formulaires d'authentification et les URL de recherche ou de
pagination. `sitemap.xml` ne couvre que les pages publiques indexables : ni
pages d'erreur, ni formulaires. Les deux portent une note d'intégration ; en
production le plan de site doit être généré depuis la base, une entrée par
leçon en accès libre.

### Un défaut de rendu sur 11 pages

Les injections décrites ci-dessus cherchaient `<body>`, `<title>` et
`<noscript>` par expression régulière sur le fichier entier. Or les en-têtes de
documentation **citent** ces balises. Sur 11 pages, la première occurrence
trouvée était donc celle du commentaire : le lien d'évitement s'est retrouvé
dans un commentaire (donc inopérant), et l'attribut `content` de la description
a absorbé le texte du commentaire **avec son `-->` de fermeture**. Le
commentaire se refermait par anticipation, la documentation devenait du contenu
visible et le `<head>` sortait de son contexte.

Les pages touchées étaient `public/login.html`, `admin/login.html`,
`admin/page/dashboard.html`, `student/page/{bookmarks,notifications,progress,search}.html`
et `teacher/page/{messages,messaging,notifications,settings}.html`.

Toutes sont réparées, et les injections ne cherchent plus qu'**au-delà du
premier `<html lang`**. Un contrôle d'équilibre des délimiteurs de commentaire
(`<!--` contre `-->`) fait maintenant partie de la vérification : c'est lui qui
a révélé le défaut, alors qu'un simple `grep` de présence le manquait, la
chaîne cherchée étant bien là, mais dans un commentaire.

C'est la deuxième fois que ce mécanisme frappe le kit. La règle à retenir :
**une balise citée dans un commentaire de documentation est indiscernable d'une
vraie balise pour une expression régulière**, et un contrôle de présence par
`grep` valide alors une injection inopérante.

Le même piège a frappé une troisième fois, sur la cible du lien d'évitement :
l'en-tête de `public/login.html` documente le passage du `<div>` conteneur à
`<main id="contenu">`, et le contrôle de présence validait donc cette **citation**
alors que la balise réelle n'avait pas l'attribut. Tous les contrôles
d'accessibilité du kit s'exécutent maintenant sur le document **commentaires et
scripts retirés** ; ils ont révélé au passage 6 pages dont mon test de présence
avait été trompé par un `<textarea name="description">` et qui n'avaient donc
jamais reçu leur `meta name="description"`.

### Une cible d'évitement posée dans l'attribut `class`

Même passe, autre défaut : le motif qui ajoutait `id="contenu"` sur la section
de contenu s'arrêtait sur la valeur de `class` sans son guillemet fermant. Sur
**58 pages**, l'attribut s'est donc retrouvé *dans* la liste de classes :

```html
<!-- écrit par erreur -->
<section class="flex-1 px-4 sm:px-8 py-6 max-w-7xl mx-auto w-full id="contenu"">
<!-- corrigé -->
<section class="flex-1 px-4 sm:px-8 py-6 max-w-7xl mx-auto w-full" id="contenu">
```

Le lien d'évitement ne menait donc nulle part sur ces pages. Là encore, un
`grep "id=\"contenu\""` répondait « présent ». Le contrôle qui l'a trouvé
vérifie que l'attribut est **porté par une balise** :
`<[a-zA-Z]+[^>]*\sid="contenu"[\s/>]`.

### Trois pages sans titre de niveau 1

`student/page/messages.html`, `teacher/page/messages.html` et
`teacher/page/messaging.html` n'avaient aucun `<h1>` : leur balisage source
commence directement par le panneau de conversation, le mot « Messagerie »
n'étant qu'un `<h2>` de colonne. Un en-tête de page a été ajouté, aligné sur
celui des autres écrans, et la hauteur des panneaux
(`h-[calc(100vh-140px)]` → `h-[calc(100vh-220px)]`) recalibrée en conséquence.

Le comptage se fait sur le **DOM rendu**, et sur les `<h1>` **visibles**, non sur
la source : plusieurs pages portent deux `<h1>` dans le fichier, l'un dans le
balisage statique et l'autre dans un gabarit JavaScript, pour deux états
mutuellement exclusifs. Compter dans la source y voyait à tort un doublon, et
`--dump-dom` sérialisant aussi le contenu des `<script>`, un comptage sur le DOM
sérialisé se trompait de la même façon.

Ce contrôle a aussi révélé que `student/page/certificat.html` ouverte sans
paramètre `?m=` n'affichait qu'un paragraphe : son état vide porte maintenant un
titre et une sortie vers la liste des certificats.

### Champs de formulaire sans nom accessible

Un `placeholder` n'est pas un nom accessible, et un `<label>` sans `for` ne
nomme rien. Le contrôle, mené sur le corps du document **commentaires et scripts
retirés**, a trouvé les cas suivants, tous corrigés :

| Correctif | Cas |
|---|---|
| `for` ajouté au `<label>` qui précède le champ | 7 + les 8 curseurs des simulations |
| `aria-label` déduit du `placeholder` | 193 |
| `aria-label` de filtre déduit de la première option | 83 |
| `aria-label` posé nommément | 4 |

La déduction automatique du libellé de filtre a d'abord été trop large : appliquée
à des `<select>` de saisie, elle produisait des noms faux comme « Filtrer par
3ème A » ou « Filtrer par antananarivo (UTC+3) ». **Un nom accessible trompeur
est pire que pas de nom :** 71 de ces libellés ont été retirés, la règle
restreinte aux `<select>` dont la première option est un « Tous / Toutes » ou
porte `value=""`, et les libellés restants repris au singulier.

Résultat : **0 champ sans nom accessible** sur les 90 fichiers.

### Commandes réduites à une icône

119 boutons et liens n'avaient qu'une icône, sans texte : loupe de recherche,
corbeille, crayon, export PDF, fermeture de modale, ainsi que les nuanciers et
la barre d'outils de l'éditeur. Ils portent désormais un `aria-label`, tiré du
`title` quand l'auteur en avait posé un, sinon déduit de l'icône Font Awesome.

**0 bouton, lien ou image sans nom accessible** dans le kit.

### Le CDN Tailwind pouvait manquer

`shared/js/tailwind.config.js` faisait `tailwind.config = {…}` sans vérifier que
le CDN chargé juste avant avait répondu. Réseau coupé, ou page ouverte en
`file://` avec une réponse lente, la page levait
`Uncaught ReferenceError: tailwind is not defined` et le reste du script était
abandonné. Le fichier teste maintenant la présence de l'objet et signale
l'absence de styles utilitaires par un avertissement, au lieu d'échouer.

---

## 12. Cohérence de la navigation

### Chaque entrée de menu porte le nom de la page qu'elle ouvre

Le contrôle compare le libellé de chaque entrée du menu latéral au `<title>` de
sa page cible. Il a trouvé **10 divergences**, où l'utilisateur cliquait sur un
mot et arrivait sur un écran qui en annonçait un autre :

| Espace | Page | Menu disait | Titre disait | Retenu |
|---|---|---|---|---|
| Apprenant | `supports.html` | Supports | Centre d'aide | **Centre d'aide** |
| Apprenant | `progress.html` | Ma progression | Suivi de progression | **Ma progression** |
| Enseignant | `editeur.html` | Créer une leçon | Éditeur de leçons | **Éditeur de leçons** |
| Enseignant | `corrections.html` | Corrections | Devoirs à corriger | **Devoirs à corriger** |
| Enseignant | `planning.html` | Planning | Emploi du temps | **Emploi du temps** |
| Enseignant | `forum.html` | Forum profs | Forum et échanges | **Forum et échanges** |
| Admin | `eleves.html` | Apprenants | Gestion des élèves | **Élèves** |
| Admin | `suivi-eleve.html` | Suivi des apprenants | Suivi des élèves | **Suivi des élèves** |
| Admin | `medias.html` | Médiathèque | Bibliothèque de médias | **Médiathèque** |
| Admin | `professeurs.html` | Enseignants | Professeurs | **Enseignants** |

`student/page/supports.html` était le cas le plus trompeur : la page est le
centre d'aide (`student.supports`, cible du lien « Centre d'aide » du menu
source), et son `<h1>` demande « Comment pouvons-nous vous aider ? ». L'entrée
« Supports » laissait attendre des supports de cours.

Les deux entrées admin ont tranché en faveur d'« élève » parce que
`suivi-eleve.html` liste matricule, classe, contact parent, moyenne, présence et
paiement : c'est de la gestion scolaire, pas du suivi d'apprentissage, lequel a
sa page dédiée (`apprentissage.html`).

**0 divergence** sur les 40 entrées des trois menus.

### Le sommaire du kit dit la même chose que les pages

Les 84 entrées de `index.html` ont été recalées sur les titres réels. Cinq
libellés ont été volontairement **conservés plus descriptifs que le `<title>`**,
parce que celui-ci reflète la donnée d'exemple affichée et non le type de page :
`subject.html` s'intitule « Mathématiques » mais reste « Fiche matière » au
sommaire, `chapter.html` « Théorème de Pythagore » reste « Fiche chapitre », et
de même pour la leçon, le quiz et le certificat.

### Chemins d'accès qui manquaient

- `admin/page/profil.html` n'était atteignable depuis aucune page : le menu du
  compte, dans la barre supérieure des 24 écrans admin, n'offrait que
  « Paramètres » et « Déconnexion ». Il propose maintenant « Mon compte ».
- Les pieds de page des espaces connectés affichaient « Mentions légales » en
  lien mort. Les 59 pages concernées pointent sur `public/mentions-legales.html`,
  qui existe désormais.
- `shared/layout-admin.html` portait encore l'ancien menu à 12 entrées : la
  coquille de référence servait un menu différent de celui de ses propres pages.

### Vocabulaire

Dans le texte visible, par espace, après alignement :

| Espace | étudiant | élève | apprenant |
|---|---|---|---|
| Public | 28 | 1 | 19 |
| Apprenant | 0 | 0 | 8 |
| Enseignant | 0 | 17 | 22 |
| Administration | 0 | 87 | 59 |

L'espace apprenant ne dit plus que « apprenant ». L'unique « élèves » du public
est dans la phrase de présentation (« accompagne les élèves et étudiants de
Madagascar »), qui est du texte de communication et reste tel quel. Les espaces
enseignant et administration mêlent les deux termes parce qu'ils mêlent les deux
métiers : gestion scolaire d'un côté, suivi de l'apprentissage de l'autre.

---

## 13. Par où commencer

1. Ouvrir **`template_v2/index.html`** : sommaire filtrable des 87 pages.
2. **Faire le parcours** : `student/page/parcours.html` → « Commencer » →
   valider quelques leçons → passer un quiz → revenir sur `parcours.html`,
   puis ouvrir `teacher/page/suivi.html` : votre progression y est remontée.
3. **Voir la partie publique gratuite** : `public/cours.html` → une leçon en
   accès libre → le simulateur intégré, sans compte ni connexion.
4. Pour comprendre le squelette : **`shared/layout-admin.html`** (le plus riche).
5. Pour comprendre le CRUD : **`admin/page/eleves.html`**, la page de référence
   sur laquelle toutes les autres listes sont calquées ; la fonction
   `ligneEleve()` en bas de page est le balisage à porter côté serveur.
6. Pour ce qui est administrable : **`admin/page/acces.html`** (quelles leçons
   sont ouvertes au public), **`contenu-public.html`** (les textes du site),
   **`navigation.html`** (les menus), **`medias.html`**, **`roles.html`**.

---

## 14. Ce que ce kit n'est pas

Il est déployable **comme démonstration statique**, jamais comme l'application :

- le contrôle d'accès est côté client (le fichier de données livré contient les
  52 leçons, l'interface en masque 36) ;
- les réponses des quiz sont dans les données ;
- la progression vit dans le `localStorage` du navigateur ;
- il n'y a ni jeton anti-CSRF, ni session, ni backend.

Chaque page indique dans son en-tête ce qu'un serveur doit reprendre.
