diff --git a/src/lib/data.ts b/src/lib/data.ts
index cf26ee642b8e04b85d2253eb05d67a80b21a7d19..bf92eed3b24fb43f0dbdea15f39b15429fc843e7 100644
--- a/src/lib/data.ts
+++ b/src/lib/data.ts
@@ -5,7 +5,7 @@ import { PROMOTION_QUERY, USER_DETAILS_QUERY } from './graphql/queries';
 type UserId = string;
 type PromoCache = {
 	lastUpdateMs: number;
-	promo: Set<UserId>;
+	promotion: Set<UserId>;
 };
 
 const cache = new Map<number, PromoCache>();
@@ -26,8 +26,8 @@ async function cacheImages(users: UserId[]): Promise<void> {
 	}
 }
 
-async function fetchPromotion(promotion: number): Promise<PromoCache> {
-	const array = await Array.fromAsync(pageIterator(PROMOTION_QUERY, { promotion }));
+async function fetchPromotion(year: number): Promise<PromoCache> {
+	const array = await Array.fromAsync(pageIterator(PROMOTION_QUERY, { year }));
 	const users = array.map((node) => node.id);
 
 	// background task
@@ -35,30 +35,31 @@ async function fetchPromotion(promotion: number): Promise<PromoCache> {
 
 	return {
 		lastUpdateMs: new Date().getTime(),
-		promo: new Set(users)
+		promotion: new Set(users)
 	};
 }
 
-export async function getPromotion(promo: number): Promise<Set<UserId>> {
-	if (cache.has(promo)) {
-		const data = cache.get(promo)!;
-		if (new Date().getTime() - data.lastUpdateMs < CACHE_DURATION_MS) return data.promo;
+export async function getPromotion(year: number): Promise<Set<UserId>> {
+	if (cache.has(year)) {
+		const data = cache.get(year)!;
+		if (new Date().getTime() - data.lastUpdateMs < CACHE_DURATION_MS) return data.promotion;
 	}
-	const freshData = await fetchPromotion(promo);
-	cache.set(promo, freshData);
-	return freshData.promo;
+	console.log('year', year, 'not in cache, fetching');
+	const freshData = await fetchPromotion(year);
+	cache.set(year, freshData);
+	return freshData.promotion;
 }
 
 export async function* promotionIterator(
 	min: number,
 	max: number
 ): AsyncGenerator<UserId, void, undefined> {
-	for (let i = min; i <= max; i++) {
-		const promo = await getPromotion(i);
-		yield* promo;
+	for (let i = min; i < max; i++) {
+		const promotion = await getPromotion(i);
+		yield* promotion;
 	}
 }
 
-export function getPromotionRange(min: number, max: number): Promise<Set<UserId>> {
+export function getPromotionRange(min: number, max = min): Promise<Set<UserId>> {
 	return Array.fromAsync(promotionIterator(min, max)).then((array) => new Set(array));
 }
diff --git a/src/lib/game.ts b/src/lib/game.ts
index 5ad81ad880d41462c91bf09c702b0f15d5e8fb7d..dcd2697a876147c51d4c9ecdb2d04f56647b8eae 100644
--- a/src/lib/game.ts
+++ b/src/lib/game.ts
@@ -3,6 +3,7 @@ import { SecureCookie } from './cookie';
 import { z } from 'zod';
 
 export enum GameStage {
+	NEW,
 	PLAYING,
 	SOLUTION,
 	NEXT,
@@ -15,6 +16,9 @@ export const gameStateSchema = z.object({
 	score: z.number(),
 	options: z.array(z.string()),
 	stage: z.nativeEnum(GameStage),
+	year: z.number(),
+	maxYear: z.number().optional(),
+	label: z.string(),
 	solution: z.string()
 });
 
@@ -22,7 +26,7 @@ export type GameState = z.infer<typeof gameStateSchema>;
 
 export class Game {
 	state: GameState;
-	protected cookie_name = 'qui-est-ce';
+	protected cookie_name = 'quiestce-quiz';
 	protected cookie: SecureCookie<GameState>;
 
 	constructor(protected cookies: Cookies) {
@@ -32,12 +36,14 @@ export class Game {
 
 	protected defaultState(): GameState {
 		return {
-			stage: GameStage.NEXT,
+			stage: GameStage.NEW,
 			history: [],
 			step: 0,
 			score: 0,
+			year: 0,
 			options: [],
-			solution: ''
+			solution: '',
+			label: '?'
 		};
 	}
 
diff --git a/src/lib/graphql/queries.ts b/src/lib/graphql/queries.ts
index c9bb02d93671c3f85bb9fe58aea82e47a9bb7bd7..f3aa344b015407f234b11ec08c66b8b6c4f8ae84 100644
--- a/src/lib/graphql/queries.ts
+++ b/src/lib/graphql/queries.ts
@@ -1,11 +1,11 @@
 import { graphql } from '.';
 
 export const PROMOTION_QUERY = graphql(`
-	query GetPromotion($first: Int!, $after: String, $promotion: Int!) {
+	query GetYear($first: Int!, $after: String, $year: Int!) {
 		page: users(
 			first: $first
 			after: $after
-			filter: { promotion: { eq: [$promotion] }, nickname: { null: false }, photo: { null: false } }
+			filter: { year: { eq: [$year] }, nickname: { null: false }, photo: { null: false } }
 		) {
 			pageInfo {
 				endCursor
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 705385da79df790db128b858708751cf3cea8f39..c92a033577a09462f8d171059cc79323ff802668 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -11,3 +11,5 @@ export function getRandomItems<T>(array: T[], count: number): T[] {
 	items.push(item);
 	return items;
 }
+
+export const sum = (a: number, b: number) => a + b;
diff --git a/src/routes/quiz/+page.server.ts b/src/routes/quiz/+page.server.ts
index ec9a25ef5e591c1b492d3ed9d296b267d27f31f0..943b4f0f61836b71ec6a298dab4b224ad729bfa0 100644
--- a/src/routes/quiz/+page.server.ts
+++ b/src/routes/quiz/+page.server.ts
@@ -3,8 +3,8 @@ import { zod } from 'sveltekit-superforms/adapters';
 import { schema } from './schema';
 import { fail, redirect } from '@sveltejs/kit';
 import { getRandomItems } from '$lib/utils';
-import { GameStage, Game } from '$lib/game';
-import { getPromotion } from '$lib/data';
+import { GameStage, Game, type GameState } from '$lib/game';
+import { getPromotionRange } from '$lib/data';
 import { client } from '$lib/graphql';
 import { USER_DETAILS_QUERY } from '$lib/graphql/queries';
 import { handleGqlError } from '$lib/graphql/error';
@@ -14,21 +14,30 @@ type Option = {
 	label: string;
 };
 
-export async function load(event) {
-	const game = new Game(event.cookies);
-
-	if (game.state.stage === GameStage.GAME_OVER) redirect(303, '/quiz/game-over');
+async function next(state: GameState) {
+	const all = await getPromotionRange(state.year, state.maxYear);
+	const previous = new Set(state.history);
+	const available = all.difference(previous);
 
-	if (game.state.stage === GameStage.NEXT) {
-		const all = await getPromotion(2023);
+	state.options = getRandomItems(Array.from(available), 4);
+	state.solution = getRandomItems([...state.options], 1)[0];
+	state.stage = GameStage.PLAYING;
+	state.step++;
+}
 
-		const previous = new Set(game.state.history);
-		const available = all.difference(previous);
+export async function load(event) {
+	const game = new Game(event.cookies);
 
-		game.state.options = getRandomItems(Array.from(available), 4);
-		game.state.solution = getRandomItems([...game.state.options], 1)[0];
-		game.state.stage = GameStage.PLAYING;
-		game.state.step++;
+	switch (game.state.stage) {
+		case GameStage.NEW:
+			redirect(303, '/quiz/new');
+			break;
+		case GameStage.GAME_OVER:
+			redirect(303, '/quiz/game-over');
+			break;
+		case GameStage.NEXT:
+			await next(game.state);
+			break;
 	}
 
 	const details = await client
@@ -46,7 +55,14 @@ export async function load(event) {
 	const photo = users.find((node) => node.id === game.state.solution)?.photo;
 
 	const form = await superValidate(zod(schema));
-	return { form, options, score: game.state.score, step: game.state.step, photo };
+	return {
+		form,
+		options,
+		score: game.state.score,
+		step: game.state.step,
+		photo,
+		label: game.state.label
+	};
 }
 
 export const actions = {
diff --git a/src/routes/quiz/+page.svelte b/src/routes/quiz/+page.svelte
index 5ef86b11af1b52738ed4215eaa71b4d4e81d3f3b..4533b4c700a084b28a86fff6c5fcc783ba93b399 100644
--- a/src/routes/quiz/+page.svelte
+++ b/src/routes/quiz/+page.svelte
@@ -5,29 +5,31 @@
 	export let data;
 	export let form;
 
-	const form2 = superForm(data.form, {
+	const sForm = superForm(data.form, {
 		// On récupère les valeurs après affichage des résultats
 		resetForm: false
 	});
 
-	const { form: formData, enhance } = form2;
+	const { form: formData, enhance } = sForm;
 
 	const questionAmount = 10;
 </script>
 
 <div class="relative mx-auto my-12 w-full grow">
 	<section class="relative m-auto flex flex-col items-center">
-		<h1 class="absolute -top-8 text-xl">Qui est-ce ?</h1>
+		<h1 class="absolute -top-8 text-xl">Qui est-ce ? {data.label}</h1>
 		<div class="relative">
-			<span class="absolute -left-14 flex flex-col items-end">
-				<span class="text-9xl leading-[0.4] text-random-300"> {data.step} </span>
-				<span> / {questionAmount} </span>
-			</span>
-			<span
-				class="absolute right-[calc(100%_+_0.5rem)] top-24 whitespace-nowrap pt-4 text-xl text-zinc-800 before:absolute before:right-0 before:top-0 before:h-1.5 before:w-10 before:bg-zinc-800"
-			>
-				{data.score}
-				<small>pts</small>
+			<span class="absolute right-[calc(100%_+_0.5rem)] text-right">
+				<span class="flex flex-col items-end">
+					<span class="text-9xl leading-[0.4] text-random-300"> {data.step} </span>
+					<span> / {questionAmount} </span>
+				</span>
+				<span class="inline-block h-1.5 w-10 bg-zinc-800"></span>
+				<br />
+				<span class="top-24 pt-4 text-xl text-zinc-800">
+					{data.score}
+					<small>pts</small>
+				</span>
 			</span>
 			<div
 				class="relative z-10 flex h-64 w-64 items-center justify-center overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-white before:absolute before:-z-10 before:h-32 before:w-32 before:rounded-full before:bg-slate-300 before:opacity-100 before:transition-[0.65s] before:duration-[ease-in-out] after:absolute after:-z-10 after:h-32 after:w-32 after:scale-0 after:rounded-full after:border-solid after:border-slate-300 after:transition-[0.4s] after:duration-[ease-in-out]"
@@ -35,7 +37,7 @@
 				<img src={data.photo?.url} alt="Chargement..." class="w-auto" />
 			</div>
 			<form method="post" use:enhance>
-				<Fieldset form={form2} name="choice">
+				<Fieldset form={sForm} name="choice">
 					<div
 						class="relative -top-6 z-20 mx-auto my-0 flex w-44 flex-col items-center space-y-0.5 rounded-2xl bg-zinc-800 px-5 py-0"
 					>
@@ -53,8 +55,7 @@
 									/>
 									<Label
 										tabindex={0}
-										data-solution={form?.solution || null}
-										class="relative block cursor-pointer select-none overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-slate-300 p-2 text-center text-lg transition-[0.45s] before:absolute before:left-2/4 before:top-2/4 before:-z-10 before:h-[200px] before:w-[200px] before:-translate-x-2/4 before:-translate-y-2/4 before:scale-0 before:rounded-full before:bg-indigo-300 before:transition-[0.2s] before:duration-[ease-in-out] focus:[outline:none] active:before:-translate-x-2/4 active:before:-translate-y-2/4 active:before:scale-100 peer-enabled:hover:translate-y-[-3px] peer-enabled:hover:bg-slate-400 peer-enabled:focus:border-indigo-500 peer-checked:peer-enabled:bg-indigo-300 data-[solution]:cursor-default data-[solution]:text-[#94acbd] peer-checked:data-[solution]:bg-red-400 peer-checked:data-[solution]:text-inherit peer-data-[valid]:!bg-lime-400 peer-data-[valid]:text-inherit"
+										class="relative block cursor-pointer select-none overflow-hidden rounded-2xl border-6 border-solid border-zinc-800 bg-slate-300 p-2 text-center text-lg transition-[0.45s] before:absolute before:left-2/4 before:top-2/4 before:-z-10 before:h-[200px] before:w-[200px] before:-translate-x-2/4 before:-translate-y-2/4 before:scale-0 before:rounded-full before:bg-indigo-300 before:transition-[0.2s] before:duration-[ease-in-out] focus:[outline:none] active:before:-translate-x-2/4 active:before:-translate-y-2/4 active:before:scale-100 peer-enabled:hover:translate-y-[-3px] peer-enabled:hover:bg-slate-400 peer-enabled:focus:border-indigo-500 peer-checked:peer-enabled:bg-indigo-300 peer-disabled:cursor-default peer-disabled:text-[#94acbd] peer-checked:peer-disabled:bg-red-400 peer-checked:peer-disabled:text-inherit peer-data-[valid]:!bg-lime-400 peer-data-[valid]:text-inherit"
 									>
 										{option.label}
 									</Label>
diff --git a/src/routes/quiz/new/+page.server.ts b/src/routes/quiz/new/+page.server.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f839073e5defb3d2beca3fd9e544b55ecc736230
--- /dev/null
+++ b/src/routes/quiz/new/+page.server.ts
@@ -0,0 +1,109 @@
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { schema } from './schema';
+import { fail, redirect } from '@sveltejs/kit';
+import { Game, GameStage } from '$lib/game';
+import { getPromotion } from '$lib/data';
+import images from './images';
+import { sum } from '$lib/utils';
+
+type BaseLevel = {
+	year: number;
+	maxYear?: number;
+	name: string;
+	image: string;
+};
+
+type Level = BaseLevel & {
+	size: number;
+};
+
+const VIIEUX_YEAR = 20;
+
+export async function load(event) {
+	const game = new Game(event.cookies);
+
+	if (game.state.stage !== GameStage.NEW) redirect(303, '/quiz');
+
+	const baseLevels: BaseLevel[] = [
+		{
+			year: 1,
+			name: '1A',
+			image: images.baby
+		},
+		{
+			year: 2,
+			name: '2A',
+			image: images.baby
+		},
+		{
+			year: 3,
+			name: '3A',
+			image: images.student
+		},
+		{
+			year: 1,
+			maxYear: 3,
+			name: '1-3A',
+			image: images.student
+		},
+		{
+			year: 4,
+			name: '4A',
+			image: images.student
+		},
+		{
+			year: 5,
+			name: '5A',
+			image: images.student
+		},
+		{
+			year: 4,
+			maxYear: VIIEUX_YEAR,
+			name: 'Viieux',
+			image: images.student
+		},
+		{
+			year: 1,
+			maxYear: VIIEUX_YEAR,
+			name: 'IIEns',
+			image: images.student
+		}
+	];
+
+	const promoSizes = await Promise.all(
+		Array.from(new Array(VIIEUX_YEAR), (_, i) => getPromotion(i + 1).then((promo) => promo.size))
+	);
+
+	const levels: Level[] = baseLevels.map((level) => {
+		const end = (level.maxYear ?? level.year) + 1;
+		return {
+			size: promoSizes.slice(level.year, end).reduce(sum, 0),
+			...level
+		};
+	});
+
+	const form = await superValidate(zod(schema));
+	return { form, levels };
+}
+
+export const actions = {
+	async default(event) {
+		const form = await superValidate(event, zod(schema));
+
+		if (!form.valid) {
+			return fail(400, { form });
+		}
+
+		const state = new Game(event.cookies);
+
+		state.state.year = form.data.year;
+		state.state.label = form.data.label;
+		state.state.maxYear = form.data.maxYear;
+		state.state.stage = GameStage.NEXT;
+
+		state.save();
+
+		redirect(303, '/quiz');
+	}
+};
diff --git a/src/routes/quiz/new/+page.svelte b/src/routes/quiz/new/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d7bc2c15f12787e3e40aed0f52befe904b0c31f1
--- /dev/null
+++ b/src/routes/quiz/new/+page.svelte
@@ -0,0 +1,48 @@
+<script lang="ts">
+	import { superForm } from 'sveltekit-superforms';
+	import { Control, Field } from 'formsnap';
+
+	export let data;
+
+	const form = superForm(data.form, {
+		// On récupère les valeurs après affichage des résultats
+		resetForm: false
+	});
+	const { enhance } = form;
+</script>
+
+<div class="m-16">
+	<div class="flex flex-wrap items-center justify-center gap-8 text-gray-700">
+		{#each data.levels as level}
+			<form method="post" use:enhance>
+				<Field {form} name="year">
+					<Control let:attrs>
+						<input type="hidden" {...attrs} value={level.year} />
+					</Control>
+				</Field>
+				<Field {form} name="maxYear">
+					<Control let:attrs>
+						<input type="hidden" {...attrs} value={level.maxYear} />
+					</Control>
+				</Field>
+				<Field {form} name="label">
+					<Control let:attrs>
+						<input type="hidden" {...attrs} value={level.name} />
+					</Control>
+				</Field>
+				<button
+					type="submit"
+					class="col-span-1 flex h-72 w-72 flex-col divide-y divide-gray-200 rounded-2xl border-6 border-solid border-zinc-800 bg-slate-100 text-center shadow transition-[0.45s] hover:translate-y-[-3px] hover:bg-slate-300 focus:border-indigo-500"
+				>
+					<div class="flex flex-1 flex-col p-8">
+						<img class="mx-auto h-32 w-32 flex-shrink-0" src={level.image} alt="" />
+						<h1 class="mt-4 text-6xl font-medium">{level.name}</h1>
+						<dl class="mt-1 flex flex-grow flex-col justify-between">
+							<dd class="text-sm font-light text-gray-500">{level.size} personnes</dd>
+						</dl>
+					</div>
+				</button>
+			</form>
+		{/each}
+	</div>
+</div>
diff --git a/src/routes/quiz/new/images/baby.png b/src/routes/quiz/new/images/baby.png
new file mode 100644
index 0000000000000000000000000000000000000000..60d09632825cea2366b7f0f19f7f2802ec29042c
Binary files /dev/null and b/src/routes/quiz/new/images/baby.png differ
diff --git a/src/routes/quiz/new/images/index.ts b/src/routes/quiz/new/images/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a947a639425f81e7eb45c8e11b46e1c8ba4f5810
--- /dev/null
+++ b/src/routes/quiz/new/images/index.ts
@@ -0,0 +1,7 @@
+import baby from './baby.png';
+import student from './student.png';
+
+export default {
+	baby,
+	student
+};
diff --git a/src/routes/quiz/new/images/student.png b/src/routes/quiz/new/images/student.png
new file mode 100644
index 0000000000000000000000000000000000000000..4df8222c20902f02cdb712ab556b047e1fa2ad68
Binary files /dev/null and b/src/routes/quiz/new/images/student.png differ
diff --git a/src/routes/quiz/new/schema.ts b/src/routes/quiz/new/schema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d561d4e5638964a0596660da8115716e51fb6aa9
--- /dev/null
+++ b/src/routes/quiz/new/schema.ts
@@ -0,0 +1,7 @@
+import { z } from 'zod';
+
+export const schema = z.object({
+	year: z.number(),
+	maxYear: z.number().optional(),
+	label: z.string()
+});