# Bilan Redesign — Plan d'implémentation > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Remplacer la mise en page plate et non hiérarchisée par une structure header-sticky + panneaux qui guide clairement le flux formateur (config → évaluation → synthèse/export). **Architecture:** Deux fichiers modifiés uniquement — `Bilan.jsx` (restructuration JSX + état toast) et `Bilan.css` (nouvelles classes de layout, suppression des anciennes). La logique métier (niveaux, drag & drop, PDF, OpenAI) reste intacte. Le test à chaque tâche est visuel : lancer le dev server et vérifier le rendu. **Tech Stack:** React 18, Vite 6, CSS pur (pas de framework CSS), Plus Jakarta Sans (Google Fonts), pdf-lib ## Global Constraints - Ne jamais toucher à la logique métier (handlers, état des items, appels OpenAI, génération PDF) - Conserver toutes les variables CSS existantes dans `:root` - Conserver le responsive mobile (media query `max-width: 768px`) - Conserver le drag & drop et les level pills existants - Dev server : `npm run dev` (port 5173 par défaut) --- ## Fichiers touchés | Fichier | Action | Responsabilité | |---|---|---| | `src/components/Bilan.jsx` | Modifier | Restructuration JSX + remplacement état `status` par `toasts[]` | | `src/components/Bilan.css` | Modifier | Nouvelles classes layout, suppression `.bilan-wrapper/.bilan-container/.controls/.header/.status` | --- ### Task 1 : Système de toasts flottants **Remplace l'état `status` / `setStatus` par un tableau `toasts[]` avec auto-dismiss. Les notifications apparaissent en bas à droite, hors du flux de contenu.** **Files:** - Modify: `src/components/Bilan.jsx` (état, showStatus, JSX toasts, finally blocks) - Modify: `src/components/Bilan.css` (nouvelles classes `.toast-container`, `.toast`, `.toast-info/success/error`) **Interfaces:** - Consumes: — - Produces: `toasts` state, `showStatus(message, type, withProgress)` (même signature qu'avant), `dismissProgressToasts()` (appelé dans les blocs finally) --- - [ ] **Étape 1 : Remplacer l'état `status` par `toasts` dans Bilan.jsx** Localiser ligne ~324 dans `src/components/Bilan.jsx`. Remplacer : ```jsx const [status, setStatus] = useState(null); ``` par : ```jsx const [toasts, setToasts] = useState([]); ``` - [ ] **Étape 2 : Supprimer le useEffect d'auto-dismiss (lignes ~355-362)** Supprimer entièrement ce bloc : ```jsx useEffect(() => { if (!status || status.withProgress) return; const timeout = setTimeout( () => setStatus(null), status.type === "info" ? 3000 : 5000 ); return () => clearTimeout(timeout); }, [status]); ``` - [ ] **Étape 3 : Remplacer `showStatus` par la version toast** Localiser et remplacer le callback `showStatus` (ligne ~346) : ```jsx const showStatus = useCallback((message, type = "info", withProgress = false) => { const id = Date.now(); setToasts((prev) => [...prev, { id, message, type, withProgress }]); if (!withProgress) { setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)); }, type === "info" ? 3000 : 5000); } }, []); ``` - [ ] **Étape 4 : Ajouter `dismissProgressToasts` après `showStatus`** ```jsx const dismissProgressToasts = useCallback(() => { setToasts((prev) => prev.filter((t) => !t.withProgress)); }, []); ``` - [ ] **Étape 5 : Mettre à jour les blocs `finally` dans `generateAISummaryOpenAI`** Localiser le bloc `finally` de `generateAISummaryOpenAI` (ligne ~686). Remplacer : ```jsx } finally { setStatus((prev) => (prev ? { ...prev, withProgress: false } : prev)); setIsAiLoading(false); } ``` par : ```jsx } finally { dismissProgressToasts(); setIsAiLoading(false); } ``` - [ ] **Étape 6 : Mettre à jour le bloc `finally` dans `downloadPDF`** Localiser le bloc `finally` de `downloadPDF` (ligne ~912). Remplacer : ```jsx } finally { setStatus((prev) => (prev ? { ...prev, withProgress: false } : prev)); } ``` par : ```jsx } finally { dismissProgressToasts(); } ``` - [ ] **Étape 7 : Ajouter le JSX des toasts dans le return** Dans le `return` de `Bilan`, ajouter juste avant la fermeture de la div racine (peu importe laquelle pour l'instant) : ```jsx
{toasts.map((toast) => (
{toast.withProgress ? ( <> {toast.message}
) : ( {toast.message} )}
))}
``` - [ ] **Étape 8 : Supprimer le JSX du statut inline existant** Dans le JSX du `return`, supprimer le bloc conditionnel `{status && (...)}` qui rend le statut à l'intérieur de `.evaluation-summary-section`. Localiser : ```jsx {status && (
...
)} ``` et le supprimer entièrement. - [ ] **Étape 9 : Ajouter le CSS des toasts dans Bilan.css** Ajouter à la fin du fichier, avant les media queries : ```css .toast-container { position: fixed; bottom: 24px; right: 24px; z-index: 1000; display: flex; flex-direction: column; gap: 8px; max-width: 360px; pointer-events: none; } .toast { padding: 12px 16px; border-radius: var(--radius-md); font-size: 13.5px; font-weight: 700; font-family: var(--font-sans); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; gap: 8px; animation: toast-in 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .toast-info { background: var(--color-indigo-50); border-left: 3px solid var(--color-indigo-500); color: var(--color-indigo-700); } .toast-success { background: #dcfce7; border-left: 3px solid #10b981; color: #15803d; } .toast-error { background: #fee2e2; border-left: 3px solid #ef4444; color: #b91c1c; } .toast-progress { width: 100%; height: 3px; background: rgba(0, 0, 0, 0.08); border-radius: 999px; overflow: hidden; } .toast-progress-bar { width: 100%; height: 100%; background: currentColor; opacity: 0.6; animation: progress-stripes 1.5s linear infinite; } @keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } ``` - [ ] **Étape 10 : Vérifier visuellement** ```bash npm run dev ``` Déclencher une action (ex : cliquer "Générer le Bilan"). Un toast doit apparaître en bas à droite et disparaître automatiquement. Aucun message de statut ne doit apparaître dans le corps de la page. - [ ] **Étape 11 : Commit** ```bash git add src/components/Bilan.jsx src/components/Bilan.css git commit -m "feat: replace inline status with floating toast notifications" ``` --- ### Task 2 : App shell + Topbar sticky **Restructure le wrapper racine en `.app-shell` + `.app-topbar` (header sticky 64px) contenant logo, CP selector pill, titre, inputs identité et boutons d'action.** **Files:** - Modify: `src/components/Bilan.jsx` (nouvelle structure return, déplacement CP selector + identité + boutons dans topbar) - Modify: `src/components/Bilan.css` (nouvelles classes `.app-shell`, `.app-topbar`, `.topbar-*`, `.cp-select`, `.topbar-input`, `.btn-topbar-*`) **Interfaces:** - Consumes: `dismissProgressToasts` (Task 1), `toasts` state (Task 1) - Produces: structure `.app-shell > .app-topbar + .app-main` utilisée par les tâches 3, 4, 5 --- - [ ] **Étape 1 : Ajouter le CSS du app-shell et du topbar dans Bilan.css** Remplacer les règles `.bilan-wrapper` et `.bilan-container` existantes par : ```css .app-shell { min-height: 100vh; background-color: #f8fafc; background-image: radial-gradient(circle at 10% 20%, rgba(98, 95, 255, 0.06) 0%, transparent 40%), radial-gradient(circle at 90% 80%, rgba(139, 92, 246, 0.04) 0%, transparent 45%); font-family: var(--font-sans); color: var(--text-primary); -webkit-font-smoothing: antialiased; } .app-topbar { position: sticky; top: 0; z-index: 100; height: 64px; background: rgba(255, 255, 255, 0.97); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-bottom: 1px solid var(--border-slate-200); box-shadow: 0 1px 8px rgba(0, 0, 0, 0.04); display: flex; align-items: center; justify-content: space-between; padding: 0 24px; gap: 16px; } .topbar-left { display: flex; align-items: center; gap: 12px; flex-shrink: 0; } .topbar-logo { height: 28px; width: auto; display: block; } .topbar-divider { width: 1px; height: 24px; background: var(--border-slate-200); } .cp-select { appearance: none; -webkit-appearance: none; background: var(--color-indigo-50); color: var(--color-indigo-600); border: 1px solid rgba(98, 95, 255, 0.2); border-radius: 999px; padding: 6px 28px 6px 12px; font-size: 13px; font-weight: 700; font-family: var(--font-sans); cursor: pointer; outline: none; background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7L10 12L15 7' stroke='%234f46e5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; background-size: 14px; transition: var(--transition); } .cp-select:hover { background-color: var(--color-indigo-100); border-color: var(--color-indigo-500); } .topbar-center { display: flex; flex-direction: column; align-items: center; gap: 1px; flex: 1; min-width: 0; } .topbar-title { font-size: 15px; font-weight: 800; color: var(--text-primary); letter-spacing: -0.02em; white-space: nowrap; } .topbar-subtitle { font-size: 11px; font-weight: 500; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .topbar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; } .topbar-input { width: 110px; height: 36px; padding: 0 12px; border-radius: var(--radius-md); border: 1px solid var(--border-slate-200); background: #f8fafc; color: var(--text-primary); font-size: 13px; font-weight: 500; font-family: var(--font-sans); outline: none; transition: var(--transition); } .topbar-input::placeholder { color: var(--text-muted); font-weight: 400; } .topbar-input:focus { border-color: var(--color-indigo-500); background: #ffffff; box-shadow: 0 0 0 3px rgba(98, 95, 255, 0.1); } .topbar-actions { display: flex; align-items: center; gap: 8px; padding-left: 12px; border-left: 1px solid var(--border-slate-200); margin-left: 4px; } .btn-topbar-secondary { height: 36px; padding: 0 14px; border-radius: var(--radius-md); border: 1px solid var(--border-slate-300); background: #ffffff; color: var(--text-secondary); font-size: 13px; font-weight: 700; font-family: var(--font-sans); cursor: pointer; transition: var(--transition); white-space: nowrap; box-shadow: var(--shadow-flat); } .btn-topbar-secondary:hover { border-color: var(--color-indigo-500); color: var(--color-indigo-600); box-shadow: 0 2px 8px rgba(98, 95, 255, 0.1); } .btn-topbar-primary { height: 36px; padding: 0 14px; border-radius: var(--radius-md); border: none; background: linear-gradient(135deg, #6d28d9 0%, #a855f7 50%, #db2777 100%); color: #ffffff; font-size: 13px; font-weight: 800; font-family: var(--font-sans); cursor: pointer; transition: var(--transition); white-space: nowrap; box-shadow: 0 4px 14px rgba(168, 85, 247, 0.3); } .btn-topbar-primary:hover:not(:disabled) { background: linear-gradient(135deg, #7c3aed 0%, #c084fc 50%, #ec4899 100%); box-shadow: 0 6px 20px rgba(168, 85, 247, 0.45); transform: translateY(-1px); } .btn-topbar-primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } .btn-topbar-success { height: 36px; padding: 0 14px; border-radius: var(--radius-md); border: none; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #ffffff; font-size: 13px; font-weight: 700; font-family: var(--font-sans); cursor: pointer; transition: var(--transition); white-space: nowrap; box-shadow: 0 4px 14px rgba(16, 185, 129, 0.2); } .btn-topbar-success:hover { background: linear-gradient(135deg, #059669 0%, #047857 100%); box-shadow: 0 6px 18px rgba(16, 185, 129, 0.35); transform: translateY(-1px); } .app-main { max-width: 1240px; margin: 0 auto; padding: 32px 24px 64px; } ``` - [ ] **Étape 2 : Restructurer le JSX du return dans Bilan.jsx** Remplacer la div racine `
` et son contenu jusqu'à la fermeture de `.bilan-container` (hors grille et section synthèse) par la nouvelle structure. Le return complet doit ressembler à : ```jsx return (
Arinfo
{currentTitle.h1} {currentTitle.h2}
setFirstName(event.target.value)} /> setLastName(event.target.value)} />
{anySelected && ( )}
{/* La toolbar critères (Task 3), la grille et la section synthèse viennent ici */} {/* Conserver la grille .competences-grid et .evaluation-summary-section existantes pour l'instant */}
{toasts.map((toast) => (
{toast.withProgress ? ( <> {toast.message}
) : ( {toast.message} )}
))}
); ``` **Important :** déplacer les éléments JSX de la grille `.competences-grid` et de `.evaluation-summary-section` à l'intérieur de `
`. Supprimer `Logo watermark` (watermark supprimé). Supprimer les anciens blocs `.controls.identity`, `.controls.actions` et `.header` du JSX (ils sont maintenant dans le topbar). - [ ] **Étape 3 : Vérifier visuellement** ```bash npm run dev ``` Résultats attendus : - Header blanc sticky visible en haut (64px) - Logo Arinfo à gauche, CP selector pill indigo, titre du CP au centre - Inputs Prénom/Nom compacts à droite + boutons d'action visibles - En scrollant, le header reste fixé en haut - La grille de critères s'affiche dans la zone principale (même si encore sans toolbar) - [ ] **Étape 4 : Commit** ```bash git add src/components/Bilan.jsx src/components/Bilan.css git commit -m "feat: add sticky app topbar with cp selector, identity inputs and action buttons" ``` --- ### Task 3 : Toolbar critères **Ajoute une barre fine (44px) sous le topbar contenant les actions de sélection groupée et l'ajout de critère. Supprime les anciens boutons/inputs équivalents du corps de page.** **Files:** - Modify: `src/components/Bilan.jsx` (ajout `.criteria-toolbar` dans `
`, suppression des anciens `.controls.actions`) - Modify: `src/components/Bilan.css` (nouvelles classes `.criteria-toolbar`, `.btn-text`, `.toolbar-add-input`, `.btn-toolbar-add`) **Interfaces:** - Consumes: structure `.app-shell > .app-topbar + .app-main` (Task 2) - Produces: toolbar rendu dans `
` avant la grille --- - [ ] **Étape 1 : Ajouter le CSS de la toolbar dans Bilan.css** Ajouter après les classes `.app-main` : ```css .criteria-toolbar { display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 4px; margin-bottom: 20px; gap: 16px; } .criteria-toolbar-left { display: flex; align-items: center; gap: 6px; } .btn-text { background: none; border: none; padding: 4px 8px; font-size: 12.5px; font-weight: 600; color: var(--text-muted); cursor: pointer; font-family: var(--font-sans); transition: var(--transition); border-radius: var(--radius-sm); box-shadow: none; } .btn-text:hover { color: var(--color-indigo-600); background: var(--color-indigo-50); transform: none; box-shadow: none; } .toolbar-sep { color: var(--border-slate-300); font-size: 14px; user-select: none; } .criteria-toolbar-right { display: flex; align-items: center; gap: 8px; } .toolbar-add-input { height: 34px; width: 240px; padding: 0 12px; border-radius: var(--radius-sm); border: 1px solid var(--border-slate-200); background: #ffffff; color: var(--text-primary); font-size: 13px; font-family: var(--font-sans); outline: none; transition: var(--transition); box-shadow: var(--shadow-flat); } .toolbar-add-input::placeholder { color: var(--text-muted); } .toolbar-add-input:focus { border-color: var(--color-indigo-500); box-shadow: 0 0 0 3px rgba(98, 95, 255, 0.1); } .btn-toolbar-add { height: 34px; width: 34px; border-radius: var(--radius-sm); border: 1px solid var(--border-slate-300); background: #ffffff; color: var(--text-secondary); font-size: 20px; font-weight: 300; font-family: var(--font-sans); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: var(--transition); box-shadow: var(--shadow-flat); line-height: 1; padding: 0; } .btn-toolbar-add:hover { background: var(--color-indigo-50); border-color: var(--color-indigo-500); color: var(--color-indigo-600); transform: none; box-shadow: var(--shadow-flat); } ``` - [ ] **Étape 2 : Ajouter le JSX de la toolbar dans Bilan.jsx** Dans `
`, ajouter en premier enfant (avant `.competences-grid`) : ```jsx
·
setNewCriterion(event.target.value)} onKeyDown={(event) => event.key === "Enter" && handleAddCriterion()} />
``` - [ ] **Étape 3 : Vérifier visuellement** ```bash npm run dev ``` Résultats attendus : - Barre fine entre le topbar et la grille avec "Tout sélectionner · Tout désélectionner" à gauche - Input + bouton "+" à droite, alignés - Les deux actions fonctionnent (cocher/décocher tous) - Ajouter un critère via Enter ou clic "+" crée un item dans "À classer" - [ ] **Étape 4 : Commit** ```bash git add src/components/Bilan.jsx src/components/Bilan.css git commit -m "feat: add criteria toolbar with selection controls and add-criterion input" ``` --- ### Task 4 : Layout interne des items en flex-row **Transforme le layout vertical de chaque `.competence-item` en ligne horizontale : checkbox → label (flex-grow) → level pills → bouton supprimer. Réduit la hauteur de page et améliore la densité.** **Files:** - Modify: `src/components/Bilan.css` (`.competence-item`, `.competence-label`, `.level-group`, `.remove-field-btn`) **Interfaces:** - Consumes: structure `.competence-item` existante dans le JSX (inchangée) - Produces: items en flex-row, visuellement plus compacts --- - [ ] **Étape 1 : Modifier `.competence-item` pour flex-row** Localiser la règle `.competence-item` dans `Bilan.css` et remplacer par : ```css .competence-item { border: 1px solid var(--border-slate-200); padding: 10px 12px; border-radius: var(--radius-md); cursor: grab; background: #ffffff; transition: var(--transition); display: flex; flex-direction: row; align-items: center; gap: 10px; box-shadow: var(--shadow-card); position: relative; } ``` - [ ] **Étape 2 : Modifier `.competence-label` pour occuper l'espace disponible** Localiser `.competence-item .competence-label` et remplacer par : ```css .competence-item .competence-label { flex: 1; min-width: 0; font-weight: 600; color: var(--text-primary); font-size: 13px; cursor: pointer; line-height: 1.4; padding-right: 0; } ``` - [ ] **Étape 3 : Modifier `.level-group` pour s'intégrer inline** Localiser `.level-group` et remplacer par : ```css .level-group { display: flex; flex-wrap: nowrap; gap: 4px; align-items: center; flex-shrink: 0; border-top: none; padding-top: 0; margin-top: 0; } ``` - [ ] **Étape 4 : Supprimer `.level-title` du JSX** Dans `Bilan.jsx`, dans le rendu de chaque item, localiser et supprimer la ligne : ```jsx Niveau : ``` Ce label textuel prend de la place inutilement en layout row. - [ ] **Étape 5 : Modifier `.remove-field-btn` pour sortir du positionnement absolu** Remplacer la règle `.remove-field-btn` par : ```css .remove-field-btn { background: #fee2e2; color: #ef4444; border: 0 !important; border-radius: 50% !important; width: 20px !important; height: 20px !important; padding: 0 !important; font-size: 12px !important; cursor: pointer; display: flex !important; align-items: center !important; justify-content: center !important; flex-shrink: 0; opacity: 0; transform: scale(0.8); transition: var(--transition); box-shadow: none !important; position: static; } ``` - [ ] **Étape 6 : Vérifier visuellement** ```bash npm run dev ``` Résultats attendus : - Chaque item de critère est maintenant une seule ligne horizontale - Checkbox à gauche, label au centre (stretch), level pills (NA/EC/AC/MA) à droite - Le bouton × apparaît au hover, après les pills - La grille est sensiblement plus compacte - [ ] **Étape 7 : Commit** ```bash git add src/components/Bilan.css src/components/Bilan.jsx git commit -m "feat: switch criteria items to horizontal flex-row layout" ``` --- ### Task 5 : Section divider + polish de la synthèse **Ajoute le séparateur visuel "Synthèse & Export", supprime le h3 redondant, améliore le header des cards de critères, et nettoie la section synthèse.** **Files:** - Modify: `src/components/Bilan.jsx` (ajout `.section-divider`, suppression `

Synthèse & Observations

`) - Modify: `src/components/Bilan.css` (`.section-divider`, amélioration `.competence-card h4`, loader inline) **Interfaces:** - Consumes: structure `.evaluation-summary-section` existante - Produces: séparateur visuel rendu, section synthèse sans titre redondant --- - [ ] **Étape 1 : Ajouter le CSS du divider et polisher les card headers** Ajouter dans `Bilan.css` après les classes `.criteria-toolbar-*` : ```css .section-divider { display: flex; align-items: center; gap: 16px; margin: 48px 0 32px; color: var(--text-muted); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; } .section-divider::before, .section-divider::after { content: ''; flex: 1; height: 1px; background: var(--border-slate-200); } ``` Modifier `.competence-card h4` pour ajouter un fond coloré léger au header de chaque card : ```css .competence-card h4 { display: flex; justify-content: space-between; align-items: center; margin: 0 -24px 0 -24px; padding: 12px 24px; font-size: 12px; font-weight: 800; color: var(--text-primary); text-transform: uppercase; letter-spacing: 0.06em; border-radius: var(--radius-lg) var(--radius-lg) 0 0; background: rgba(248, 250, 252, 0.8); border-bottom: 1px solid var(--border-slate-200); } ``` - [ ] **Étape 2 : Ajouter le JSX du divider dans Bilan.jsx** Dans `
`, avant `.evaluation-summary-section`, ajouter : ```jsx
Synthèse & Export
``` - [ ] **Étape 3 : Supprimer le h3 redondant dans .evaluation-summary-section** Dans le JSX de `.evaluation-summary-section`, supprimer : ```jsx

Synthèse & Observations

``` - [ ] **Étape 4 : Supprimer le bloc `.ai-status-loader` du JSX (déjà remplacé par toasts)** Localiser et supprimer du JSX : ```jsx {isAiLoading && (
Génération du résumé en cours…
)} ``` - [ ] **Étape 5 : Vérifier visuellement** ```bash npm run dev ``` Résultats attendus : - Ligne horizontale avec "Synthèse & Export" centré entre la grille et la section de synthèse - Headers des cards de critères ont un fond légèrement teinté - Pas de h3 redondant dans la section synthèse - Le loader inline a disparu (remplacé par toast en Task 1) - [ ] **Étape 6 : Commit** ```bash git add src/components/Bilan.jsx src/components/Bilan.css git commit -m "feat: add section divider, polish card headers, remove redundant labels" ``` --- ### Task 6 : Nettoyage CSS final + responsive **Supprime toutes les classes CSS devenues inutilisées et adapte les media queries au nouveau layout.** **Files:** - Modify: `src/components/Bilan.css` (suppression `.bilan-wrapper`, `.bilan-container`, `.header`, `.controls`, `.logo-bg`, `.status`, `.back-btn`, `.ai-loader`, `.ai-status-loader`; mise à jour media queries) **Interfaces:** - Consumes: tout le CSS final des tasks 1–5 - Produces: fichier CSS sans classes orphelines, responsive fonctionnel --- - [ ] **Étape 1 : Supprimer les classes obsolètes de Bilan.css** Supprimer entièrement les blocs CSS suivants (ils ne sont plus référencés dans le JSX) : - `.bilan-wrapper` et `.logo-bg` - `.bilan-container` - `.header`, `.header h1`, `.header h2` - `.back-btn`, `.back-btn:hover` - `.controls`, `.controls.controls-select`, `.controls.controls-select label`, `.controls.identity input`, `.controls.actions input`, `.controls.actions button:last-of-type`, `.controls input`, `.controls select` - `.status`, `.status.info`, `.status.success`, `.status.error`, `.status .progress`, `.status .progress-bar` - `.ai-loader` - `.ai-status-loader` - `.bilan-container button`, `.bilan-container button:hover`, `.bilan-container button:disabled` (les boutons du topbar ont leurs propres classes) - `.bilan-container select`, `.bilan-container select option` - `.bilan-container textarea` (remplacé par les règles dans `.summary-inputs-panel` et `.summary-output-panel`) - [ ] **Étape 2 : Mettre à jour les media queries** Localiser le bloc `@media (max-width: 768px)` et le remplacer par : ```css @media (max-width: 768px) { .app-topbar { height: auto; flex-direction: column; align-items: flex-start; padding: 12px 16px; gap: 10px; } .topbar-center { align-items: flex-start; } .topbar-right { flex-wrap: wrap; gap: 8px; } .topbar-input { width: 90px; } .topbar-actions { border-left: none; padding-left: 0; margin-left: 0; flex-wrap: wrap; } .app-main { padding: 16px 16px 48px; } .criteria-toolbar { flex-direction: column; align-items: flex-start; height: auto; padding: 8px 0; gap: 8px; } .toolbar-add-input { width: 100%; } .criteria-toolbar-right { width: 100%; } .summary-grid { grid-template-columns: 1fr; gap: 24px; } .competences-grid { grid-template-columns: 1fr; } .competence-item { flex-wrap: wrap; } .level-group { flex-wrap: wrap; margin-left: 28px; } .toast-container { bottom: 12px; right: 12px; left: 12px; max-width: none; } } ``` - [ ] **Étape 3 : Vérification finale desktop** ```bash npm run dev ``` Vérifier le workflow complet : 1. Changer de CP → topbar se met à jour immédiatement 2. Renseigner Prénom/Nom dans le topbar 3. Cocher plusieurs critères + assigner des niveaux 4. Cliquer "📝 Générer" → toast succès apparaît en bas à droite 5. Cliquer "✨ Résumer avec l'IA" (sans clé) → toast erreur apparaît 6. Le bouton "📄 PDF" apparaît dès qu'un critère est coché 7. Ajouter un critère custom via la toolbar → apparaît dans "À classer" 8. Drag & drop entre colonnes fonctionne toujours - [ ] **Étape 4 : Vérification mobile (DevTools, 375px)** Redimensionner à 375px. Vérifier : - Topbar passe en colonne, reste lisible - La grille passe en 1 colonne - La summary grid passe en 1 colonne - Les toasts s'affichent en bas en pleine largeur - [ ] **Étape 5 : Commit final** ```bash git add src/components/Bilan.jsx src/components/Bilan.css git commit -m "chore: remove unused CSS classes and update responsive breakpoints" ```