feat(epic-4): chemins narratifs, easter eggs, challenge et contact

Epic 4: Chemins Narratifs, Challenge & Contact

Stories implementees:
- 4.1: Composant ChoiceCards pour choix narratifs binaires
- 4.2: Sequence d'intro narrative avec Le Bug
- 4.3: Chemins narratifs differencies avec useNarrativePath
- 4.4: Table easter_eggs et systeme de detection (API + composable)
- 4.5: Easter eggs UI (popup, notification, collection)
- 4.6: Page challenge avec puzzle de code
- 4.7: Page revelation "Monde de Code"
- 4.8: Page contact avec formulaire et stats

Fichiers crees:
- Frontend: ChoiceCards, IntroSequence, ZoneEndChoice, EasterEggPopup,
  CodePuzzle, ChallengeSuccess, CodeWorld, et pages intro/challenge/revelation
- API: EasterEggController, Model, Migration, Seeder

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 13:35:12 +01:00
parent 64b1a33d10
commit 7e87a341a2
38 changed files with 3037 additions and 96 deletions

View File

@@ -0,0 +1,88 @@
<template>
<button
type="button"
class="choice-card group relative flex flex-col items-center p-6 rounded-xl border-2 transition-all duration-300 focus:outline-none"
:class="[
selected
? 'border-sky-accent bg-sky-accent/10 scale-105 shadow-lg shadow-sky-accent/20'
: 'border-sky-text/20 bg-sky-dark/50 hover:border-sky-accent/50 hover:bg-sky-dark/80',
disabled && 'opacity-50 cursor-not-allowed',
]"
:style="{ '--zone-color': choice.zoneColor }"
:disabled="disabled"
:aria-checked="selected"
role="radio"
@click="emit('select')"
>
<!-- Glow effect au hover -->
<div
class="absolute inset-0 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
:style="{ boxShadow: `0 0 30px ${choice.zoneColor}40` }"
/>
<!-- Icône -->
<div
class="w-16 h-16 rounded-full flex items-center justify-center text-4xl mb-4"
:style="{ backgroundColor: `${choice.zoneColor}20` }"
>
{{ choice.icon }}
</div>
<!-- Texte narratif -->
<p class="font-narrative text-lg text-sky-text text-center leading-relaxed">
{{ text }}
</p>
<!-- Indicateur de sélection -->
<div
v-if="selected"
class="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-sky-accent flex items-center justify-center"
>
<span class="text-white text-sm">&#x2713;</span>
</div>
</button>
</template>
<script setup lang="ts">
import type { Choice } from '~/types/choice'
const props = defineProps<{
choice: Choice
selected: boolean
disabled: boolean
}>()
const emit = defineEmits<{
select: []
}>()
const { locale } = useI18n()
const text = computed(() => {
return locale.value === 'fr' ? props.choice.textFr : props.choice.textEn
})
</script>
<style scoped>
.choice-card:focus-visible {
outline: 2px solid var(--sky-accent, #38bdf8);
outline-offset: 4px;
}
.choice-card:not(:disabled):hover {
transform: translateY(-4px);
}
@media (prefers-reduced-motion: reduce) {
.choice-card,
.choice-card * {
transition: none !important;
animation: none !important;
transform: none !important;
}
.choice-card:not(:disabled):hover {
transform: none !important;
}
}
</style>