--- stepsCompleted: [1, 2, 3, 4] status: complete validatedAt: '2026-02-03' inputDocuments: - docs/prd-gamification.md - docs/planning-artifacts/architecture.md - docs/planning-artifacts/ux-design-specification.md - docs/brainstorming-gamification-2026-01-26.md --- # skycel - Epic Breakdown ## Overview This document provides the complete epic and story breakdown for skycel, decomposing the requirements from the PRD, UX Design if it exists, and Architecture requirements into implementable stories. ## Requirements Inventory ### Functional Requirements - **FR1** : Le système offre une double entrée au visiteur : "Partir à l'aventure" (expérience complète) ou "Je n'ai pas le temps" (mode express avec roadmap) - **FR2** : Les transitions entre pages sont animées de manière seamless via Nuxt transitions, créant une impression de "changement de zone" immersive - **FR3** : Un narrateur-guide accompagne le visiteur avec des textes contextuels tout au long de l'expérience - **FR4** : Une carte interactive (Konva.js) permet la navigation non-linéaire et affiche la progression du visiteur - **FR5** : Un arbre de compétences interactif (vis.js) visualise les skills avec niveaux évoluant selon les projets - **FR6** : Les compétences sont cliquables et mènent directement aux projets qui les utilisent - **FR7** : Les témoignages s'affichent sous forme de dialogues PNJ style Zelda avec avatar, bulles, effet typewriter et personnalités variées - **FR8** : Une barre de progression globale indique l'avancement dans l'exploration du site - **FR9** : Le système propose 2-3 choix binaires créant 4-8 parcours narratifs différents, tous menant au contact - **FR10** : Un challenge/puzzle accessible doit être résolu pour accéder au formulaire de contact (avec système d'indices) - **FR11** : Des easter eggs cachés récompensent l'exploration avec des snippets de code ou anecdotes - **FR12** : La progression est sauvegardée automatiquement en LocalStorage pour permettre la reprise - **FR13** : Le site supporte deux langues (FR par défaut + EN) avec détection par URL (/en/...) - **FR14** : Le formulaire de contact est présenté comme la récompense finale "Tu m'as trouvé !" avec célébration ### NonFunctional Requirements - **NFR1** : Le bundle JS total (Nuxt + Konva + vis.js) ne doit pas dépasser 170kb gzippé, avec lazy-loading des composants lourds - **NFR2** : Le temps de chargement initial (LCP) doit rester sous 2.5 secondes sur connexion 3G - **NFR3** : Le site doit être responsive et offrir une expérience adaptée mobile (carte simplifiée en Chemin Libre vertical) - **NFR4** : Le site doit fonctionner sur les navigateurs modernes (Chrome, Firefox, Safari, Edge — 2 dernières versions) - **NFR5** : Les URLs doivent être SEO-friendly et le contenu principal accessible aux crawlers (SSR Nuxt 4) - **NFR6** : Les animations doivent respecter `prefers-reduced-motion` pour l'accessibilité - **NFR7** : Le système i18n utilise @nuxtjs/i18n avec fichiers JSON, rendu SSR pour SEO optimal - **NFR8** : Les images sont optimisées en WebP avec lazy loading ### Additional Requirements **Architecture :** - Starter template : Nuxt 4 (`npx nuxi@latest init`) + Laravel 12 (`composer create-project`) — impacte Epic 1 Story 1 - Structure monorepo `frontend/` (Nuxt 4 avec structure `app/`) + `api/` (Laravel 12) - Table `translations` centralisée en MariaDB pour i18n du contenu dynamique (clés i18n dans les tables métier) - API REST avec API Key (`X-API-Key`) + CORS strict (domaine frontend uniquement) - Store Pinia `useProgressionStore` avec persistance LocalStorage via `pinia-plugin-persistedstate` + compatibilité SSR - Architecture composants frontend : `ui/` (atomiques réutilisables), `feature/` (métier), `layout/` (structure page) - GSAP pour animations complexes (Swup.js abandonné — redondant avec transitions Nuxt natives) - Transitions Nuxt natives (`pageTransition` + ``) + CSS animations - Sécurité contact : reCAPTCHA v3 (invisible) + honeypot + rate limiting Laravel (5 req/min par IP) - Bandeau RGPD intégré à l'immersion narrative (dialogue PNJ/narrateur, style "pacte d'aventurier") - Déploiement : Nginx → Node.js :3000 (Nuxt SSR) + PHP-FPM :9000 (Laravel API) + MariaDB :3306 - CI/CD : Script `deploy.sh` manuel (git pull, build, migrate, restart) - Backups BDD : cron quotidien mysqldump + réplication distante rsync/scp, rétention 7 jours - Environnements : production (`skycel.fr`, branche `prod`) + staging (`staging.skycel.fr`) - Cache : File cache Laravel (driver `file`, pas de Redis) - Double validation : frontend (UX temps réel) + backend (Form Requests Laravel, source de vérité) - Format réponse API : Laravel API Resources avec enveloppe `{ data, meta }` - Gestion langue API : header `Accept-Language` → middleware Laravel → jointure table translations **UX :** - Système de héros : 3 personnages (Recruteur/Client/Dev) impactant vouvoiement, ton narrateur, type de challenges - Narrateur = "Le Bug" (araignée, mascotte micro-entreprise) avec arc de révélation progressive (silhouette sombre → araignée complète en 5 étapes liées à la progression) - Page résumé 30s (`/resume`) : URL directe pour candidatures, accès sans passer par la landing - Déblocage contact après 2 zones visitées (pas de blocage excessif) - Challenge final optionnel (jamais bloquant l'accès au contact) - Challenge post-formulaire ("En attendant que le dev retrouve sa boîte mail...") - "Monde de Code" : révélation finale — paysage en blocs de code ASCII art, avatar Célian au centre - Navigation mobile : "Chemin Libre" vertical (ZoneCards scrollables) au lieu de Konva.js - Bottom bar mobile fixe : Carte, Progression, Paramètres (thumb zone) - Tous les feedbacks système passent par le narrateur (pas de toasts/notifications classiques) - Headless UI / Radix UI pour composants standards accessibles (modals, tooltips, toggles, menus) - Design tokens Tailwind : `sky-dark` (noir→bleu), `sky-accent` (#fa784f orange), `sky-text` (blanc cassé→jaune) - Polices : serif élégante (narrateur/PNJ/narration) + sans-serif moderne (interface/UI) - Approche Mobile First CSS - WCAG AA : contraste ≥ 4.5:1, touch targets 44x44px min, skip links, `aria-live="polite"` narrateur, navigation clavier complète - Sortie de zone par choix narratif du narrateur (pas de bouton "retour" froid) - Couleurs par zone sur la carte (teintes uniques par section) - Espacement aéré : spacing scale de 4px à 128px **Brainstorming :** - Schéma BDD détaillé : 8 tables (projects, skills, skill_project, testimonials, narrator_texts, easter_eggs, user_progress, translations) - Personnalités PNJ : sage, sarcastique, enthousiaste, professionnel - Easter eggs : triggers variés (click, hover, konami, scroll, sequence) - Rewards easter eggs : snippet, anecdote, image, badge - Sauvegarde cloud progression par email (optionnel, phase 2) - Rappel email narratif après X jours (hors scope MVP) ### FR Coverage Map | FR | Epic | Description | |---|---|---| | FR1 | Epic 1 | Double entrée Aventure / Mode Express | | FR2 | Epic 1 | Transitions de page animées seamless | | FR3 | Epic 3 | Narrateur-guide contextuel | | FR4 | Epic 3 | Carte interactive navigation non-linéaire | | FR5 | Epic 2 | Arbre de compétences interactif | | FR6 | Epic 2 | Compétences cliquables → projets liés | | FR7 | Epic 2 | Témoignages dialogues PNJ | | FR8 | Epic 3 | Barre de progression globale | | FR9 | Epic 4 | Choix binaires créant parcours multiples | | FR10 | Epic 4 | Challenge/puzzle avant contact | | FR11 | Epic 4 | Easter eggs cachés | | FR12 | Epic 3 | Sauvegarde progression LocalStorage | | FR13 | Epic 1 | Bilingue FR/EN | | FR14 | Epic 4 | Contact comme récompense finale | ## Epic List ### Epic 1 : Fondations & Double Entrée Le visiteur arrive sur le site, choisit son héros et son mode (Aventure ou Express), et peut naviguer entre les pages avec des transitions immersives. Le site est bilingue et fonctionnel en SSR. **FRs couverts :** FR1, FR2, FR13 ### Epic 2 : Contenu & Découverte Le visiteur explore les zones de contenu : projets (galerie + détail), compétences organisées par catégories avec liens vers les projets associés, témoignages en dialogues PNJ, et parcours en timeline narrative. **FRs couverts :** FR5, FR6, FR7 ### Epic 3 : Navigation Gamifiée & Progression Le visiteur navigue via la carte interactive (Konva.js desktop / Chemin Libre mobile), est accompagné par le narrateur-guide (Le Bug), et voit sa progression sauvegardée automatiquement avec une barre XP. **FRs couverts :** FR3, FR4, FR8, FR12 ### Epic 4 : Chemins Narratifs, Challenge & Contact Le visiteur fait des choix qui créent son parcours unique, relève un challenge optionnel, et accède à la révélation finale "Monde de Code" + formulaire de contact comme récompense narrative. Les easter eggs récompensent l'exploration. **FRs couverts :** FR9, FR10, FR11, FR14 --- ## Epic 1 : Fondations & Double Entrée Le visiteur arrive sur le site, choisit son héros et son mode (Aventure ou Express), et peut naviguer entre les pages avec des transitions immersives. Le site est bilingue et fonctionnel en SSR. ### Story 1.1 : Initialisation du monorepo et infrastructure As a développeur, I want un projet monorepo Nuxt 4 + Laravel 12 initialisé avec les configurations de base, So that le développement peut commencer sur des fondations solides. **Acceptance Criteria:** **Given** un nouveau repository Git **When** le projet est initialisé **Then** la structure monorepo `frontend/` (Nuxt 4) + `api/` (Laravel 12) est en place **And** Nuxt 4 est configuré avec SSR activé, TypeScript, et les modules `@nuxtjs/i18n`, `@nuxtjs/tailwindcss`, `@pinia/nuxt`, `nuxt/image`, `@nuxtjs/sitemap` **And** Laravel 12 est configuré en mode API-only avec CORS autorisant le domaine frontend **And** le middleware API Key (`X-API-Key`) est en place sur les routes API **And** les fichiers `.env.example` existent pour frontend et backend **And** TailwindCSS est configuré avec les design tokens (`sky-dark`, `sky-accent` #fa784f, `sky-text`) **And** les polices sont définies (serif narrateur + sans-serif UI) **And** le `.gitignore` est approprié pour les deux applications ### Story 1.2 : Base de données et migrations initiales As a développeur, I want le schéma de base de données MariaDB avec les tables nécessaires à l'Epic 1, So that l'API peut servir du contenu bilingue. **Acceptance Criteria:** **Given** une connexion MariaDB configurée dans Laravel **When** `php artisan migrate` est exécuté **Then** la table `translations` est créée (id, lang, key_name, value, timestamps) avec index unique (lang, key_name) **And** la table `projects` est créée (id, slug, title_key, description_key, short_description_key, image, url, github_url, date_completed, is_featured, display_order, timestamps) **And** la table `skills` est créée (id, slug, name_key, description_key, icon, category, max_level, display_order) **And** la table `skill_project` est créée (id, skill_id, project_id, level_before, level_after, level_description_key) avec foreign keys **And** les Models Eloquent sont définis avec leurs relations (Project belongsToMany Skill, etc.) **And** des Seeders de base sont disponibles avec données de test en FR et EN **And** `php artisan db:seed` fonctionne correctement ### Story 1.3 : Système i18n frontend + API bilingue As a visiteur, I want voir le site dans ma langue (FR ou EN), So that je comprends le contenu. **Acceptance Criteria:** **Given** le module `@nuxtjs/i18n` configuré avec stratégie `prefix_except_default` **When** le visiteur accède à `/` ou `/en` **Then** le contenu statique UI est affiché dans la langue correspondante via fichiers JSON (`i18n/fr.json`, `i18n/en.json`) **And** les URLs FR sont par défaut (`/`, `/projets`, `/competences`, `/contact`) **And** les URLs EN sont préfixées (`/en`, `/en/projects`, `/en/skills`, `/en/contact`) **And** `useI18n()`, `$t()`, `localePath()`, `switchLocalePath()` fonctionnent en SSR **And** les tags `hreflang` sont générés automatiquement dans le `` **And** l'attribut `lang` du `` est dynamique (fr/en) **And** le middleware Laravel extrait `Accept-Language` et joint la table `translations` pour le contenu dynamique **And** les API Resources Laravel renvoient le contenu traduit selon la langue demandée **And** le fallback est FR si langue non supportée ### Story 1.4 : Layouts, routing et transitions de page As a visiteur, I want une navigation fluide entre les pages avec des transitions immersives, So that l'expérience ressemble à un changement de zone, pas à un rechargement. **Acceptance Criteria:** **Given** la structure de pages Nuxt 4 (`app/pages/`) **When** le visiteur navigue entre les pages **Then** les transitions de page sont animées (fade + slide) via `pageTransition` dans `nuxt.config.ts` **And** la navigation utilise `` pour l'hydration SPA (pas de rechargement) **And** le layout par défaut (`default.vue`) inclut le header avec barre de progression (placeholder) et sélecteur de langue **And** un layout `minimal.vue` existe pour le mode express **And** le `scrollBehavior` est personnalisé (smooth scroll, retour position sauvegardée) **And** `prefers-reduced-motion` désactive les animations de transition via media query CSS **And** une page 404 (`error.vue`) bilingue est en place **And** les meta tags SEO dynamiques fonctionnent via `useHead()` et `useSeoMeta()` **And** le favicon est configuré ### Story 1.5 : Landing page et choix du héros As a visiteur, I want choisir entre l'aventure et le mode express, puis sélectionner mon héros, So that mon expérience est adaptée à mon profil et mon temps disponible. **Acceptance Criteria:** **Given** le visiteur arrive sur la landing page (`/`) **When** la page se charge **Then** deux CTA distincts sont visibles : "Partir à l'aventure" et "Mode express" **And** un texte d'accroche intrigant bilingue est affiché **And** une animation d'entrée subtile est présente (respectant `prefers-reduced-motion`) **And** le design est responsive (mobile + desktop) **And** au clic sur "Partir à l'aventure", le composant `HeroSelector` s'affiche avec 3 cards illustrées (Recruteur, Client, Développeur) avec nom et description courte **And** le héros sélectionné est stocké dans le store Pinia `useProgressionStore` (champ `hero`) **And** au clic sur "Mode express", le visiteur est redirigé vers la page résumé **And** le `HeroSelector` est accessible au clavier (`role="radiogroup"`, flèches pour naviguer, Enter pour sélectionner) ### Story 1.6 : Store Pinia progression et bandeau RGPD As a visiteur, I want que ma progression soit sauvegardée et que mon consentement soit respecté, So that je peux reprendre mon exploration et mes données sont protégées. **Acceptance Criteria:** **Given** le visiteur accède au site **When** le consentement RGPD n'a pas encore été donné **Then** un bandeau de consentement immersif s'affiche (style narratif/dialogue, pas un bandeau classique) **And** le store Pinia `useProgressionStore` est initialisé avec : sessionId (UUID v4), hero, currentPath, visitedSections, completionPercent, easterEggsFound, challengeCompleted, contactUnlocked, narratorStage, choices, consentGiven **And** la persistance LocalStorage est activée via `pinia-plugin-persistedstate` (uniquement après consentement) **And** le store est compatible SSR (initialisation vide côté serveur, réhydratation client) **And** si une progression existante est détectée, un message "Bienvenue à nouveau" est affiché **And** l'action `$reset()` permet de réinitialiser la progression ### Story 1.7 : Page résumé express et mode pressé As a visiteur pressé ou recruteur, I want une vue condensée de toutes les informations essentielles, So that je peux évaluer le développeur en 30 secondes. **Acceptance Criteria:** **Given** le visiteur accède à `/resume` (FR) ou `/en/resume` (EN) directement ou via "Mode express" **When** la page se charge **Then** le contenu affiché comprend : nom, titre, photo/avatar, accroche (5s) **And** les compétences clés avec stack technique sont visibles (10s) **And** 3-4 projets highlights avec liens sont affichés (10s) **And** un CTA de contact direct est visible (5s) **And** un bouton discret "Voir l'aventure" invite à l'expérience complète **And** la page est fonctionnelle en FR et EN **And** les données sont chargées depuis l'API (projets, skills) **And** les meta tags SEO sont optimisés pour cette page **And** le layout `minimal.vue` est utilisé --- ## Epic 2 : Contenu & Découverte Le visiteur explore les zones de contenu : projets (galerie + détail), compétences organisées par catégories avec liens vers les projets associés, témoignages en dialogues PNJ, et parcours en timeline narrative. ### Story 2.1 : Composant ProjectCard As a développeur, I want un composant réutilisable de card de projet, So that je peux afficher les projets de manière cohérente sur la galerie et ailleurs dans le site. **Acceptance Criteria:** **Given** le composant `ProjectCard` est implémenté **When** il reçoit les données d'un projet en props **Then** il affiche l'image du projet (WebP, lazy loading) **And** il affiche le titre traduit selon la langue courante **And** il affiche la description courte traduite **And** un hover effect révèle un CTA "Découvrir" avec animation subtile **And** le composant est cliquable et navigue vers `/projets/{slug}` (ou `/en/projects/{slug}`) **And** le composant respecte `prefers-reduced-motion` pour les animations **And** le composant est responsive (adaptation mobile/desktop) **And** le composant est accessible (focus visible, `role` approprié) ### Story 2.2 : Page Projets - Galerie As a visiteur, I want voir la liste des projets réalisés par le développeur, So that je peux évaluer son expérience et choisir lesquels explorer en détail. **Acceptance Criteria:** **Given** le visiteur accède à `/projets` (FR) ou `/en/projects` (EN) **When** la page se charge **Then** une grille responsive de `ProjectCard` s'affiche **And** les projets sont triés par date avec les "featured" en tête **And** une animation d'entrée progressive des cards est présente (respectant `prefers-reduced-motion`) **And** les données sont chargées depuis l'API `/api/projects` avec le contenu traduit **And** les meta tags SEO sont dynamiques pour cette page **And** le layout s'adapte : grille sur desktop, cards empilées sur mobile ### Story 2.3 : Page Projet - Détail As a visiteur, I want voir les détails d'un projet spécifique, So that je comprends le travail réalisé et les technologies utilisées. **Acceptance Criteria:** **Given** le visiteur accède à `/projets/{slug}` (FR) ou `/en/projects/{slug}` (EN) **When** la page se charge **Then** le titre, la description complète et l'image principale du projet s'affichent **And** la date de réalisation est visible **And** la liste des compétences utilisées s'affiche avec leurs niveaux (avant/après le projet) **And** les liens externes sont présents : URL du projet live (si existe), repository GitHub (si existe) **And** une navigation "Projet précédent / Projet suivant" permet de parcourir les projets **And** un bouton retour vers la galerie est visible **And** les meta tags SEO sont dynamiques (titre, description, image Open Graph) **And** si le slug n'existe pas, une page 404 appropriée s'affiche **And** le design est responsive (adaptation mobile/desktop) ### Story 2.4 : Page Compétences - Affichage par catégories As a visiteur, I want voir les compétences du développeur organisées par catégorie, So that je comprends son profil technique global. **Acceptance Criteria:** **Given** le visiteur accède à `/competences` (FR) ou `/en/skills` (EN) **When** la page se charge **Then** les compétences sont affichées groupées par catégorie (Frontend, Backend, Tools, Soft skills) **And** chaque compétence affiche : icône, nom traduit, niveau actuel (représentation visuelle) **And** les données sont chargées depuis l'API `/api/skills` avec le contenu traduit **And** une animation d'entrée des éléments est présente (respectant `prefers-reduced-motion`) **And** sur desktop : préparé pour accueillir le skill tree vis.js (Epic 3) **And** sur mobile : liste groupée par catégorie avec design adapté **And** les meta tags SEO sont dynamiques pour cette page **And** chaque compétence est visuellement cliquable (affordance) ### Story 2.5 : Compétences cliquables → Projets liés As a visiteur, I want cliquer sur une compétence pour voir les projets qui l'utilisent, So that je peux voir des preuves concrètes de maîtrise. **Acceptance Criteria:** **Given** le visiteur est sur la page Compétences **When** il clique sur une compétence **Then** un panneau/modal s'ouvre avec la liste des projets liés à cette compétence **And** pour chaque projet lié : titre, description courte, lien vers le détail **And** l'indication du niveau avant/après chaque projet est visible (progression) **And** une animation d'ouverture/fermeture fluide est présente (respectant `prefers-reduced-motion`) **And** la fermeture est possible par clic extérieur, bouton close, ou touche Escape **And** le panneau/modal utilise Headless UI pour l'accessibilité **And** la navigation au clavier est fonctionnelle (Tab, Escape, Enter) **And** le focus est piégé dans le modal quand ouvert (`focus trap`) **And** les données viennent de la relation `skill_project` via l'API ### Story 2.6 : Page Témoignages et migrations BDD As a visiteur, I want voir les témoignages des personnes ayant travaillé avec le développeur, So that j'ai une validation sociale de ses compétences. **Acceptance Criteria:** **Given** les migrations Laravel sont exécutées **When** `php artisan migrate` est lancé **Then** la table `testimonials` est créée (id, name, role, company, avatar, text_key, personality ENUM, project_id FK nullable, display_order, is_active, timestamps) **And** les seeders de test sont disponibles avec des témoignages en FR et EN **Given** le visiteur accède à `/temoignages` (FR) ou `/en/testimonials` (EN) **When** la page se charge **Then** la liste des témoignages s'affiche depuis l'API `/api/testimonials` **And** chaque témoignage affiche : nom, rôle, entreprise, avatar, texte traduit **And** la personnalité de chaque PNJ est indiquée visuellement (style différent selon personality) **And** un lien vers le projet associé est présent si pertinent **And** l'ordre d'affichage respecte `display_order` **And** le design est préparé pour accueillir le composant DialoguePNJ (story suivante) **And** les meta tags SEO sont dynamiques pour cette page ### Story 2.7 : Composant Dialogue PNJ As a visiteur, I want lire les témoignages comme des dialogues de personnages style Zelda, So that l'expérience est immersive et mémorable. **Acceptance Criteria:** **Given** le composant `DialoguePNJ` est implémenté **When** il reçoit les données d'un témoignage en props **Then** l'avatar du PNJ s'affiche à gauche avec un style illustratif **And** une bulle de dialogue s'affiche à droite avec le texte **And** l'effet typewriter fait apparaître le texte lettre par lettre **And** un clic ou appui sur Espace accélère l'animation typewriter (x3-x5) **And** la personnalité du PNJ influence le style visuel de la bulle (sage, sarcastique, enthousiaste, professionnel) **And** la police serif narrative est utilisée pour le texte du dialogue **And** `prefers-reduced-motion` affiche le texte complet instantanément **And** le texte complet est accessible via `aria-label` pour les screen readers **And** une navigation entre témoignages est disponible (précédent/suivant) **And** une transition animée s'effectue entre les PNJ **And** un indicateur du témoignage actuel est visible (ex: 2/5) **And** la navigation au clavier est fonctionnelle (flèches gauche/droite) ### Story 2.8 : Page Parcours - Timeline narrative As a visiteur, I want découvrir le parcours professionnel du développeur sous forme de timeline, So that je comprends son évolution et son expérience. **Acceptance Criteria:** **Given** le visiteur accède à `/parcours` (FR) ou `/en/journey` (EN) **When** la page se charge **Then** une timeline verticale affiche les étapes chronologiques du parcours **And** chaque étape affiche : date, titre, description narrative traduite **And** sur desktop : les étapes alternent gauche/droite pour un effet visuel dynamique **And** sur mobile : les étapes sont linéaires (toutes du même côté) **And** une animation d'apparition au scroll est présente (respectant `prefers-reduced-motion`) **And** des icônes ou images illustrent les étapes clés **And** le contenu est bilingue (FR/EN) et chargé depuis l'API ou fichiers i18n **And** les meta tags SEO sont dynamiques pour cette page **And** la police serif narrative est utilisée pour les descriptions --- ## Epic 3 : Navigation Gamifiée & Progression Le visiteur navigue via la carte interactive (Konva.js desktop / Chemin Libre mobile), est accompagné par le narrateur-guide (Le Bug), et voit sa progression sauvegardée automatiquement avec une barre XP. ### Story 3.1 : Table narrator_texts et API narrateur As a développeur, I want une infrastructure pour stocker et servir les textes du narrateur, So that le narrateur peut afficher des messages contextuels variés. **Acceptance Criteria:** **Given** les migrations Laravel sont exécutées **When** `php artisan migrate` est lancé **Then** la table `narrator_texts` est créée (id, context, text_key, variant, timestamps) **And** les contextes définis incluent : intro, transition_projects, transition_skills, transition_testimonials, transition_journey, hint, encouragement_25, encouragement_50, encouragement_75, contact_unlocked, welcome_back **And** plusieurs variantes par contexte permettent une sélection aléatoire **And** les seeders insèrent les textes de base en FR et EN dans la table `translations` **Given** l'API `/api/narrator/{context}` est appelée **When** un contexte valide est fourni **Then** un texte aléatoire parmi les variantes de ce contexte est retourné **And** le texte est traduit selon le header `Accept-Language` **And** le ton est adapté au héros (vouvoiement pour Recruteur, tutoiement pour Client/Dev) ### Story 3.2 : Composant NarratorBubble (Le Bug) As a visiteur, I want voir un narrateur-guide qui m'accompagne dans mon exploration, So that je me sens guidé et l'expérience est immersive. **Acceptance Criteria:** **Given** le composant `NarratorBubble` est implémenté **When** le narrateur doit afficher un message **Then** une bulle apparaît en bas de l'écran (desktop) ou au-dessus de la bottom bar (mobile) **And** l'avatar du Bug (araignée) s'affiche avec son apparence selon le `narratorStage` du store **And** le texte apparaît avec effet typewriter (lettre par lettre) **And** un clic ou Espace accélère l'animation typewriter **And** la bulle peut être fermée/minimisée sans bloquer la navigation **And** le composant utilise `aria-live="polite"` et `role="status"` pour l'accessibilité **And** `prefers-reduced-motion` affiche le texte instantanément **And** la police serif narrative est utilisée pour le texte **And** l'animation d'apparition/disparition est fluide et non-bloquante ### Story 3.3 : Textes narrateur contextuels et arc de révélation As a visiteur, I want que le narrateur réagisse à mes actions et évolue visuellement, So that l'expérience est personnalisée et le narrateur devient familier. **Acceptance Criteria:** **Given** le visiteur navigue sur le site **When** il effectue des actions clés **Then** le narrateur affiche un message d'accueil à l'arrivée (adapté au héros choisi) **And** des messages de transition s'affichent entre les zones **And** des encouragements apparaissent à 25%, 50%, 75% de progression **And** des indices s'affichent si le visiteur semble inactif (> 30s sans action) **And** un message spécial "Bienvenue à nouveau" s'affiche si progression existante détectée **And** le message de déblocage du contact s'affiche après 2 zones visitées **Given** le visiteur progresse dans l'exploration **When** le `completionPercent` atteint certains seuils **Then** le `narratorStage` du store est mis à jour (1→5) **And** l'apparence du Bug évolue : silhouette sombre (1) → forme vague (2) → pattes visibles (3) → araignée reconnaissable (4) → mascotte complète révélée (5) **And** le ton du narrateur évolue de mystérieux à complice ### Story 3.4 : Barre de progression globale (XP bar) As a visiteur, I want voir ma progression dans l'exploration du site, So that je sais combien il me reste à découvrir. **Acceptance Criteria:** **Given** le visiteur est en mode Aventure **When** il navigue sur le site **Then** une barre de progression discrète s'affiche dans le header **And** le pourcentage est calculé selon les sections visitées (Projets, Compétences, Témoignages, Parcours) **And** l'animation de la barre est fluide lors des mises à jour **And** un tooltip au hover indique les sections visitées et restantes **And** le design évoque une barre XP style RPG (cohérent avec `sky-accent`) **And** la barre respecte `prefers-reduced-motion` (pas d'animation si activé) **And** sur mobile, la progression est accessible via la bottom bar **And** la barre n'est pas visible en mode Express/Résumé ### Story 3.5 : Logique de progression et déblocage contact As a visiteur, I want que ma progression débloque l'accès au contact, So that l'exploration est récompensée sans être frustrante. **Acceptance Criteria:** **Given** le store `useProgressionStore` est actif **When** le visiteur visite une nouvelle zone **Then** la zone est ajoutée à `visitedSections` **And** le `completionPercent` est recalculé automatiquement **And** la progression est persistée en LocalStorage (si consentement RGPD donné) **Given** le visiteur a visité 2 zones ou plus **When** la condition est atteinte **Then** `contactUnlocked` passe à `true` **And** le narrateur annonce le déblocage avec un message spécial **And** la zone Contact s'illumine sur la carte (si visible) **And** le visiteur peut continuer à explorer ou aller au contact **Given** le visiteur revient sur le site **When** une progression existe en LocalStorage **Then** le store est réhydraté avec l'état sauvegardé **And** le narrateur affiche "Bienvenue à nouveau" **And** la carte affiche l'état correct des zones visitées ### Story 3.6 : Carte interactive desktop (Konva.js) As a visiteur desktop, I want naviguer via une carte interactive visuelle, So that j'explore librement le portfolio comme un monde. **Acceptance Criteria:** **Given** le visiteur est sur desktop (≥ 1024px) et accède à la carte **When** la carte se charge **Then** un canvas Konva.js affiche une carte stylisée avec les zones (Projets, Compétences, Parcours, Témoignages, Contact) **And** le composant est chargé en lazy-loading (`.client.vue`) pour respecter le budget JS **And** chaque zone a une apparence distincte (teinte unique, icône) **And** les zones visitées ont une apparence différente des zones non visitées **And** la zone Contact est verrouillée visuellement si `contactUnlocked` est `false` **And** la position actuelle du visiteur est marquée sur la carte **And** au hover sur une zone : le nom et le statut s'affichent (tooltip) **And** au clic sur une zone : navigation vers la section correspondante avec transition **And** un curseur personnalisé indique les zones cliquables **And** la navigation au clavier est fonctionnelle (Tab entre zones, Enter pour naviguer) **And** les zones ont des labels ARIA descriptifs ### Story 3.7 : Navigation mobile - Chemin Libre et Bottom Bar As a visiteur mobile, I want naviguer facilement avec une interface adaptée au tactile, So that l'expérience reste immersive sur petit écran. **Acceptance Criteria:** **Given** le visiteur est sur mobile (< 768px) **When** il accède à la navigation **Then** le "Chemin Libre" affiche les zones en cards verticales scrollables (`ZoneCard`) **And** chaque `ZoneCard` affiche : illustration, nom de la zone, statut (visité/nouveau/verrouillé) **And** une ligne décorative relie les cards visuellement (effet chemin) **And** un tap sur une zone navigue vers la section correspondante **And** la zone Contact affiche un cadenas si `contactUnlocked` est `false` **Given** la bottom bar mobile est affichée **When** le visiteur interagit **Then** 3 icônes sont accessibles : Carte (ouvre le Chemin Libre), Progression (affiche le %), Paramètres **And** les touch targets font au minimum 48x48px **And** la bottom bar est fixe et toujours visible **And** le narrateur s'affiche au-dessus de la bottom bar quand actif --- ## Epic 4 : Chemins Narratifs, Challenge & Contact Le visiteur fait des choix qui créent son parcours unique, relève un challenge optionnel, et accède à la révélation finale "Monde de Code" + formulaire de contact comme récompense narrative. Les easter eggs récompensent l'exploration. ### Story 4.1 : Composant ChoiceCards et choix narratifs As a visiteur, I want faire des choix qui influencent mon parcours, So that mon expérience est unique et personnalisée. **Acceptance Criteria:** **Given** le composant `ChoiceCards` est implémenté **When** le narrateur propose un choix **Then** 2 cards s'affichent côte à côte (desktop) ou empilées (mobile) **And** chaque card affiche : icône, texte narratif du choix **And** un hover/focus highlight la card sélectionnable **And** un clic enregistre le choix dans `choices` du store Pinia **And** une transition animée mène vers la destination choisie **And** le composant est accessible (`role="radiogroup"`, navigation clavier, focus visible) **And** `prefers-reduced-motion` simplifie les animations **And** le style est cohérent avec l'univers narratif (police serif, couleurs des zones) ### Story 4.2 : Intro narrative et premier choix As a visiteur aventurier, I want une introduction narrative captivante suivie d'un premier choix, So that je suis immergé dès le début de l'aventure. **Acceptance Criteria:** **Given** le visiteur a sélectionné son héros sur la landing page **When** il commence l'aventure **Then** une séquence d'intro narrative s'affiche avec le narrateur (Le Bug) **And** le texte présente le "héros mystérieux" (le développeur) à découvrir **And** l'effet typewriter anime le texte (skippable par clic/Espace) **And** l'ambiance visuelle est immersive (fond sombre, illustrations) **And** un bouton "Continuer" permet d'avancer **And** à la fin de l'intro, le premier choix binaire s'affiche via `ChoiceCards` **And** le choix propose deux zones à explorer en premier (ex: Projets vs Compétences) **And** le contenu est bilingue (FR/EN) et adapté au héros (vouvoiement/tutoiement) **And** la durée de l'intro est courte (15-30s max, skippable) ### Story 4.3 : Chemins narratifs différenciés As a visiteur, I want que mes choix aient un impact visible sur mon parcours, So that je sens que mon expérience est vraiment personnalisée. **Acceptance Criteria:** **Given** le visiteur fait des choix tout au long de l'aventure **When** il navigue entre les zones **Then** 2-3 points de choix binaires créent 4-8 parcours possibles **And** chaque choix est enregistré dans `choices` du store **And** l'ordre suggéré des zones varie selon le chemin choisi **And** les textes du narrateur s'adaptent au chemin (transitions contextuelles) **And** tous les chemins permettent de visiter tout le contenu **And** tous les chemins mènent au contact (pas de "mauvais" choix) **And** le `currentPath` du store reflète le chemin actuel **And** à la fin de chaque zone, le narrateur propose un choix vers la suite ### Story 4.4 : Table easter_eggs et système de détection As a développeur, I want une infrastructure pour gérer les easter eggs cachés, So that je peux ajouter des surprises récompensant l'exploration. **Acceptance Criteria:** **Given** les migrations Laravel sont exécutées **When** `php artisan migrate` est lancé **Then** la table `easter_eggs` est créée (id, slug, location, trigger_type ENUM, reward_type ENUM, reward_key, difficulty, is_active, timestamps) **And** les trigger_types incluent : click, hover, konami, scroll, sequence **And** les reward_types incluent : snippet, anecdote, image, badge **And** les seeders insèrent 5-10 easter eggs avec leurs récompenses traduites **Given** l'API `/api/easter-eggs` est appelée **When** la requête est faite **Then** les métadonnées des easter eggs actifs sont retournées (slug, location, trigger_type) **And** les réponses/récompenses ne sont PAS incluses (pour éviter la triche) **Given** l'API `/api/easter-eggs/{slug}/validate` est appelée **When** un slug valide est fourni **Then** la récompense traduite est retournée **And** l'easter egg est marqué comme trouvé côté client (store) ### Story 4.5 : Easter eggs - Implémentation UI et collection As a visiteur curieux, I want découvrir des surprises cachées et voir ma collection, So that l'exploration est récompensée. **Acceptance Criteria:** **Given** des easter eggs sont placés sur différentes pages **When** le visiteur déclenche un easter egg (clic, hover, konami, scroll, sequence) **Then** une animation de découverte s'affiche (popup, effet visuel) **And** la récompense est affichée (snippet de code, anecdote, image, badge) **And** le narrateur réagit avec enthousiasme **And** une notification "Easter egg trouvé ! (X/Y)" s'affiche **And** le slug est ajouté à `easterEggsFound` dans le store **And** un bouton permet de fermer et continuer **Given** le visiteur accède à sa collection (via paramètres ou zone dédiée) **When** la collection s'affiche **Then** une grille montre les easter eggs trouvés et des silhouettes mystère pour les non-trouvés **And** les détails sont visibles pour les découverts **And** un compteur X/Y indique la progression **And** un badge spécial s'affiche si 100% trouvés ### Story 4.6 : Page Challenge - Structure et puzzle As a visiteur, I want relever un défi optionnel avant d'accéder au contact, So that l'accès au développeur est une récompense méritée (mais pas bloquante). **Acceptance Criteria:** **Given** le visiteur accède à `/challenge` (après avoir débloqué le contact) **When** la page se charge **Then** une introduction narrative "Une dernière épreuve..." s'affiche **And** un puzzle logique/code simple est présenté (réordonner, compléter, décoder) **And** la difficulté est calibrée : 1-3 minutes pour résoudre **And** le thème est lié au développement/code **And** un système d'indices est disponible (bouton "Besoin d'aide ?") **And** 3 niveaux d'indices progressifs sont proposés **And** après 3 indices, une option "Passer" apparaît **And** le challenge est TOUJOURS skippable (bouton discret "Passer directement au contact") **And** une validation avec feedback clair indique succès/échec **And** une animation de succès célèbre la réussite **And** `challengeCompleted` est mis à `true` dans le store si réussi ### Story 4.7 : Révélation "Monde de Code" As a visiteur ayant complété le parcours, I want vivre un moment waouh de révélation finale, So that la découverte du développeur est mémorable. **Acceptance Criteria:** **Given** le visiteur accède à la zone Contact (après challenge ou skip) **When** la révélation se déclenche **Then** une transition immersive mène vers le "Monde de Code" **And** un paysage composé de blocs de code ASCII art s'affiche (montagnes, arbres, ville en code) **And** le code scroll/apparaît progressivement (animation) **And** l'avatar illustré de Célian est révélé au centre du monde de code **And** le narrateur (Le Bug) commente : "Tu l'as trouvé !" **And** le message "Tu m'as trouvé !" s'affiche avec effet de célébration **And** sur mobile, une version allégée mais émotionnellement équivalente s'affiche **And** `prefers-reduced-motion` affiche une version statique **And** une description alternative est disponible pour les screen readers **And** un bouton permet de continuer vers le formulaire de contact ### Story 4.8 : Page Contact - Formulaire et célébration As a visiteur ayant trouvé le développeur, I want le contacter facilement avec une célébration, So that l'envoi du message est une conclusion satisfaisante. **Acceptance Criteria:** **Given** le visiteur est sur la page Contact après la révélation **When** la page s'affiche **Then** un message de félicitations avec stats du parcours est visible (zones visitées, easter eggs trouvés, temps passé) **And** un formulaire de contact s'affiche : nom (requis), email (requis), message (requis) **And** la validation temps réel est effectuée côté frontend (champs requis, format email) **And** les erreurs sont communiquées par le narrateur (pas de messages d'erreur classiques) **And** un champ honeypot invisible est présent (anti-spam) **And** reCAPTCHA v3 est intégré de manière invisible **And** le bouton d'envoi utilise la couleur accent (`sky-accent`) **Given** le formulaire est soumis **When** les données sont envoyées à l'API **Then** la validation backend Laravel (Form Request) vérifie les données **And** le rate limiting (5 req/min par IP) est appliqué **And** l'email est envoyé via Laravel Mail **And** une animation de célébration s'affiche (confettis ou similaire) **And** le narrateur confirme l'envoi avec un message personnalisé **And** en cas d'erreur, le narrateur explique le problème avec bienveillance ### Story 4.9 : Challenge post-formulaire As a visiteur ayant envoyé un message, I want m'amuser en attendant la réponse, So that le temps d'attente est transformé en moment de jeu. **Acceptance Criteria:** **Given** le formulaire de contact a été envoyé avec succès **When** la confirmation s'affiche **Then** un message "En attendant que le développeur retrouve le chemin vers sa boîte mail..." est affiché **And** un challenge optionnel bonus est proposé **And** le challenge est différent du challenge principal (mini-jeu, quiz, exploration) **And** le visiteur peut fermer et quitter à tout moment **And** la participation est totalement optionnelle **And** le résultat n'impacte rien (juste pour le fun) **And** le narrateur commente avec humour