diff --git a/.gitignore b/.gitignore
index e844b39764fa90f8213c3290014af1036df4a030..dee9b5768d01bb3195034b4adff5fdfe436bd554 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ vite.config.js.timestamp-*
 vite.config.ts.timestamp-*
 
 static/*.jpg
+
+$houdini
+.vscode
diff --git a/.graphqlrc.yaml b/.graphqlrc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..427c16f1c51f90ea58d430cb51dbbb15954f6d0e
--- /dev/null
+++ b/.graphqlrc.yaml
@@ -0,0 +1,9 @@
+projects:
+  default:
+    schema:
+      - ./src/lib/graphql/schema.gql
+      - ./$houdini/graphql/schema.graphql
+    documents:
+      - '**/*.gql'
+      - '**/*.svelte'
+      - ./$houdini/graphql/documents.gql
diff --git a/houdini.config.js b/houdini.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..e86aa667dd22a62cc00862421e320a05ec723991
--- /dev/null
+++ b/houdini.config.js
@@ -0,0 +1,42 @@
+/** @type {import('houdini').ConfigFile} */
+export default {
+	plugins: {
+		'houdini-svelte': {
+			client: './src/lib/graphql/client'
+		}
+	},
+	schemaPath: './src/lib/graphql/schema.gql',
+	watchSchema: {
+		url(env) {
+			return `${env.API_ORIGIN}/graphql/v0`;
+		},
+		headers: {
+			Authorization(env) {
+				return `Basic ${env.API_TOKEN}`;
+			}
+		}
+	},
+	scalars: {
+		Date: {
+			type: 'string'
+		},
+		DateTime: {
+			type: 'string'
+		},
+		Url: {
+			type: 'string'
+		},
+		SmolStr: {
+			type: 'string'
+		},
+		UUID: {
+			type: 'string'
+		},
+		Identifier: {
+			type: 'string'
+		},
+		Base64: {
+			type: 'string'
+		}
+	}
+};
diff --git a/package.json b/package.json
index fa89baaddd9f36502fdddc081c78aed72075ba3f..faebf6d9918d88c76836b7903b0324e148cd68ab 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
 		"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
 		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
 		"lint": "prettier --check . && eslint .",
-		"format": "prettier --write ."
+		"format": "prettier --write .",
+		"pull-schema": "houdini pull-schema"
 	},
 	"devDependencies": {
 		"@sveltejs/adapter-auto": "^3.0.0",
@@ -21,6 +22,8 @@
 		"eslint-config-prettier": "^9.1.0",
 		"eslint-plugin-svelte": "^2.36.0",
 		"globals": "^15.0.0",
+		"houdini": "^1.2.56",
+		"houdini-svelte": "^1.2.56",
 		"prettier": "^3.1.1",
 		"prettier-plugin-svelte": "^3.1.2",
 		"prettier-plugin-tailwindcss": "^0.6.5",
@@ -34,6 +37,7 @@
 	"type": "module",
 	"packageManager": "pnpm@9.7.0+sha512.dc09430156b427f5ecfc79888899e1c39d2d690f004be70e05230b72cb173d96839587545d09429b55ac3c429c801b4dc3c0e002f653830a420fa2dd4e3cf9cf",
 	"dependencies": {
+		"cryptr": "^6.3.0",
 		"formsnap": "^1.0.1",
 		"sveltekit-superforms": "^2.17.0",
 		"zod": "^3.23.8"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d472da5d24c7a8bb5a4b23f9ad5bf7ea155d2f7b..cf87dd6be214b5c697d0be425d664369390e9308 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,25 +8,28 @@ importers:
 
   .:
     dependencies:
+      cryptr:
+        specifier: ^6.3.0
+        version: 6.3.0
       formsnap:
         specifier: ^1.0.1
-        version: 1.0.1(svelte@4.2.18)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18))
+        version: 1.0.1(svelte@4.2.18)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18))
       sveltekit-superforms:
         specifier: ^2.17.0
-        version: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)
+        version: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)
       zod:
         specifier: ^3.23.8
         version: 3.23.8
     devDependencies:
       '@sveltejs/adapter-auto':
         specifier: ^3.0.0
-        version: 3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))
+        version: 3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))
       '@sveltejs/kit':
         specifier: ^2.0.0
-        version: 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)
+        version: 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       '@sveltejs/vite-plugin-svelte':
         specifier: ^3.0.0
-        version: 3.1.1(svelte@4.2.18)(vite@5.4.2)
+        version: 3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       '@types/eslint':
         specifier: ^9.6.0
         version: 9.6.0
@@ -45,6 +48,12 @@ importers:
       globals:
         specifier: ^15.0.0
         version: 15.9.0
+      houdini:
+        specifier: ^1.2.56
+        version: 1.2.56
+      houdini-svelte:
+        specifier: ^1.2.56
+        version: 1.2.56(@types/node@22.5.0)
       prettier:
         specifier: ^3.1.1
         version: 3.3.3
@@ -71,7 +80,7 @@ importers:
         version: 8.2.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.4)
       vite:
         specifier: ^5.0.3
-        version: 5.4.2
+        version: 5.4.2(@types/node@22.5.0)
 
 packages:
 
@@ -89,142 +98,307 @@ packages:
   '@ark/util@0.1.0':
     resolution: {integrity: sha512-qCLYICQoCy3kEKDVwirQp8qvxhY7NJd8BhhoHaj1l3wCFAk9NUbcDsxAkPStZEMdPI/d7NcbGJe8SWZuRG2twQ==}
 
+  '@babel/helper-string-parser@7.24.8':
+    resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.24.7':
+    resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.25.4':
+    resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
   '@babel/runtime@7.25.4':
     resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/types@7.25.4':
+    resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@clack/core@0.3.4':
+    resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==}
+
+  '@clack/prompts@0.6.3':
+    resolution: {integrity: sha512-AM+kFmAHawpUQv2q9+mcB6jLKxXGjgu/r2EQjEwujgpCdzrST6BJqYw00GRn56/L/Izw5U7ImoLmy00X/r80Pw==}
+    bundledDependencies:
+      - is-unicode-supported
+
+  '@envelop/core@4.0.3':
+    resolution: {integrity: sha512-O0Vz8E0TObT6ijAob8jYFVJavcGywKThM3UAsxUIBBVPYZTMiqI9lo2gmAnbMUnrDcAYkUTZEW9FDYPRdF5l6g==}
+    engines: {node: '>=16.0.0'}
+
+  '@envelop/types@4.0.1':
+    resolution: {integrity: sha512-ULo27/doEsP7uUhm2iTnElx13qTO6I5FKvmLoX41cpfuw8x6e0NUFknoqhEsLzAbgz8xVS5mjwcxGCXh4lDYzg==}
+    engines: {node: '>=16.0.0'}
+
   '@esbuild/aix-ppc64@0.21.5':
     resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [aix]
 
+  '@esbuild/android-arm64@0.18.20':
+    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+
   '@esbuild/android-arm64@0.21.5':
     resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
 
+  '@esbuild/android-arm@0.18.20':
+    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+
   '@esbuild/android-arm@0.21.5':
     resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [android]
 
+  '@esbuild/android-x64@0.18.20':
+    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+
   '@esbuild/android-x64@0.21.5':
     resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
 
+  '@esbuild/darwin-arm64@0.18.20':
+    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+
   '@esbuild/darwin-arm64@0.21.5':
     resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
 
+  '@esbuild/darwin-x64@0.18.20':
+    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+
   '@esbuild/darwin-x64@0.21.5':
     resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
 
+  '@esbuild/freebsd-arm64@0.18.20':
+    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+
   '@esbuild/freebsd-arm64@0.21.5':
     resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
 
+  '@esbuild/freebsd-x64@0.18.20':
+    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+
   '@esbuild/freebsd-x64@0.21.5':
     resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
 
+  '@esbuild/linux-arm64@0.18.20':
+    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+
   '@esbuild/linux-arm64@0.21.5':
     resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
 
+  '@esbuild/linux-arm@0.18.20':
+    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+
   '@esbuild/linux-arm@0.21.5':
     resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
 
+  '@esbuild/linux-ia32@0.18.20':
+    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+
   '@esbuild/linux-ia32@0.21.5':
     resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
 
+  '@esbuild/linux-loong64@0.18.20':
+    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+
   '@esbuild/linux-loong64@0.21.5':
     resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
     engines: {node: '>=12'}
     cpu: [loong64]
     os: [linux]
 
+  '@esbuild/linux-mips64el@0.18.20':
+    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+
   '@esbuild/linux-mips64el@0.21.5':
     resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
 
+  '@esbuild/linux-ppc64@0.18.20':
+    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+
   '@esbuild/linux-ppc64@0.21.5':
     resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
 
+  '@esbuild/linux-riscv64@0.18.20':
+    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+
   '@esbuild/linux-riscv64@0.21.5':
     resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
 
+  '@esbuild/linux-s390x@0.18.20':
+    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+
   '@esbuild/linux-s390x@0.21.5':
     resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
 
+  '@esbuild/linux-x64@0.18.20':
+    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+
   '@esbuild/linux-x64@0.21.5':
     resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
 
+  '@esbuild/netbsd-x64@0.18.20':
+    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+
   '@esbuild/netbsd-x64@0.21.5':
     resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
 
+  '@esbuild/openbsd-x64@0.18.20':
+    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+
   '@esbuild/openbsd-x64@0.21.5':
     resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
 
+  '@esbuild/sunos-x64@0.18.20':
+    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+
   '@esbuild/sunos-x64@0.21.5':
     resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
 
+  '@esbuild/win32-arm64@0.18.20':
+    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+
   '@esbuild/win32-arm64@0.21.5':
     resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
 
+  '@esbuild/win32-ia32@0.18.20':
+    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+
   '@esbuild/win32-ia32@0.21.5':
     resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
 
+  '@esbuild/win32-x64@0.18.20':
+    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+
   '@esbuild/win32-x64@0.21.5':
     resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
     engines: {node: '>=12'}
@@ -260,10 +434,70 @@ packages:
   '@exodus/schemasafe@1.3.0':
     resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==}
 
+  '@fastify/busboy@2.1.1':
+    resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
+    engines: {node: '>=14'}
+
   '@gcornut/valibot-json-schema@0.31.0':
     resolution: {integrity: sha512-3xGptCurm23e7nuPQkdrE5rEs1FeTPHhAUsBuwwqG4/YeZLwJOoYZv+fmsppUEfo5y9lzUwNQrNqLS/q7HMc7g==}
     hasBin: true
 
+  '@graphql-tools/executor@1.3.1':
+    resolution: {integrity: sha512-tgJDdGf9SCAm64ofEMZdv925u6/J+eTmv36TGNLxgP2DpCJsZ6gnJ4A+0D28EazDXqJIvMiPd+3d+o3cCRCAnQ==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/merge@8.4.2':
+    resolution: {integrity: sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/merge@9.0.6':
+    resolution: {integrity: sha512-TmkzFTFVieHnqu9mPTF6RxAQltaprpDQnM5HMTPSyMLXnJGMTvdWejV0yORKj7DW1YSi791/sUnKf8HytepBFQ==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/schema@10.0.6':
+    resolution: {integrity: sha512-EIJgPRGzpvDFEjVp+RF1zNNYIC36BYuIeZ514jFoJnI6IdxyVyIRDLx/ykgMdaa1pKQerpfdqDnsF4JnZoDHSQ==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/schema@9.0.19':
+    resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/utils@10.5.4':
+    resolution: {integrity: sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-tools/utils@9.2.1':
+    resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==}
+    peerDependencies:
+      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-typed-document-node/core@3.2.0':
+    resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
+    peerDependencies:
+      graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
+
+  '@graphql-yoga/logger@1.0.0':
+    resolution: {integrity: sha512-JYoxwnPggH2BfO+dWlWZkDeFhyFZqaTRGLvFhy+Pjp2UxitEW6nDrw+pEDw/K9tJwMjIFMmTT9VfTqrnESmBHg==}
+    engines: {node: '>=16.0.0'}
+
+  '@graphql-yoga/subscription@4.0.0':
+    resolution: {integrity: sha512-0qsN/BPPZNMoC2CZ8i+P6PgiJyHh1H35aKDt37qARBDaIOKDQuvEOq7+4txUKElcmXi7DYFo109FkhSQoEajrg==}
+    engines: {node: '>=16.0.0'}
+
+  '@graphql-yoga/typed-event-target@2.0.0':
+    resolution: {integrity: sha512-oA/VGxGmaSDym1glOHrltw43qZsFwLLjBwvh57B79UKX/vo3+UQcRgOyE44c5RP7DCYjkrC2tuArZmb6jCzysw==}
+    engines: {node: '>=16.0.0'}
+
   '@hapi/hoek@9.3.0':
     resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
 
@@ -300,6 +534,13 @@ packages:
   '@jridgewell/trace-mapping@0.3.25':
     resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
+  '@kamilkisiela/fast-url-parser@1.1.4':
+    resolution: {integrity: sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==}
+
+  '@kitql/helpers@0.8.9':
+    resolution: {integrity: sha512-uDBFBvCYUT4UaZZKv7gJejQvbrOp4YyI1S0Z92DPiMbyLq0DPDXz3Lt2ZqUZKlQrinBX+W1TO6w0RudEX6Q6WA==}
+    engines: {node: ^16.14 || >=18}
+
   '@nodelib/fs.scandir@2.1.5':
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -323,6 +564,9 @@ packages:
     resolution: {integrity: sha512-xhhEcEvhQC8mP5oOr5hbE4CmUgmw/IPV1jhpGg2xSkzoFrt9i8YVqBQt9744EFesi5F7pBheWozg63RUBM/5JA==}
     engines: {node: '>=18.16.0'}
 
+  '@repeaterjs/repeater@3.0.6':
+    resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==}
+
   '@rollup/rollup-android-arm-eabi@4.21.0':
     resolution: {integrity: sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==}
     cpu: [arm]
@@ -423,6 +667,14 @@ packages:
     peerDependencies:
       '@sveltejs/kit': ^2.0.0
 
+  '@sveltejs/kit@1.30.4':
+    resolution: {integrity: sha512-JSQIQT6XvdchCRQEm7BABxPC56WP5RYVONAi+09S8tmzeP43fBsRlr95bFmsTQM2RHBldfgQk+jgdnsKI75daA==}
+    engines: {node: ^16.14 || >=18}
+    hasBin: true
+    peerDependencies:
+      svelte: ^3.54.0 || ^4.0.0-next.0 || ^5.0.0-next.0
+      vite: ^4.0.0
+
   '@sveltejs/kit@2.5.24':
     resolution: {integrity: sha512-Nr2oxsCsDfEkdS/zzQQQbsPYTbu692Qs3/iE3L7VHzCVjG2+WujF9oMUozWI7GuX98KxYSoPMlAsfmDLSg44hQ==}
     engines: {node: '>=18.13'}
@@ -432,6 +684,14 @@ packages:
       svelte: ^4.0.0 || ^5.0.0-next.0
       vite: ^5.0.3
 
+  '@sveltejs/vite-plugin-svelte-inspector@1.0.4':
+    resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==}
+    engines: {node: ^14.18.0 || >= 16}
+    peerDependencies:
+      '@sveltejs/vite-plugin-svelte': ^2.2.0
+      svelte: ^3.54.0 || ^4.0.0
+      vite: ^4.0.0
+
   '@sveltejs/vite-plugin-svelte-inspector@2.1.0':
     resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==}
     engines: {node: ^18.0.0 || >=20}
@@ -440,6 +700,13 @@ packages:
       svelte: ^4.0.0 || ^5.0.0-next.0
       vite: ^5.0.0
 
+  '@sveltejs/vite-plugin-svelte@2.5.3':
+    resolution: {integrity: sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w==}
+    engines: {node: ^14.18.0 || >= 16}
+    peerDependencies:
+      svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0
+      vite: ^4.0.0
+
   '@sveltejs/vite-plugin-svelte@3.1.1':
     resolution: {integrity: sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==}
     engines: {node: ^18.0.0 || >=20}
@@ -447,6 +714,12 @@ packages:
       svelte: ^4.0.0 || ^5.0.0-next.0
       vite: ^5.0.0
 
+  '@types/braces@3.0.4':
+    resolution: {integrity: sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==}
+
+  '@types/cookie@0.5.4':
+    resolution: {integrity: sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==}
+
   '@types/cookie@0.6.0':
     resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
 
@@ -456,9 +729,18 @@ packages:
   '@types/estree@1.0.5':
     resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
 
+  '@types/fs-extra@9.0.13':
+    resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
+
   '@types/json-schema@7.0.15':
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
 
+  '@types/micromatch@4.0.9':
+    resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==}
+
+  '@types/node@22.5.0':
+    resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==}
+
   '@types/pug@2.0.10':
     resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
 
@@ -522,6 +804,9 @@ packages:
     resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
+  '@ungap/structured-clone@1.2.0':
+    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+
   '@vinejs/compiler@2.5.0':
     resolution: {integrity: sha512-hg4ekaB5Y2zh+IWzBiC/WCDWrIfpVnKu/ubUvelKlidc/VbulsexoFRw5kJGHZenPVI5YzNnDeTdYSALkTV7jQ==}
     engines: {node: '>=18.0.0'}
@@ -530,6 +815,22 @@ packages:
     resolution: {integrity: sha512-Qq3XxbA26jzqS9ICifkqzT399lMQZ2fWtqeV3luI2as+UIK7qDifJFU2Q4W3q3IB5VXoWxgwAZSZEO0em9I/qQ==}
     engines: {node: '>=18.16.0'}
 
+  '@whatwg-node/events@0.1.2':
+    resolution: {integrity: sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==}
+    engines: {node: '>=18.0.0'}
+
+  '@whatwg-node/fetch@0.9.21':
+    resolution: {integrity: sha512-Wt0jPb+04JjobK0pAAN7mEHxVHcGA9HoP3OyCsZtyAecNQeADXCZ1MihFwVwjsgaRYuGVmNlsCmLxlG6mor8Gw==}
+    engines: {node: '>=18.0.0'}
+
+  '@whatwg-node/node-fetch@0.5.25':
+    resolution: {integrity: sha512-m6TrxcJlS8ptYLTQL+Ex931RFJsoCQtBQWBNHi5b0xHS0C7FJGUJl1asYZ7MdOhZqdiMVcs1lNJeHsfzyUNjOg==}
+    engines: {node: '>=18.0.0'}
+
+  '@whatwg-node/server@0.9.49':
+    resolution: {integrity: sha512-3KzLXw80gWnTsQ746G/LFdCThTPfDodjQs4PnmoNuPa6XUOl4HWq8TlJpxtmnEEB+y+UYLal+3VQ68dtYlbUDQ==}
+    engines: {node: '>=18.0.0'}
+
   acorn-jsx@5.3.2:
     resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
     peerDependencies:
@@ -582,6 +883,10 @@ packages:
     resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
     engines: {node: '>=8'}
 
+  ast-types@0.16.1:
+    resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
+    engines: {node: '>=4'}
+
   autoprefixer@10.4.20:
     resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
     engines: {node: ^10 || ^12 || >=14}
@@ -622,6 +927,13 @@ packages:
   buffer-from@1.1.2:
     resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
 
+  builtins@5.1.0:
+    resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==}
+
+  busboy@1.6.0:
+    resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+    engines: {node: '>=10.16.0'}
+
   callsites@3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
@@ -659,17 +971,32 @@ packages:
     resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
     engines: {node: '>= 6'}
 
+  commander@9.5.0:
+    resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+    engines: {node: ^12.20.0 || >=14}
+
   concat-map@0.0.1:
     resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
 
+  cookie@0.5.0:
+    resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+    engines: {node: '>= 0.6'}
+
   cookie@0.6.0:
     resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
     engines: {node: '>= 0.6'}
 
+  cross-inspect@1.0.1:
+    resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==}
+    engines: {node: '>=16.0.0'}
+
   cross-spawn@7.0.3:
     resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
     engines: {node: '>= 8'}
 
+  cryptr@6.3.0:
+    resolution: {integrity: sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ==}
+
   css-tree@2.3.1:
     resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
     engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
@@ -679,6 +1006,10 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
+  data-uri-to-buffer@4.0.1:
+    resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
+    engines: {node: '>= 12'}
+
   dayjs@1.11.13:
     resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
 
@@ -706,6 +1037,9 @@ packages:
     resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
     engines: {node: '>=8'}
 
+  devalue@4.3.3:
+    resolution: {integrity: sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg==}
+
   devalue@5.0.0:
     resolution: {integrity: sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==}
 
@@ -719,6 +1053,10 @@ packages:
   dlv@1.1.3:
     resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
 
+  dset@3.1.3:
+    resolution: {integrity: sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==}
+    engines: {node: '>=4'}
+
   eastasianwidth@0.2.0:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
 
@@ -740,6 +1078,11 @@ packages:
     peerDependencies:
       esbuild: '*'
 
+  esbuild@0.18.20:
+    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+    engines: {node: '>=12'}
+    hasBin: true
+
   esbuild@0.21.5:
     resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
     engines: {node: '>=12'}
@@ -812,6 +1155,11 @@ packages:
     resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
+  esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+
   esquery@1.6.0:
     resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
     engines: {node: '>=0.10'}
@@ -831,6 +1179,13 @@ packages:
     resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
     engines: {node: '>=0.10.0'}
 
+  execa@6.1.0:
+    resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  fast-decode-uri-component@1.0.1:
+    resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+
   fast-deep-equal@3.1.3:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 
@@ -844,9 +1199,16 @@ packages:
   fast-levenshtein@2.0.6:
     resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
 
+  fast-querystring@1.1.2:
+    resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+
   fastq@1.17.1:
     resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
 
+  fetch-blob@3.2.0:
+    resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
+    engines: {node: ^12.20 || >= 14.13}
+
   file-entry-cache@8.0.0:
     resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
     engines: {node: '>=16.0.0'}
@@ -870,6 +1232,10 @@ packages:
     resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
     engines: {node: '>=14'}
 
+  formdata-polyfill@4.0.10:
+    resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+    engines: {node: '>=12.20.0'}
+
   formsnap@1.0.1:
     resolution: {integrity: sha512-TvU9CoLSiacW1c7wXhLiyVpyy/LBfG0CEFDbs3M3jrsxBSrkTpsuhbQ8JYKY3CNCmIhZlgxCH+Vqr7RBF9G53w==}
     peerDependencies:
@@ -879,6 +1245,13 @@ packages:
   fraction.js@4.3.7:
     resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
 
+  fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+
+  fs-monkey@1.0.6:
+    resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==}
+
   fs.realpath@1.0.0:
     resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
 
@@ -890,6 +1263,10 @@ packages:
   function-bind@1.1.2:
     resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 
+  get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+
   glob-parent@5.1.2:
     resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
     engines: {node: '>= 6'}
@@ -906,6 +1283,11 @@ packages:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
     deprecated: Glob versions prior to v9 are no longer supported
 
+  glob@8.1.0:
+    resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+    engines: {node: '>=12'}
+    deprecated: Glob versions prior to v9 are no longer supported
+
   globals@14.0.0:
     resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
     engines: {node: '>=18'}
@@ -930,6 +1312,16 @@ packages:
   graphemer@1.4.0:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
 
+  graphql-yoga@4.0.5:
+    resolution: {integrity: sha512-vIbJU9QX5RP4PoxbMCHcfOlt/3EsC/0uLdAOlKaiUvlwJDTFCaIHo2X10vL4i/27Gw8g90ECIwm2YbmeLDwcqg==}
+    engines: {node: '>=16.0.0'}
+    peerDependencies:
+      graphql: ^15.2.0 || ^16.0.0
+
+  graphql@15.9.0:
+    resolution: {integrity: sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==}
+    engines: {node: '>= 10.x'}
+
   has-flag@4.0.0:
     resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
     engines: {node: '>=8'}
@@ -938,6 +1330,17 @@ packages:
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
 
+  houdini-svelte@1.2.56:
+    resolution: {integrity: sha512-t2eDPcMXstdTMv694KukZgmhJHy5WrB2dFZyVFMXaWHNT3ETtMFG99Mg0HXdxzSFzaiWXJnZIHneKdJ/bSZPaA==}
+
+  houdini@1.2.56:
+    resolution: {integrity: sha512-AqYuXuuCQMrkpePhNjZHnmUeIi8iOPBHOq7WmmNR1fmUhPhSFQAXOzI8EFCwwDB4h8W393pVSzH7bOZsx8h0Iw==}
+    hasBin: true
+
+  human-signals@3.0.1:
+    resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
+    engines: {node: '>=12.20.0'}
+
   ignore@5.3.2:
     resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
     engines: {node: '>= 4'}
@@ -991,6 +1394,10 @@ packages:
   is-reference@3.0.2:
     resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
 
+  is-stream@3.0.0:
+    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
   isexe@2.0.0:
     resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
 
@@ -1021,6 +1428,9 @@ packages:
   json-stable-stringify-without-jsonify@1.0.1:
     resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
 
+  jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+
   just-clone@6.2.0:
     resolution: {integrity: sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==}
 
@@ -1068,17 +1478,32 @@ packages:
   mdn-data@2.0.30:
     resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
 
+  memfs@3.5.3:
+    resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
+    engines: {node: '>= 4.0.0'}
+
   memoize-weak@1.0.2:
     resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==}
 
+  merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
   merge2@1.4.1:
     resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
     engines: {node: '>= 8'}
 
+  micromatch@4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+
   micromatch@4.0.7:
     resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
     engines: {node: '>=8.6'}
 
+  mimic-fn@4.0.0:
+    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+    engines: {node: '>=12'}
+
   min-indent@1.0.1:
     resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
     engines: {node: '>=4'}
@@ -1086,6 +1511,10 @@ packages:
   minimatch@3.1.2:
     resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
 
+  minimatch@5.1.6:
+    resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+    engines: {node: '>=10'}
+
   minimatch@9.0.5:
     resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
     engines: {node: '>=16 || 14 >=14.17'}
@@ -1105,6 +1534,10 @@ packages:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
 
+  mrmime@1.0.1:
+    resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
+    engines: {node: '>=10'}
+
   mrmime@2.0.0:
     resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
     engines: {node: '>=10'}
@@ -1128,6 +1561,14 @@ packages:
   natural-compare@1.4.0:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
 
+  node-domexception@1.0.0:
+    resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+    engines: {node: '>=10.5.0'}
+
+  node-fetch@3.3.2:
+    resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
   node-releases@2.0.18:
     resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
 
@@ -1143,6 +1584,13 @@ packages:
     resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
     engines: {node: '>=14.16'}
 
+  npm-run-path@5.3.0:
+    resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+  npx-import@1.1.4:
+    resolution: {integrity: sha512-3ShymTWOgqGyNlh5lMJAejLuIv3W1K3fbI5Ewc6YErZU3Sp0PqsNs8UIU1O8z5+KVl/Du5ag56Gza9vdorGEoA==}
+
   object-assign@4.1.1:
     resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
     engines: {node: '>=0.10.0'}
@@ -1154,6 +1602,10 @@ packages:
   once@1.4.0:
     resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
 
+  onetime@6.0.0:
+    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+    engines: {node: '>=12'}
+
   optionator@0.9.4:
     resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
     engines: {node: '>= 0.8.0'}
@@ -1173,6 +1625,9 @@ packages:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
 
+  parse-package-name@1.0.0:
+    resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==}
+
   path-exists@4.0.0:
     resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
     engines: {node: '>=8'}
@@ -1185,6 +1640,10 @@ packages:
     resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
     engines: {node: '>=8'}
 
+  path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+
   path-parse@1.0.7:
     resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
 
@@ -1366,6 +1825,10 @@ packages:
     resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
     engines: {node: '>=8.10.0'}
 
+  recast@0.23.9:
+    resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==}
+    engines: {node: '>= 4'}
+
   regenerator-runtime@0.14.1:
     resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
 
@@ -1386,6 +1849,11 @@ packages:
     deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
+  rollup@3.29.4:
+    resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
+    engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+    hasBin: true
+
   rollup@4.21.0:
     resolution: {integrity: sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -1417,6 +1885,9 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
+  signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
   signal-exit@4.1.0:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
@@ -1425,6 +1896,9 @@ packages:
     resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
     engines: {node: '>= 10'}
 
+  sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
   slash@3.0.0:
     resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
     engines: {node: '>=8'}
@@ -1444,6 +1918,10 @@ packages:
     resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
     engines: {node: '>=0.10.0'}
 
+  streamsearch@1.1.0:
+    resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+    engines: {node: '>=10.0.0'}
+
   string-width@4.2.3:
     resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
     engines: {node: '>=8'}
@@ -1460,6 +1938,10 @@ packages:
     resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
     engines: {node: '>=12'}
 
+  strip-final-newline@3.0.0:
+    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+    engines: {node: '>=12'}
+
   strip-indent@3.0.0:
     resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
     engines: {node: '>=8'}
@@ -1500,6 +1982,12 @@ packages:
       svelte:
         optional: true
 
+  svelte-hmr@0.15.3:
+    resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==}
+    engines: {node: ^12.20 || ^14.13.1 || >= 16}
+    peerDependencies:
+      svelte: ^3.19.0 || ^4.0.0
+
   svelte-hmr@0.16.0:
     resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==}
     engines: {node: ^12.20 || ^14.13.1 || >= 16}
@@ -1543,6 +2031,10 @@ packages:
       typescript:
         optional: true
 
+  svelte@3.59.2:
+    resolution: {integrity: sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==}
+    engines: {node: '>= 8'}
+
   svelte@4.2.18:
     resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==}
     engines: {node: '>=16'}
@@ -1574,6 +2066,13 @@ packages:
   tiny-glob@0.2.9:
     resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
 
+  tiny-invariant@1.3.3:
+    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
+  to-fast-properties@2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
   to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -1604,6 +2103,9 @@ packages:
   tslib@2.4.0:
     resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
 
+  tslib@2.7.0:
+    resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
+
   type-check@0.4.0:
     resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
     engines: {node: '>= 0.8.0'}
@@ -1626,6 +2128,17 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
+  undici-types@6.19.8:
+    resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
+
+  undici@5.28.4:
+    resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
+    engines: {node: '>=14.0'}
+
+  universalify@2.0.1:
+    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+    engines: {node: '>= 10.0.0'}
+
   update-browserslist-db@1.1.0:
     resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
     hasBin: true
@@ -1635,6 +2148,9 @@ packages:
   uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
 
+  urlpattern-polyfill@10.0.0:
+    resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==}
+
   util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 
@@ -1644,20 +2160,31 @@ packages:
   valibot@0.35.0:
     resolution: {integrity: sha512-+i2aCRkReTrd5KBN/dW2BrPOvFnU5LXTV2xjZnjnqUIO8YUx6P2+MgRrkwF2FhkexgyKq/NIZdPdknhHf5A/Ww==}
 
+  validate-npm-package-name@4.0.0:
+    resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==}
+    engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+
   validator@13.12.0:
     resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==}
     engines: {node: '>= 0.10'}
 
-  vite@5.4.2:
-    resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
-    engines: {node: ^18.0.0 || >=20.0.0}
+  value-or-promise@1.0.12:
+    resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==}
+    engines: {node: '>=12'}
+
+  vite-plugin-watch-and-run@1.7.0:
+    resolution: {integrity: sha512-f6TUUxDvOeFPMJ1/NDK8N1y/65w8h4jPZGsuOOYVnaK4lkutN95rTNAunsr0fcgTVo1BRUMxDTY7iFlsGuiCig==}
+    engines: {node: ^16.14 || >=18}
+
+  vite@4.5.3:
+    resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==}
+    engines: {node: ^14.18.0 || >=16.0.0}
     hasBin: true
     peerDependencies:
-      '@types/node': ^18.0.0 || >=20.0.0
+      '@types/node': '>= 14'
       less: '*'
       lightningcss: ^1.21.0
       sass: '*'
-      sass-embedded: '*'
       stylus: '*'
       sugarss: '*'
       terser: ^5.4.0
@@ -1670,8 +2197,6 @@ packages:
         optional: true
       sass:
         optional: true
-      sass-embedded:
-        optional: true
       stylus:
         optional: true
       sugarss:
@@ -1679,14 +2204,49 @@ packages:
       terser:
         optional: true
 
-  vitefu@0.2.5:
-    resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
-    peerDependencies:
-      vite: ^3.0.0 || ^4.0.0 || ^5.0.0
-    peerDependenciesMeta:
+  vite@5.4.2:
+    resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+
+  vitefu@0.2.5:
+    resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
+    peerDependencies:
+      vite: ^3.0.0 || ^4.0.0 || ^5.0.0
+    peerDependenciesMeta:
       vite:
         optional: true
 
+  web-streams-polyfill@3.3.3:
+    resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
+    engines: {node: '>= 8'}
+
   which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
     engines: {node: '>= 8'}
@@ -1748,77 +2308,177 @@ snapshots:
   '@ark/util@0.1.0':
     optional: true
 
+  '@babel/helper-string-parser@7.24.8': {}
+
+  '@babel/helper-validator-identifier@7.24.7': {}
+
+  '@babel/parser@7.25.4':
+    dependencies:
+      '@babel/types': 7.25.4
+
   '@babel/runtime@7.25.4':
     dependencies:
       regenerator-runtime: 0.14.1
     optional: true
 
+  '@babel/types@7.25.4':
+    dependencies:
+      '@babel/helper-string-parser': 7.24.8
+      '@babel/helper-validator-identifier': 7.24.7
+      to-fast-properties: 2.0.0
+
+  '@clack/core@0.3.4':
+    dependencies:
+      picocolors: 1.0.1
+      sisteransi: 1.0.5
+
+  '@clack/prompts@0.6.3':
+    dependencies:
+      '@clack/core': 0.3.4
+      picocolors: 1.0.1
+      sisteransi: 1.0.5
+
+  '@envelop/core@4.0.3':
+    dependencies:
+      '@envelop/types': 4.0.1
+      tslib: 2.7.0
+
+  '@envelop/types@4.0.1':
+    dependencies:
+      tslib: 2.7.0
+
   '@esbuild/aix-ppc64@0.21.5':
     optional: true
 
+  '@esbuild/android-arm64@0.18.20':
+    optional: true
+
   '@esbuild/android-arm64@0.21.5':
     optional: true
 
+  '@esbuild/android-arm@0.18.20':
+    optional: true
+
   '@esbuild/android-arm@0.21.5':
     optional: true
 
+  '@esbuild/android-x64@0.18.20':
+    optional: true
+
   '@esbuild/android-x64@0.21.5':
     optional: true
 
+  '@esbuild/darwin-arm64@0.18.20':
+    optional: true
+
   '@esbuild/darwin-arm64@0.21.5':
     optional: true
 
+  '@esbuild/darwin-x64@0.18.20':
+    optional: true
+
   '@esbuild/darwin-x64@0.21.5':
     optional: true
 
+  '@esbuild/freebsd-arm64@0.18.20':
+    optional: true
+
   '@esbuild/freebsd-arm64@0.21.5':
     optional: true
 
+  '@esbuild/freebsd-x64@0.18.20':
+    optional: true
+
   '@esbuild/freebsd-x64@0.21.5':
     optional: true
 
+  '@esbuild/linux-arm64@0.18.20':
+    optional: true
+
   '@esbuild/linux-arm64@0.21.5':
     optional: true
 
+  '@esbuild/linux-arm@0.18.20':
+    optional: true
+
   '@esbuild/linux-arm@0.21.5':
     optional: true
 
+  '@esbuild/linux-ia32@0.18.20':
+    optional: true
+
   '@esbuild/linux-ia32@0.21.5':
     optional: true
 
+  '@esbuild/linux-loong64@0.18.20':
+    optional: true
+
   '@esbuild/linux-loong64@0.21.5':
     optional: true
 
+  '@esbuild/linux-mips64el@0.18.20':
+    optional: true
+
   '@esbuild/linux-mips64el@0.21.5':
     optional: true
 
+  '@esbuild/linux-ppc64@0.18.20':
+    optional: true
+
   '@esbuild/linux-ppc64@0.21.5':
     optional: true
 
+  '@esbuild/linux-riscv64@0.18.20':
+    optional: true
+
   '@esbuild/linux-riscv64@0.21.5':
     optional: true
 
+  '@esbuild/linux-s390x@0.18.20':
+    optional: true
+
   '@esbuild/linux-s390x@0.21.5':
     optional: true
 
+  '@esbuild/linux-x64@0.18.20':
+    optional: true
+
   '@esbuild/linux-x64@0.21.5':
     optional: true
 
+  '@esbuild/netbsd-x64@0.18.20':
+    optional: true
+
   '@esbuild/netbsd-x64@0.21.5':
     optional: true
 
+  '@esbuild/openbsd-x64@0.18.20':
+    optional: true
+
   '@esbuild/openbsd-x64@0.21.5':
     optional: true
 
+  '@esbuild/sunos-x64@0.18.20':
+    optional: true
+
   '@esbuild/sunos-x64@0.21.5':
     optional: true
 
+  '@esbuild/win32-arm64@0.18.20':
+    optional: true
+
   '@esbuild/win32-arm64@0.21.5':
     optional: true
 
+  '@esbuild/win32-ia32@0.18.20':
+    optional: true
+
   '@esbuild/win32-ia32@0.21.5':
     optional: true
 
+  '@esbuild/win32-x64@0.18.20':
+    optional: true
+
   '@esbuild/win32-x64@0.21.5':
     optional: true
 
@@ -1858,6 +2518,8 @@ snapshots:
   '@exodus/schemasafe@1.3.0':
     optional: true
 
+  '@fastify/busboy@2.1.1': {}
+
   '@gcornut/valibot-json-schema@0.31.0':
     dependencies:
       valibot: 0.31.1
@@ -1867,6 +2529,77 @@ snapshots:
       esbuild-runner: 2.2.2(esbuild@0.21.5)
     optional: true
 
+  '@graphql-tools/executor@1.3.1(graphql@15.9.0)':
+    dependencies:
+      '@graphql-tools/utils': 10.5.4(graphql@15.9.0)
+      '@graphql-typed-document-node/core': 3.2.0(graphql@15.9.0)
+      '@repeaterjs/repeater': 3.0.6
+      graphql: 15.9.0
+      tslib: 2.7.0
+      value-or-promise: 1.0.12
+
+  '@graphql-tools/merge@8.4.2(graphql@15.9.0)':
+    dependencies:
+      '@graphql-tools/utils': 9.2.1(graphql@15.9.0)
+      graphql: 15.9.0
+      tslib: 2.4.0
+
+  '@graphql-tools/merge@9.0.6(graphql@15.9.0)':
+    dependencies:
+      '@graphql-tools/utils': 10.5.4(graphql@15.9.0)
+      graphql: 15.9.0
+      tslib: 2.4.0
+
+  '@graphql-tools/schema@10.0.6(graphql@15.9.0)':
+    dependencies:
+      '@graphql-tools/merge': 9.0.6(graphql@15.9.0)
+      '@graphql-tools/utils': 10.5.4(graphql@15.9.0)
+      graphql: 15.9.0
+      tslib: 2.7.0
+      value-or-promise: 1.0.12
+
+  '@graphql-tools/schema@9.0.19(graphql@15.9.0)':
+    dependencies:
+      '@graphql-tools/merge': 8.4.2(graphql@15.9.0)
+      '@graphql-tools/utils': 9.2.1(graphql@15.9.0)
+      graphql: 15.9.0
+      tslib: 2.4.0
+      value-or-promise: 1.0.12
+
+  '@graphql-tools/utils@10.5.4(graphql@15.9.0)':
+    dependencies:
+      '@graphql-typed-document-node/core': 3.2.0(graphql@15.9.0)
+      cross-inspect: 1.0.1
+      dset: 3.1.3
+      graphql: 15.9.0
+      tslib: 2.4.0
+
+  '@graphql-tools/utils@9.2.1(graphql@15.9.0)':
+    dependencies:
+      '@graphql-typed-document-node/core': 3.2.0(graphql@15.9.0)
+      graphql: 15.9.0
+      tslib: 2.4.0
+
+  '@graphql-typed-document-node/core@3.2.0(graphql@15.9.0)':
+    dependencies:
+      graphql: 15.9.0
+
+  '@graphql-yoga/logger@1.0.0':
+    dependencies:
+      tslib: 2.7.0
+
+  '@graphql-yoga/subscription@4.0.0':
+    dependencies:
+      '@graphql-yoga/typed-event-target': 2.0.0
+      '@repeaterjs/repeater': 3.0.6
+      '@whatwg-node/events': 0.1.2
+      tslib: 2.7.0
+
+  '@graphql-yoga/typed-event-target@2.0.0':
+    dependencies:
+      '@repeaterjs/repeater': 3.0.6
+      tslib: 2.7.0
+
   '@hapi/hoek@9.3.0':
     optional: true
 
@@ -1905,6 +2638,12 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.2
       '@jridgewell/sourcemap-codec': 1.5.0
 
+  '@kamilkisiela/fast-url-parser@1.1.4': {}
+
+  '@kitql/helpers@0.8.9':
+    dependencies:
+      esm-env: 1.0.0
+
   '@nodelib/fs.scandir@2.1.5':
     dependencies:
       '@nodelib/fs.stat': 2.0.5
@@ -1925,6 +2664,8 @@ snapshots:
   '@poppinss/macroable@1.0.2':
     optional: true
 
+  '@repeaterjs/repeater@3.0.6': {}
+
   '@rollup/rollup-android-arm-eabi@4.21.0':
     optional: true
 
@@ -1990,14 +2731,34 @@ snapshots:
   '@sodaru/yup-to-json-schema@2.0.1':
     optional: true
 
-  '@sveltejs/adapter-auto@3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))':
+  '@sveltejs/adapter-auto@3.2.4(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))':
     dependencies:
-      '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)
+      '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       import-meta-resolve: 4.1.0
 
-  '@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)':
+  '@sveltejs/kit@1.30.4(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))':
     dependencies:
-      '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.4.2)
+      '@sveltejs/vite-plugin-svelte': 2.5.3(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))
+      '@types/cookie': 0.5.4
+      cookie: 0.5.0
+      devalue: 4.3.3
+      esm-env: 1.0.0
+      kleur: 4.1.5
+      magic-string: 0.30.11
+      mrmime: 1.0.1
+      sade: 1.8.1
+      set-cookie-parser: 2.7.0
+      sirv: 2.0.4
+      svelte: 3.59.2
+      tiny-glob: 0.2.9
+      undici: 5.28.4
+      vite: 4.5.3(@types/node@22.5.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))':
+    dependencies:
+      '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       '@types/cookie': 0.6.0
       cookie: 0.6.0
       devalue: 5.0.0
@@ -2011,31 +2772,58 @@ snapshots:
       sirv: 2.0.4
       svelte: 4.2.18
       tiny-glob: 0.2.9
-      vite: 5.4.2
+      vite: 5.4.2(@types/node@22.5.0)
 
-  '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)':
+  '@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.5.3(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0)))(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))':
     dependencies:
-      '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.4.2)
+      '@sveltejs/vite-plugin-svelte': 2.5.3(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))
+      debug: 4.3.6
+      svelte: 3.59.2
+      vite: 4.5.3(@types/node@22.5.0)
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))':
+    dependencies:
+      '@sveltejs/vite-plugin-svelte': 3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       debug: 4.3.6
       svelte: 4.2.18
-      vite: 5.4.2
+      vite: 5.4.2(@types/node@22.5.0)
     transitivePeerDependencies:
       - supports-color
 
-  '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2)':
+  '@sveltejs/vite-plugin-svelte@2.5.3(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))':
     dependencies:
-      '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)
+      '@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.5.3(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0)))(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))
+      debug: 4.3.6
+      deepmerge: 4.3.1
+      kleur: 4.1.5
+      magic-string: 0.30.11
+      svelte: 3.59.2
+      svelte-hmr: 0.15.3(svelte@3.59.2)
+      vite: 4.5.3(@types/node@22.5.0)
+      vitefu: 0.2.5(vite@4.5.3(@types/node@22.5.0))
+    transitivePeerDependencies:
+      - supports-color
+
+  '@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))':
+    dependencies:
+      '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       debug: 4.3.6
       deepmerge: 4.3.1
       kleur: 4.1.5
       magic-string: 0.30.11
       svelte: 4.2.18
       svelte-hmr: 0.16.0(svelte@4.2.18)
-      vite: 5.4.2
-      vitefu: 0.2.5(vite@5.4.2)
+      vite: 5.4.2(@types/node@22.5.0)
+      vitefu: 0.2.5(vite@5.4.2(@types/node@22.5.0))
     transitivePeerDependencies:
       - supports-color
 
+  '@types/braces@3.0.4': {}
+
+  '@types/cookie@0.5.4': {}
+
   '@types/cookie@0.6.0': {}
 
   '@types/eslint@9.6.0':
@@ -2045,8 +2833,20 @@ snapshots:
 
   '@types/estree@1.0.5': {}
 
+  '@types/fs-extra@9.0.13':
+    dependencies:
+      '@types/node': 22.5.0
+
   '@types/json-schema@7.0.15': {}
 
+  '@types/micromatch@4.0.9':
+    dependencies:
+      '@types/braces': 3.0.4
+
+  '@types/node@22.5.0':
+    dependencies:
+      undici-types: 6.19.8
+
   '@types/pug@2.0.10': {}
 
   '@types/validator@13.12.0':
@@ -2133,6 +2933,8 @@ snapshots:
       '@typescript-eslint/types': 8.2.0
       eslint-visitor-keys: 3.4.3
 
+  '@ungap/structured-clone@1.2.0': {}
+
   '@vinejs/compiler@2.5.0':
     optional: true
 
@@ -2148,6 +2950,27 @@ snapshots:
       validator: 13.12.0
     optional: true
 
+  '@whatwg-node/events@0.1.2':
+    dependencies:
+      tslib: 2.7.0
+
+  '@whatwg-node/fetch@0.9.21':
+    dependencies:
+      '@whatwg-node/node-fetch': 0.5.25
+      urlpattern-polyfill: 10.0.0
+
+  '@whatwg-node/node-fetch@0.5.25':
+    dependencies:
+      '@kamilkisiela/fast-url-parser': 1.1.4
+      busboy: 1.6.0
+      fast-querystring: 1.1.2
+      tslib: 2.7.0
+
+  '@whatwg-node/server@0.9.49':
+    dependencies:
+      '@whatwg-node/fetch': 0.9.21
+      tslib: 2.7.0
+
   acorn-jsx@5.3.2(acorn@8.12.1):
     dependencies:
       acorn: 8.12.1
@@ -2194,6 +3017,10 @@ snapshots:
 
   array-union@2.1.0: {}
 
+  ast-types@0.16.1:
+    dependencies:
+      tslib: 2.4.0
+
   autoprefixer@10.4.20(postcss@8.4.41):
     dependencies:
       browserslist: 4.23.3
@@ -2235,6 +3062,14 @@ snapshots:
   buffer-from@1.1.2:
     optional: true
 
+  builtins@5.1.0:
+    dependencies:
+      semver: 7.6.3
+
+  busboy@1.6.0:
+    dependencies:
+      streamsearch: 1.1.0
+
   callsites@3.1.0: {}
 
   camelcase-css@2.0.1: {}
@@ -2277,16 +3112,26 @@ snapshots:
 
   commander@4.1.1: {}
 
+  commander@9.5.0: {}
+
   concat-map@0.0.1: {}
 
+  cookie@0.5.0: {}
+
   cookie@0.6.0: {}
 
+  cross-inspect@1.0.1:
+    dependencies:
+      tslib: 2.4.0
+
   cross-spawn@7.0.3:
     dependencies:
       path-key: 3.1.1
       shebang-command: 2.0.0
       which: 2.0.2
 
+  cryptr@6.3.0: {}
+
   css-tree@2.3.1:
     dependencies:
       mdn-data: 2.0.30
@@ -2294,6 +3139,8 @@ snapshots:
 
   cssesc@3.0.0: {}
 
+  data-uri-to-buffer@4.0.1: {}
+
   dayjs@1.11.13:
     optional: true
 
@@ -2309,6 +3156,8 @@ snapshots:
 
   detect-indent@6.1.0: {}
 
+  devalue@4.3.3: {}
+
   devalue@5.0.0: {}
 
   didyoumean@1.2.2: {}
@@ -2319,6 +3168,8 @@ snapshots:
 
   dlv@1.1.3: {}
 
+  dset@3.1.3: {}
+
   eastasianwidth@0.2.0: {}
 
   electron-to-chromium@1.5.13: {}
@@ -2336,6 +3187,31 @@ snapshots:
       tslib: 2.4.0
     optional: true
 
+  esbuild@0.18.20:
+    optionalDependencies:
+      '@esbuild/android-arm': 0.18.20
+      '@esbuild/android-arm64': 0.18.20
+      '@esbuild/android-x64': 0.18.20
+      '@esbuild/darwin-arm64': 0.18.20
+      '@esbuild/darwin-x64': 0.18.20
+      '@esbuild/freebsd-arm64': 0.18.20
+      '@esbuild/freebsd-x64': 0.18.20
+      '@esbuild/linux-arm': 0.18.20
+      '@esbuild/linux-arm64': 0.18.20
+      '@esbuild/linux-ia32': 0.18.20
+      '@esbuild/linux-loong64': 0.18.20
+      '@esbuild/linux-mips64el': 0.18.20
+      '@esbuild/linux-ppc64': 0.18.20
+      '@esbuild/linux-riscv64': 0.18.20
+      '@esbuild/linux-s390x': 0.18.20
+      '@esbuild/linux-x64': 0.18.20
+      '@esbuild/netbsd-x64': 0.18.20
+      '@esbuild/openbsd-x64': 0.18.20
+      '@esbuild/sunos-x64': 0.18.20
+      '@esbuild/win32-arm64': 0.18.20
+      '@esbuild/win32-ia32': 0.18.20
+      '@esbuild/win32-x64': 0.18.20
+
   esbuild@0.21.5:
     optionalDependencies:
       '@esbuild/aix-ppc64': 0.21.5
@@ -2463,6 +3339,8 @@ snapshots:
       acorn-jsx: 5.3.2(acorn@8.12.1)
       eslint-visitor-keys: 3.4.3
 
+  esprima@4.0.1: {}
+
   esquery@1.6.0:
     dependencies:
       estraverse: 5.3.0
@@ -2479,6 +3357,20 @@ snapshots:
 
   esutils@2.0.3: {}
 
+  execa@6.1.0:
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 3.0.1
+      is-stream: 3.0.0
+      merge-stream: 2.0.0
+      npm-run-path: 5.3.0
+      onetime: 6.0.0
+      signal-exit: 3.0.7
+      strip-final-newline: 3.0.0
+
+  fast-decode-uri-component@1.0.1: {}
+
   fast-deep-equal@3.1.3: {}
 
   fast-glob@3.3.2:
@@ -2493,10 +3385,19 @@ snapshots:
 
   fast-levenshtein@2.0.6: {}
 
+  fast-querystring@1.1.2:
+    dependencies:
+      fast-decode-uri-component: 1.0.1
+
   fastq@1.17.1:
     dependencies:
       reusify: 1.0.4
 
+  fetch-blob@3.2.0:
+    dependencies:
+      node-domexception: 1.0.0
+      web-streams-polyfill: 3.3.3
+
   file-entry-cache@8.0.0:
     dependencies:
       flat-cache: 4.0.1
@@ -2522,14 +3423,26 @@ snapshots:
       cross-spawn: 7.0.3
       signal-exit: 4.1.0
 
-  formsnap@1.0.1(svelte@4.2.18)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)):
+  formdata-polyfill@4.0.10:
+    dependencies:
+      fetch-blob: 3.2.0
+
+  formsnap@1.0.1(svelte@4.2.18)(sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)):
     dependencies:
       nanoid: 5.0.7
       svelte: 4.2.18
-      sveltekit-superforms: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)
+      sveltekit-superforms: 2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)
 
   fraction.js@4.3.7: {}
 
+  fs-extra@10.1.0:
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+
+  fs-monkey@1.0.6: {}
+
   fs.realpath@1.0.0: {}
 
   fsevents@2.3.3:
@@ -2537,6 +3450,8 @@ snapshots:
 
   function-bind@1.1.2: {}
 
+  get-stream@6.0.1: {}
+
   glob-parent@5.1.2:
     dependencies:
       is-glob: 4.0.3
@@ -2563,6 +3478,14 @@ snapshots:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
+  glob@8.1.0:
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 5.1.6
+      once: 1.4.0
+
   globals@14.0.0: {}
 
   globals@15.9.0: {}
@@ -2584,12 +3507,81 @@ snapshots:
 
   graphemer@1.4.0: {}
 
+  graphql-yoga@4.0.5(graphql@15.9.0):
+    dependencies:
+      '@envelop/core': 4.0.3
+      '@graphql-tools/executor': 1.3.1(graphql@15.9.0)
+      '@graphql-tools/schema': 10.0.6(graphql@15.9.0)
+      '@graphql-tools/utils': 10.5.4(graphql@15.9.0)
+      '@graphql-yoga/logger': 1.0.0
+      '@graphql-yoga/subscription': 4.0.0
+      '@whatwg-node/fetch': 0.9.21
+      '@whatwg-node/server': 0.9.49
+      dset: 3.1.3
+      graphql: 15.9.0
+      lru-cache: 10.4.3
+      tslib: 2.7.0
+
+  graphql@15.9.0: {}
+
   has-flag@4.0.0: {}
 
   hasown@2.0.2:
     dependencies:
       function-bind: 1.1.2
 
+  houdini-svelte@1.2.56(@types/node@22.5.0):
+    dependencies:
+      '@kitql/helpers': 0.8.9
+      '@sveltejs/kit': 1.30.4(svelte@3.59.2)(vite@4.5.3(@types/node@22.5.0))
+      ast-types: 0.16.1
+      estree-walker: 3.0.3
+      graphql: 15.9.0
+      houdini: 1.2.56
+      recast: 0.23.9
+      rollup: 3.29.4
+      svelte: 3.59.2
+      vite: 4.5.3(@types/node@22.5.0)
+    transitivePeerDependencies:
+      - '@types/node'
+      - less
+      - lightningcss
+      - sass
+      - stylus
+      - sugarss
+      - supports-color
+      - terser
+
+  houdini@1.2.56:
+    dependencies:
+      '@babel/parser': 7.25.4
+      '@clack/prompts': 0.6.3
+      '@graphql-tools/merge': 9.0.6(graphql@15.9.0)
+      '@graphql-tools/schema': 9.0.19(graphql@15.9.0)
+      '@kitql/helpers': 0.8.9
+      '@types/estree': 1.0.5
+      '@types/fs-extra': 9.0.13
+      '@types/micromatch': 4.0.9
+      '@ungap/structured-clone': 1.2.0
+      '@whatwg-node/server': 0.9.49
+      ast-types: 0.16.1
+      commander: 9.5.0
+      deepmerge: 4.3.1
+      estree-walker: 3.0.3
+      fs-extra: 10.1.0
+      glob: 8.1.0
+      graphql: 15.9.0
+      graphql-yoga: 4.0.5(graphql@15.9.0)
+      memfs: 3.5.3
+      micromatch: 4.0.7
+      minimatch: 5.1.6
+      node-fetch: 3.3.2
+      npx-import: 1.1.4
+      recast: 0.23.9
+      vite-plugin-watch-and-run: 1.7.0
+
+  human-signals@3.0.1: {}
+
   ignore@5.3.2: {}
 
   import-fresh@3.3.0:
@@ -2632,6 +3624,8 @@ snapshots:
     dependencies:
       '@types/estree': 1.0.5
 
+  is-stream@3.0.0: {}
+
   isexe@2.0.0: {}
 
   jackspeak@3.4.3:
@@ -2667,6 +3661,12 @@ snapshots:
 
   json-stable-stringify-without-jsonify@1.0.1: {}
 
+  jsonfile@6.1.0:
+    dependencies:
+      universalify: 2.0.1
+    optionalDependencies:
+      graceful-fs: 4.2.11
+
   just-clone@6.2.0: {}
 
   keyv@4.5.4:
@@ -2704,21 +3704,38 @@ snapshots:
 
   mdn-data@2.0.30: {}
 
+  memfs@3.5.3:
+    dependencies:
+      fs-monkey: 1.0.6
+
   memoize-weak@1.0.2: {}
 
+  merge-stream@2.0.0: {}
+
   merge2@1.4.1: {}
 
+  micromatch@4.0.5:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+
   micromatch@4.0.7:
     dependencies:
       braces: 3.0.3
       picomatch: 2.3.1
 
+  mimic-fn@4.0.0: {}
+
   min-indent@1.0.1: {}
 
   minimatch@3.1.2:
     dependencies:
       brace-expansion: 1.1.11
 
+  minimatch@5.1.6:
+    dependencies:
+      brace-expansion: 2.0.1
+
   minimatch@9.0.5:
     dependencies:
       brace-expansion: 2.0.1
@@ -2733,6 +3750,8 @@ snapshots:
 
   mri@1.2.0: {}
 
+  mrmime@1.0.1: {}
+
   mrmime@2.0.0: {}
 
   ms@2.1.2: {}
@@ -2749,6 +3768,14 @@ snapshots:
 
   natural-compare@1.4.0: {}
 
+  node-domexception@1.0.0: {}
+
+  node-fetch@3.3.2:
+    dependencies:
+      data-uri-to-buffer: 4.0.1
+      fetch-blob: 3.2.0
+      formdata-polyfill: 4.0.10
+
   node-releases@2.0.18: {}
 
   normalize-path@3.0.0: {}
@@ -2758,6 +3785,17 @@ snapshots:
   normalize-url@8.0.1:
     optional: true
 
+  npm-run-path@5.3.0:
+    dependencies:
+      path-key: 4.0.0
+
+  npx-import@1.1.4:
+    dependencies:
+      execa: 6.1.0
+      parse-package-name: 1.0.0
+      semver: 7.6.3
+      validate-npm-package-name: 4.0.0
+
   object-assign@4.1.1: {}
 
   object-hash@3.0.0: {}
@@ -2766,6 +3804,10 @@ snapshots:
     dependencies:
       wrappy: 1.0.2
 
+  onetime@6.0.0:
+    dependencies:
+      mimic-fn: 4.0.0
+
   optionator@0.9.4:
     dependencies:
       deep-is: 0.1.4
@@ -2789,12 +3831,16 @@ snapshots:
     dependencies:
       callsites: 3.1.0
 
+  parse-package-name@1.0.0: {}
+
   path-exists@4.0.0: {}
 
   path-is-absolute@1.0.1: {}
 
   path-key@3.1.1: {}
 
+  path-key@4.0.0: {}
+
   path-parse@1.0.7: {}
 
   path-scurry@1.11.1:
@@ -2900,6 +3946,14 @@ snapshots:
     dependencies:
       picomatch: 2.3.1
 
+  recast@0.23.9:
+    dependencies:
+      ast-types: 0.16.1
+      esprima: 4.0.1
+      source-map: 0.6.1
+      tiny-invariant: 1.3.3
+      tslib: 2.4.0
+
   regenerator-runtime@0.14.1:
     optional: true
 
@@ -2917,6 +3971,10 @@ snapshots:
     dependencies:
       glob: 7.2.3
 
+  rollup@3.29.4:
+    optionalDependencies:
+      fsevents: 2.3.3
+
   rollup@4.21.0:
     dependencies:
       '@types/estree': 1.0.5
@@ -2964,6 +4022,8 @@ snapshots:
 
   shebang-regex@3.0.0: {}
 
+  signal-exit@3.0.7: {}
+
   signal-exit@4.1.0: {}
 
   sirv@2.0.4:
@@ -2972,6 +4032,8 @@ snapshots:
       mrmime: 2.0.0
       totalist: 3.0.1
 
+  sisteransi@1.0.5: {}
+
   slash@3.0.0: {}
 
   sorcery@0.11.1:
@@ -2989,8 +4051,9 @@ snapshots:
       source-map: 0.6.1
     optional: true
 
-  source-map@0.6.1:
-    optional: true
+  source-map@0.6.1: {}
+
+  streamsearch@1.1.0: {}
 
   string-width@4.2.3:
     dependencies:
@@ -3012,6 +4075,8 @@ snapshots:
     dependencies:
       ansi-regex: 6.0.1
 
+  strip-final-newline@3.0.0: {}
+
   strip-indent@3.0.0:
     dependencies:
       min-indent: 1.0.1
@@ -3067,6 +4132,10 @@ snapshots:
     optionalDependencies:
       svelte: 4.2.18
 
+  svelte-hmr@0.15.3(svelte@3.59.2):
+    dependencies:
+      svelte: 3.59.2
+
   svelte-hmr@0.16.0(svelte@4.2.18):
     dependencies:
       svelte: 4.2.18
@@ -3084,6 +4153,8 @@ snapshots:
       postcss-load-config: 4.0.2(postcss@8.4.41)
       typescript: 5.5.4
 
+  svelte@3.59.2: {}
+
   svelte@4.2.18:
     dependencies:
       '@ampproject/remapping': 2.3.0
@@ -3101,9 +4172,9 @@ snapshots:
       magic-string: 0.30.11
       periscopic: 3.1.0
 
-  sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18):
+  sveltekit-superforms@2.17.0(@sveltejs/kit@2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18):
     dependencies:
-      '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2))(svelte@4.2.18)(vite@5.4.2)
+      '@sveltejs/kit': 2.5.24(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0)))(svelte@4.2.18)(vite@5.4.2(@types/node@22.5.0))
       devalue: 5.0.0
       just-clone: 6.2.0
       memoize-weak: 1.0.2
@@ -3169,6 +4240,10 @@ snapshots:
       globalyzer: 0.1.0
       globrex: 0.1.2
 
+  tiny-invariant@1.3.3: {}
+
+  to-fast-properties@2.0.0: {}
+
   to-regex-range@5.0.1:
     dependencies:
       is-number: 7.0.0
@@ -3189,8 +4264,9 @@ snapshots:
 
   ts-interface-checker@0.1.13: {}
 
-  tslib@2.4.0:
-    optional: true
+  tslib@2.4.0: {}
+
+  tslib@2.7.0: {}
 
   type-check@0.4.0:
     dependencies:
@@ -3212,6 +4288,14 @@ snapshots:
 
   typescript@5.5.4: {}
 
+  undici-types@6.19.8: {}
+
+  undici@5.28.4:
+    dependencies:
+      '@fastify/busboy': 2.1.1
+
+  universalify@2.0.1: {}
+
   update-browserslist-db@1.1.0(browserslist@4.23.3):
     dependencies:
       browserslist: 4.23.3
@@ -3222,6 +4306,8 @@ snapshots:
     dependencies:
       punycode: 2.3.1
 
+  urlpattern-polyfill@10.0.0: {}
+
   util-deprecate@1.0.2: {}
 
   valibot@0.31.1:
@@ -3230,20 +4316,47 @@ snapshots:
   valibot@0.35.0:
     optional: true
 
+  validate-npm-package-name@4.0.0:
+    dependencies:
+      builtins: 5.1.0
+
   validator@13.12.0:
     optional: true
 
-  vite@5.4.2:
+  value-or-promise@1.0.12: {}
+
+  vite-plugin-watch-and-run@1.7.0:
+    dependencies:
+      '@kitql/helpers': 0.8.9
+      micromatch: 4.0.5
+
+  vite@4.5.3(@types/node@22.5.0):
+    dependencies:
+      esbuild: 0.18.20
+      postcss: 8.4.41
+      rollup: 3.29.4
+    optionalDependencies:
+      '@types/node': 22.5.0
+      fsevents: 2.3.3
+
+  vite@5.4.2(@types/node@22.5.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.41
       rollup: 4.21.0
     optionalDependencies:
+      '@types/node': 22.5.0
       fsevents: 2.3.3
 
-  vitefu@0.2.5(vite@5.4.2):
+  vitefu@0.2.5(vite@4.5.3(@types/node@22.5.0)):
+    optionalDependencies:
+      vite: 4.5.3(@types/node@22.5.0)
+
+  vitefu@0.2.5(vite@5.4.2(@types/node@22.5.0)):
     optionalDependencies:
-      vite: 5.4.2
+      vite: 5.4.2(@types/node@22.5.0)
+
+  web-streams-polyfill@3.3.3: {}
 
   which@2.0.2:
     dependencies:
diff --git a/src/app.html b/src/app.html
index 94ae4211a6fda7d838a5999014895a9a0c793a27..9c53e5672bcb5fee0c05adb61df69174c7fe9682 100644
--- a/src/app.html
+++ b/src/app.html
@@ -4,7 +4,10 @@
 		<meta charset="utf-8" />
 		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
-		<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Londrina+Solid|Nunito:400,300">
+		<link
+			rel="stylesheet"
+			href="https://fonts.googleapis.com/css?family=Londrina+Solid|Nunito:400,300"
+		/>
 		%sveltekit.head%
 	</head>
 	<body data-sveltekit-preload-data="hover" class="h-full">
diff --git a/src/client.ts b/src/client.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d3b21e6ffa9d94142bbbd57f3ab1c19e23799eb
--- /dev/null
+++ b/src/client.ts
@@ -0,0 +1,15 @@
+import { HoudiniClient } from '$houdini';
+
+export default new HoudiniClient({
+	url: 'https://api.iiens.net/graphql/v0'
+
+	// uncomment this to configure the network call (for things like authentication)
+	// for more information, please visit here: https://www.houdinigraphql.com/guides/authentication
+	// fetchParams({ session }) {
+	//     return {
+	//         headers: {
+	//             Authentication: `Bearer ${session.token}`,
+	//         }
+	//     }
+	// }
+});
diff --git a/src/lib/env.ts b/src/lib/env.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ae984638a80a387df668c6f4e0b2fb4ceff194c6
--- /dev/null
+++ b/src/lib/env.ts
@@ -0,0 +1,20 @@
+import * as assert from 'assert';
+import { env } from '$env/dynamic/private';
+
+type RecordFromKeys<T extends readonly string[]> = Record<T[number], string>;
+
+function ensureEnv<K extends readonly string[]>(keys: K): RecordFromKeys<K> {
+	const cleanEnv = {} as RecordFromKeys<K>;
+
+	for (const key of keys) {
+		const value = env[key];
+		if (!env.CI) {
+			assert.ok(value, `Variable d'environnement ${key} manquante`);
+		}
+		cleanEnv[key as keyof RecordFromKeys<K>] = value!;
+	}
+
+	return cleanEnv;
+}
+
+export default ensureEnv(['API_ORIGIN', 'API_TOKEN'] as const);
diff --git a/src/lib/graphql/client.ts b/src/lib/graphql/client.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e422bf9740abefd2fa1e9ea50e4f3c72abb317e7
--- /dev/null
+++ b/src/lib/graphql/client.ts
@@ -0,0 +1,13 @@
+import env from '$lib/env';
+import { HoudiniClient } from '$houdini';
+
+export default new HoudiniClient({
+	url: `${env.API_ORIGIN}/graphql/v0`,
+	fetchParams() {
+		return {
+			headers: {
+				Authorization: `Basic ${env.API_TOKEN}`
+			}
+		};
+	}
+});
diff --git a/src/lib/graphql/queries/promo.gql b/src/lib/graphql/queries/promo.gql
new file mode 100644
index 0000000000000000000000000000000000000000..c057596b982a774222ca1db5d6fa4035d7a589dc
--- /dev/null
+++ b/src/lib/graphql/queries/promo.gql
@@ -0,0 +1,16 @@
+query GetPromotion($first: Int!, $after: String, $promotion: Int!) {
+	page: users(
+		first: $first
+		after: $after
+		filter: { promotion: { eq: [$promotion] }, nickname: { null: false } }
+	) {
+		pageInfo {
+			endCursor
+			hasNextPage
+		}
+		nodes {
+			id
+			uuid
+		}
+	}
+}
diff --git a/src/lib/graphql/queries/user_details.gql b/src/lib/graphql/queries/user_details.gql
new file mode 100644
index 0000000000000000000000000000000000000000..ae256bd0a0c4bfcb92a054b0ded57d1c04f47c5f
--- /dev/null
+++ b/src/lib/graphql/queries/user_details.gql
@@ -0,0 +1,11 @@
+query UserDetails($first: Int = 4, $uuidList: [String!]!) {
+	page: users(first: $first, filter: { id: { like: $uuidList } }) {
+		nodes {
+			uuid
+			id
+			nickname
+			photo
+			photoThumbnailHash
+		}
+	}
+}
diff --git a/src/lib/graphql/query.ts b/src/lib/graphql/query.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2179f16c34b5086566f594a2c14868b7d4ca68da
--- /dev/null
+++ b/src/lib/graphql/query.ts
@@ -0,0 +1,43 @@
+import type { GraphQLValue, GraphQLVariables, QueryResult, QueryStore } from '$houdini';
+import type { RequestEvent } from '@sveltejs/kit';
+
+type Page<T> = {
+	page: {
+		pageInfo: {
+			hasNextPage: boolean;
+			endCursor: string | null;
+		};
+		nodes: T[];
+	};
+};
+
+type NodeType<T> = T extends Page<infer U> ? U : never;
+
+interface PageArgs {
+	first: number;
+	after: string | null | undefined;
+}
+
+export async function* pageIterator<
+	Data extends Page<GraphQLValue>,
+	Input extends PageArgs | GraphQLVariables
+>(
+	event: RequestEvent,
+	queryStore: new () => QueryStore<Data, Input>,
+	variables: Omit<Input, 'first' | 'after'>,
+	pageSize = 100
+): AsyncGenerator<NodeType<Data>, void, undefined> {
+	const store = new queryStore();
+	let cursor: string | null = null;
+	let hasNextPage = true;
+
+	while (hasNextPage) {
+		const result: QueryResult<Data, Input> = await store.fetch({
+			event,
+			variables: { first: pageSize, after: cursor, ...variables } as Input
+		});
+		yield* (result.data?.page.nodes as NodeType<Data>[]) ?? [];
+		cursor = result.data?.page.pageInfo.endCursor ?? null;
+		hasNextPage = !!result.data?.page.pageInfo.hasNextPage;
+	}
+}
diff --git a/src/lib/graphql/schema.gql b/src/lib/graphql/schema.gql
new file mode 100644
index 0000000000000000000000000000000000000000..b5ee8c41567eb2ac6d26b1aaf8f6715af5288094
--- /dev/null
+++ b/src/lib/graphql/schema.gql
@@ -0,0 +1,1479 @@
+directive @concat(value: String!) on FIELD
+
+"""
+# Valeurs non nulles
+Transforme les champs `null` par une valeur par défaut.
+STRING => ""
+NUMBER => 0
+BOOLEAN => false
+LIST => []
+OBJECT => {}
+"""
+directive @non_null(from: FromNull!) on FIELD
+
+"""
+Indicates that an Input Object is a OneOf Input Object (and thus requires
+                        exactly one of its field be provided)
+"""
+directive @oneOf on INPUT_OBJECT
+
+union Account = Group | User
+
+input AccountId {
+	id: SmolStr
+	uuid: UUID
+}
+
+type Address {
+	"""
+	# **ADMIN ONLY**
+	## UUID interne de l'adresse
+
+	Par exemple, `72c612f8-1d07-432e-8f71-60584be51f40`
+	"""
+	accountUuid: UUID!
+
+	"""
+	## Pays de l'adresse
+	"""
+	country: String!
+
+	"""
+	## Adresse postale complète de l'adresse
+	Formatée pour être affichée ou utilisée sur une étiquette d'envoi.
+	Ce champ PEUT contenir plusieurs lignes, séparées par des retours à la ligne.
+	Les nouvelles lignes peuvent être représentées soit par une paire retour
+	chariot/saut de ligne ("\r\n"), soit par un seul caractère de saut de ligne ("\n").
+	"""
+	formatted: String!
+
+	"""
+	## Ville ou localité de l'adresse
+	"""
+	locality: String!
+
+	"""
+	## Nom de l'adresse
+	Le nom usuel de l'adresse s'il existe. Limite de **64** caractères.
+
+	Par exemple, `Pyroloc` ou `Résidence "Les Estudines du Parc"`.
+	"""
+	name: String
+
+	"""
+	## Code postal de l'adresse
+	"""
+	postalCode: String!
+
+	"""
+	## Région de l'adresse
+	Province, de la préfecture ou région.
+	"""
+	region: String
+
+	"""
+	## Rue de l'adresse
+	Composante complète de l'adresse de la rue, qui PEUT inclure le numéro de la maison,
+	le nom de la rue, la boîte postale et des informations sur plusieurs lignes concernant
+	l'adresse de la rue. Ce champ PEUT contenir plusieurs lignes, séparées par des retours
+	à la ligne. Les nouvelles lignes peuvent être représentées soit par une paire retour
+	chariot/saut de ligne ("\r\n"), soit par un seul caractère de saut de ligne ("\n").
+	"""
+	streetAddress: String!
+}
+
+"""
+Base64 [RFC4648](https://www.rfc-editor.org/rfc/rfc4648)
+"""
+scalar Base64
+
+input BooleanFilter {
+	eq: Boolean!
+}
+
+type BuildInfo {
+	ref: String
+	sha: String
+}
+
+"""
+Rôles dans un group
+"""
+enum ClaimAccess {
+	"""
+	Création
+	"""
+	CREATE
+
+	"""
+	Suppression
+	"""
+	DELETE
+
+	"""
+	Aucun
+	"""
+	NONE
+
+	"""
+	Lecture
+	"""
+	READ
+
+	"""
+	Écriture
+	"""
+	WRITE
+}
+
+input CreateGroup {
+	id: Identifier!
+	name: String!
+	type: GroupType
+}
+
+input CreateToken {
+	allowedIps: [String!]
+	claims: [NewTokenClaim!]
+	description: String!
+	globalAccess: ClaimAccess
+	id: String!
+}
+
+input CreateTraining {
+	fullName: String
+	id: Identifier!
+	name: String!
+}
+
+input CreateUnixAccount {
+	vhost: String
+}
+
+input CreateUser {
+	familyName: String!
+	givenName: String!
+	password: Password
+	promotion: Int!
+	trainingId: Identifier!
+}
+
+"""
+Rôles dans un groupe
+"""
+enum CurrentGroupRole {
+	"""
+	Administrateur·trice
+	"""
+	ADMIN
+
+	"""
+	Membre du bureau
+	"""
+	BOARD_MEMBER
+
+	"""
+	Membre ordinaire
+	"""
+	MEMBER
+
+	"""
+	Président·e
+	"""
+	OWNER
+}
+
+"""
+ISO 8601 calendar date without timezone.
+Format: %Y-%m-%d
+
+# Examples
+
+* `1994-11-13`
+* `2000-02-24`
+"""
+scalar Date
+
+"""
+A datetime with timezone offset.
+
+The input is a string in RFC3339 format, e.g. "2022-01-12T04:00:19.12345Z"
+or "2022-01-12T04:00:19+03:00". The output is also a string in RFC3339
+format, but it is always normalized to the UTC (Z) offset, e.g.
+"2022-01-12T04:00:19.12345Z".
+"""
+scalar DateTime
+
+input DateTimeFilter {
+	eq: [LocalDateTime!]
+	gt: LocalDateTime
+	lt: LocalDateTime
+}
+
+enum FromNull {
+	BOOLEAN
+	LIST
+	NUMBER
+	OBJECT
+	STRING
+}
+
+"""
+Genre d'une personne
+"""
+enum Gender {
+	"""
+	Féminin
+	"""
+	FEMALE
+
+	"""
+	Masculin
+	"""
+	MALE
+
+	"""
+	Non binaire
+	"""
+	NON_BINARY
+
+	"""
+	Inconnu
+	"""
+	UNKNOWN
+}
+
+input GenderFilter {
+	isIn: [Gender!]!
+}
+
+type GlobalValues {
+	evenSemester: Boolean!
+	integration: Boolean!
+	schoolYear: Int!
+}
+
+type Group {
+	"""
+	## Groupe actif
+	**VRAI** si le group est encore actif.
+	Se traduit par une déclaration lors d'une réunion publique de l'AEIIE s'il
+	s'agit d'un group BdE. Les listes candidates à l'élection BdE sont actives le
+	temps de la campagne.
+	"""
+	active: Boolean!
+
+	"""
+	## URL de la photo de profil du groupe
+	Cette URL fait référence à un fichier image (par exemple, un fichier image PNG,
+	JPEG ou GIF), plutôt qu'à une page Web contenant une image.
+
+	Par exemple, `https://api.iiens.net/picture/acier2020` ou `https://api.iiens.net/picture/dupontj2042`
+	"""
+	backgroundImage: Url!
+
+	"""
+	Hash de l'image d'arrière plan du groupe
+	"""
+	backgroundImageThumbnailHash: Base64!
+
+	"""
+	## Date de la création du compte du group
+	Sa valeur est un string JSON [RFC3339] représentant le temps  entre 1970-01-01T0:0:0Z,
+	mesuré en UTC, et la date/heure.
+	"""
+	createdAt: DateTime!
+
+	"""
+	## Description du group
+	Une description plus complète que `short_description`, sans limite de caractères.
+	"""
+	description: String
+
+	"""
+	## Adresse électronique préférée de l'utilisateur.
+	Sa valeur DOIT être conforme à la syntaxe addr-spec de la RFC 5322 [RFC5322].
+
+	Par exemple, `arise@iiens.net` ou `bde@iiens.net`.
+	"""
+	email: String!
+
+	"""
+	# **ADMIN ONLY**
+	## Compte caché
+	Le compte du group sera invisible de toutes les requêtes, sauf admin si le filtre
+	de la requête le demande.
+	"""
+	hidden: Boolean!
+
+	"""
+	## Identifiant du group
+	Le nom du group normalisé (sans accents, caractères spéciaux ni espaces).
+	Ne peut contenir que des caractères ASCII et éventuellement des tirets pour
+	remplacer les espaces.
+
+	Par exemple, `arise` ou `aeiie`.
+	"""
+	id: Identifier!
+
+	"""
+	## Nom du group
+	Le nom usuel du group, présenté sous forme d'acronyme ou sous forme longue.
+
+	Par exemple, `ARISE` ou `BdE`.
+	"""
+	name: String!
+
+	"""
+	## URL de la photo de profil du groupe
+	Cette URL fait référence à un fichier image (par exemple, un fichier image PNG,
+	JPEG ou GIF), plutôt qu'à une page Web contenant une image.
+
+	Par exemple, `https://api.iiens.net/picture/acier2020` ou `https://api.iiens.net/picture/dupontj2042`
+	"""
+	picture: Url!
+
+	"""
+	Hash de la photo de profil du groupe
+	"""
+	pictureThumbnailHash: Base64!
+
+	"""
+	## URL de la page de profil du group
+	Page de profil sur [www.iiens.net].
+
+	Par exemple, `https://www.iiens.net/eleve/acier2020` ou `https://www.iiens.net/eleve/dupontj2042`
+	"""
+	profile: Url!
+
+	"""
+	## Description courte du group
+	64 caractères maximum, de quoi présenter le group succinctement.
+	"""
+	shortDescription: String
+
+	"""
+	## Type de groupe
+	Club, Association 1901, etc.
+	"""
+	type: GroupType!
+	unixAccount: UnixAccount
+
+	"""
+	## Date de la dernière mise à jour des informations relatives au group
+	Sa valeur est un string JSON [RFC3339] représentant le temps  entre 1970-01-01T0:0:0Z,
+	mesuré en UTC, et la date/heure.
+	"""
+	updatedAt: DateTime!
+
+	"""
+	# **ADMIN ONLY**
+	## UUID interne du group
+
+	Par exemple, `72c612f8-1d07-432e-8f71-60584be51f40`
+	"""
+	uuid: UUID!
+
+	"""
+	## URL du site perso du groupe
+	"""
+	website: Url
+}
+
+type GroupConnection {
+	"""
+	A list of edges.
+	"""
+	edges: [GroupEdge!]!
+
+	"""
+	A list of nodes.
+	"""
+	nodes: [Group!]!
+
+	"""
+	Information to aid in pagination.
+	"""
+	pageInfo: PageInfo!
+	remainingCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type GroupEdge {
+	"""
+	A cursor for use in pagination
+	"""
+	cursor: String!
+
+	"""
+	The item at the end of the edge
+	"""
+	node: Group!
+	remainingCount: Int!
+}
+
+input GroupFilter {
+	accountUuid: UuidFilter
+	active: BooleanFilter
+	createdAt: DateTimeFilter
+	description: NullableTextFilter
+	email: TextFilter
+	emailVerified: BooleanFilter
+	hidden: BooleanFilter
+	id: TextFilter
+	name: TextFilter
+	or: [GroupFilter!]
+	shortDescription: NullableTextFilter
+	type: GroupTypeFilter
+	unixAccount: UnixAccountFilter
+	updatedAt: DateTimeFilter
+	uuid: UuidFilter
+	website: NullableTextFilter
+}
+
+input GroupId {
+	email: String
+	id: SmolStr
+	uuid: UUID
+}
+
+type GroupOfMember {
+	group: Group!
+	isAdmin: Boolean!
+	isBoardMember: Boolean!
+	isOwner: Boolean!
+	role: CurrentGroupRole!
+	since: Date!
+}
+
+input GroupOfMemberFilter {
+	role: CurrentGroupRole
+	since: NeverDateFilter
+	strictRole: Boolean! = false
+}
+
+"""
+Rôles dans un groupe
+"""
+enum GroupRole {
+	"""
+	Administrateur·trice
+	"""
+	ADMIN
+
+	"""
+	Membre du bureau
+	"""
+	BOARD_MEMBER
+
+	"""
+	A quitté le groupe
+	"""
+	GONE
+
+	"""
+	Membre ordinaire
+	"""
+	MEMBER
+
+	"""
+	Président·e
+	"""
+	OWNER
+}
+
+enum GroupType {
+	ASSOCIATION
+	CLUB
+	COMMISSION
+	HOME
+	LIST
+	OTHER
+	UNKNOWN
+}
+
+input GroupTypeFilter {
+	isIn: [GroupType!]!
+}
+
+type Health {
+	db: String!
+}
+
+type HistoricalGroupOfMember {
+	group: Group!
+	role: GroupRole!
+	since: Date!
+}
+
+input HistoricalGroupOfMemberFilter {
+	role: GroupRole
+	since: NeverDateFilter
+	strictRole: Boolean! = false
+}
+
+type HistoricalMemberOfGroup {
+	role: GroupRole!
+	since: Date!
+	user: User!
+}
+
+scalar Identifier
+
+input Int16Filter {
+	eq: [Int!]
+	gt: Int
+	lt: Int
+}
+
+input LevenshteinFilter {
+	threshold: Int
+	value: String!
+}
+
+"""
+A local datetime without timezone offset.
+
+The input/output is a string in ISO 8601 format without timezone, including
+subseconds. E.g. "2022-01-12T07:30:19.12345".
+"""
+scalar LocalDateTime
+
+type Mutation {
+	addClaims(claims: [NewTokenClaim!]!, token: TokenId!): Token!
+	addGroupMemberRole(group: GroupId!, role: GroupRole!, user: UserId!): HistoricalMemberOfGroup!
+	copyClaims(dstToken: TokenId!, srcToken: TokenId!): Token!
+
+	"""
+	Création d'un groupe
+	"""
+	createGroup(group: CreateGroup!): Group!
+	createGroupUnixAccount(account: CreateUnixAccount!, group: GroupId!): UnixAccount!
+	createToken(token: CreateToken!): PlainToken!
+	createTraining(training: CreateTraining!): Training!
+
+	"""
+	Création d'un utilisateur
+	### Erreurs possibles
+	- `NOT_FOUND`: La formation n'existe pas.
+	- `ID_GENERATION_FAILURE`: L'identifiant de l'utilisateur n'a pas pu être généré.
+	"""
+	createUser(user: CreateUser!): User!
+	createUserUnixAccount(account: CreateUnixAccount!, user: UserId!): UnixAccount!
+	deleteClaims(claims: [String!], token: TokenId!): Token!
+
+	"""
+	Suppression d'un groupe
+	### Erreurs possibles
+	- `NOT_FOUND`: Le groupe n'existe pas.
+	"""
+	deleteGroup(group: GroupId!): Boolean!
+	deleteToken(token: TokenId!): Int!
+	deleteTraining(training: TrainingId!): Int!
+
+	"""
+	Suppression d'un utilisateur
+	### Erreurs possibles
+	- `NOT_FOUND`: L'utilisateur n'existe pas.
+	"""
+	deleteUser(user: UserId!): Int!
+	endOfIntegration: Boolean!
+	evenSemester: Boolean!
+	newYear: Int!
+	regenerateToken(token: TokenId!): PlainToken!
+	status: Int!
+
+	"""
+	Mise à jour d'un groupe
+	### Erreurs possibles
+	- `NOT_FOUND`: Le groupe n'existe pas.
+	"""
+	updateGroup(group: GroupId!, update: UpdateGroup!): Group!
+	updateToken(token: TokenId!, update: UpdateToken!): Token!
+	updateTraining(training: TrainingId!, update: UpdateTraining!): Training!
+	updateUnixAccount(unixAccount: UnixAccountId!, update: UpdateUnixAccount!): UnixAccount!
+
+	"""
+	Mise à jour d'un utilisateur
+	### Erreurs possibles
+	- `NOT_FOUND`: L'utilisateur ou la formation n'existe pas.
+	"""
+	updateUser(update: UpdateUser!, user: UserId!): User!
+}
+
+input NeverDateFilter {
+	eq: [Date!]
+	gt: Date
+	lt: Date
+}
+
+input NewTokenClaim {
+	access: ClaimAccess!
+	claim: String!
+}
+
+input NullableDateFilter {
+	day: Int
+	eq: [Date!]
+	gt: Date
+	lt: Date
+	month: Int
+	null: Boolean
+}
+
+input NullableDateTimeFilter {
+	eq: [LocalDateTime!]
+	gt: LocalDateTime
+	lt: LocalDateTime
+	null: Boolean
+}
+
+input NullableFilter {
+	null: Boolean
+}
+
+input NullableTextFilter {
+	levenshtein: [LevenshteinFilter!]
+	like: [String!]
+	notLike: [String!]
+	null: Boolean
+	similar: [SimilarFilter!]
+}
+
+enum OrderByGroup {
+	CREATED_AT
+	ID
+	NAME
+	UPDATED_AT
+	UUID
+}
+
+enum OrderByUser {
+	BIRTH_DATE
+	CREATED_AT
+	FAMILY_NAME
+	GIVEN_NAME
+	ID
+	LAST_USED_AT
+	NICKNAME
+	PROMOTION
+	UPDATED_AT
+	UUID
+	YEAR
+}
+
+"""
+Information about pagination in a connection
+"""
+type PageInfo {
+	"""
+	When paginating forwards, the cursor to continue.
+	"""
+	endCursor: String
+
+	"""
+	When paginating forwards, are there more items?
+	"""
+	hasNextPage: Boolean!
+
+	"""
+	When paginating backwards, are there more items?
+	"""
+	hasPreviousPage: Boolean!
+
+	"""
+	When paginating backwards, the cursor to continue.
+	"""
+	startCursor: String
+}
+
+input Password {
+	hash: String
+	plain: String
+}
+
+type PlainToken {
+	base64: String!
+	raw: String!
+}
+
+"""
+Ensemble des requêtes possibles pour l'API Arise.
+Certaines peuvent être cachées, en fonction des permissions courantes.
+"""
+type Query {
+	account(account: AccountId!): Account
+	buildInfo: BuildInfo!
+	currentToken: Token!
+	globalsValues: GlobalValues!
+	group(group: GroupId!): Group
+	groups(
+		after: String
+		before: String
+		filter: GroupFilter
+		first: Int
+		last: Int
+		orderBy: [OrderByGroup!]! = []
+	): GroupConnection!
+	health: Health!
+	oAuthAppOwner: Account!
+	token(token: TokenId!): Token
+	tokens: [Token!]!
+	trainings: [Training!]!
+	user(user: UserId!): User
+	users(
+		after: String
+		before: String
+		filter: UserFilter
+		first: Int
+		last: Int
+		orderBy: [OrderByUser!]! = []
+	): UserConnection!
+}
+
+input SimilarFilter {
+	threshold: Float
+	value: String!
+}
+
+scalar SmolStr
+
+input TextFilter {
+	levenshtein: [LevenshteinFilter!]
+	like: [String!]
+	notLike: [String!]
+	similar: [SimilarFilter!]
+}
+
+type Token {
+	allowedIps: [String!]!
+	claims: [TokenClaim!]!
+
+	"""
+	# **ADMIN ONLY**
+	## Description du jeton
+	"""
+	description: String!
+	globalAccess: ClaimAccess
+
+	"""
+	# **ADMIN ONLY**
+	## Identifiant du jeton
+	"""
+	id: String!
+
+	"""
+	# **ADMIN ONLY**
+	## Hash du jeton
+	Standard actuel : BLAKE3
+
+	Par exemple, `$blake3$1ChQCR0BrfBO42AkRogZaw$+COVVpcK/ptUTSckqIdI/rFF1JdIkvk9V++z56kLNf4'`
+	"""
+	tokenHash: String!
+
+	"""
+	# **ADMIN ONLY**
+	## UUID interne du jeton
+
+	Par exemple, `72c612f8-1d07-432e-8f71-60584be51f40`
+	"""
+	uuid: UUID!
+}
+
+type TokenClaim {
+	access: ClaimAccess!
+	claim: String!
+}
+
+input TokenId {
+	globalAccess: ClaimAccess
+	id: SmolStr
+	uuid: UUID
+}
+
+type Training {
+	fullName: String
+	id: Identifier!
+	name: String!
+	uuid: UUID!
+}
+
+input TrainingId {
+	id: SmolStr
+	uuid: UUID
+}
+
+"""
+A UUID is a unique 128-bit number, stored as 16 octets. UUIDs are parsed as
+Strings within GraphQL. UUIDs are used to assign unique identifiers to
+entities without requiring a central allocating authority.
+
+# References
+
+* [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier)
+* [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122)
+"""
+scalar UUID
+
+type UnixAccount {
+	accountUuid: UUID!
+	uid: Int!
+	vhost: String
+
+	"""
+	URL du site personnel de l'utilisateur
+
+	Page "perso" de l'utilisateur hébergée sur les serveurs d'ARISE.
+
+	Par exemple, `https://acier.perso.iiens.net`
+	"""
+	website: Url
+}
+
+input UnixAccountFilter {
+	null: Boolean
+	uid: Int16Filter
+	uuid: UuidFilter
+	vhost: TextFilter
+}
+
+input UnixAccountId {
+	uid: Int
+	uuid: UUID
+}
+
+input UpdateGroup {
+	active: Boolean
+	description: String
+	email: String
+	emailVerified: Boolean
+	hidden: Boolean
+	id: Identifier
+	name: String
+	shortDescription: String
+	type: GroupType
+	website: Url
+}
+
+input UpdateToken {
+	allowedIps: [String!]
+	description: String
+	id: String
+}
+
+input UpdateTraining {
+	fullName: String
+	name: String
+}
+
+input UpdateUnixAccount {
+	uid: Int
+	vhost: String
+}
+
+input UpdateUser {
+	aeiieMember: Boolean
+	birthdate: Date
+	diplomaYearDuration: Int
+	email: String
+	emailVerified: Boolean
+	extendedTrialPeriod: Boolean
+	familyName: String
+	gapYear: Int
+	gender: Gender
+	givenNameAtBirth: String
+	givenNameInUse: String
+	hidden: Boolean
+	id: Identifier
+	initialPromotion: Int
+	lastUsedAt: LocalDateTime
+	locale: String
+	middleName: String
+	nickname: String
+	password: Password
+	phoneNumber: String
+	phoneNumberVerified: Boolean
+	public: Boolean
+	schoolLogin: String
+	suspended: Boolean
+	trainingId: SmolStr
+	website: Url
+	zoneinfo: String
+}
+
+"""
+URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/)
+"""
+scalar Url
+
+scalar Urn
+
+type User {
+	"""
+	Adresse de l'utilisateur
+	"""
+	address: Address
+
+	"""
+	Adhérent de l'AEIIE
+
+	**VRAI** si l'utilisateur est membre de l'AEIIE. **FAUX** à la création du compte.
+	"""
+	aeiieMember: Boolean!
+
+	"""
+	URL de l'image d'arrière plan de l'utilisateur
+
+	Cette URL fait référence à un fichier image (par exemple, un fichier image PNG,
+	JPEG ou GIF), plutôt qu'à une page Web contenant une image.
+
+	Par exemple, `https://api.iiens.net/picture/acier2020`
+	"""
+	backgroundImage: Url!
+
+	"""
+	Hash de l'image d'arrière plan de l'utilisateur
+	"""
+	backgroundImageThumbnailHash: Base64!
+
+	"""
+	Date de naissance de l'utilisateur
+	"""
+	birthdate: Date
+
+	"""
+	Nom complet administratif de l'utilisateur
+
+	Présenté sous une forme affichable, avec toutes les parties du nom, **SAUF** le surnom.
+
+	Par exemple, `Jean Dupont`
+	"""
+	civilName: String!
+
+	"""
+	Date de la création du compte de l'utilisateur
+
+	Sa valeur est un string JSON [RFC3339] représentant le temps  entre 1970-01-01T0:0:0Z,
+	mesuré en UTC, et la date/heure.
+	"""
+	createdAt: DateTime!
+
+	"""
+	Années d'études requises jusqu'au diplôme
+
+	Par défaut cette valeur est de 03.
+	"""
+	diplomaYearDuration: Int!
+
+	"""
+	Adresse électronique de l'utilisateur
+
+	Un alias de l'adresse électronique véritable de l'utilisateur.
+	À utiliser en priorité pour la création de comptes.
+
+	Par exemple, `acier2020.e2e01581919c@alias.iiens.net`.
+	"""
+	email: String!
+
+	"""
+	Adresse électronique réelle de l'utilisateur
+
+	Sa valeur DOIT être conforme à la syntaxe addr-spec de la RFC 5322 [RFC5322].
+
+	Par exemple, `foo.bar@ensiie.fr` ou `dupont.jean@gmail.com`.
+	"""
+	emailForwardAddress: String
+
+	"""
+	Adresse électronique de l'utilisateur vérifiée
+
+	Vrai si l'adresse électronique préférée de l'utilisateur a été vérifiée, sinon faux.
+	Lorsque cette valeur d'allégation est vraie, cela signifie qu'ARISE a pris des mesures
+	positives pour s'assurer que l'adresse électronique était contrôlée par l'utilisateur
+	au moment où la vérification a été effectuée.
+	"""
+	emailVerified: Boolean!
+
+	"""
+	Période d'essai étendue de l'utilisateur
+
+	**FAUX** lors de la création du compte.
+
+	Cette variable permet d'outrepasser le champ `restricted_access`.
+	"""
+	extendedTrialPeriod: Boolean!
+
+	"""
+	Nom(s) de famille de l'utilisateur
+
+	Notez que dans certaines cultures, les personnes peuvent avoir plusieurs noms de famille ou
+	aucun nom de famille ; tous peuvent être présents, les noms étant séparés par des caractères d'espacement.
+
+	Par exemple, `Leroy`
+	"""
+	familyName: String!
+
+	"""
+	Prénoms de l'utilisateur
+
+	Tous les prénoms de l'utilisateur concaténés ensembles.
+
+	Par exemple, `Jean Pierre-Jacques` ou `Robert`
+	"""
+	forenames: String!
+
+	"""
+	Années d'étude d'écart de l'utilisateur
+
+	Désigne des périodes de temps où un utilisateur prend une pause ou un écart par
+	rapport à la norme. Cela peut être causé par une année sabbatique, une année de
+	redoublement ou pour d'autres raisons.
+
+	Par défaut cette valeur est de 0.
+	"""
+	gapYear: Int!
+
+	"""
+	Genre de l'utilisateur
+
+	Par défaut à `UNKNOWN`.
+	"""
+	gender: Gender!
+
+	"""
+	Prénom(s) de l'utilisateur
+
+	Notez que dans certaines cultures, les personnes peuvent avoir plusieurs prénoms ;
+	tous peuvent être présents, les noms étant séparés par des caractères d'espacement.
+
+	Par exemple, `Jean` ou `Pierre Jacques`
+	"""
+	givenName: String!
+
+	"""
+	Prénom(s) de naissance de l'utilisateur
+
+	Notez que dans certaines cultures, les personnes peuvent avoir plusieurs prénoms ;
+	tous peuvent être présents, les noms étant séparés par des caractères d'espacement.
+
+	Par exemple, `Jean` ou `Pierre Jacques`
+	"""
+	givenNameAtBirth: String!
+
+	"""
+	Prénom(s) d'usage de l'utilisateur
+
+	Notez que dans certaines cultures, les personnes peuvent avoir plusieurs prénoms ;
+	tous peuvent être présents, les noms étant séparés par des caractères d'espacement.
+
+	Par exemple, `Jeanne` ou `Pierre Jacques`
+	"""
+	givenNameInUse: String
+
+	"""
+	Groupe de l'utilisateur
+	"""
+	group(id: GroupId!): GroupOfMember
+
+	"""
+	Historique des groupes de l'utilisateur
+
+	Cet historique retrace tous les rôles que l'utilisateur à pu prendre au
+	sein des groups de l'école.
+	"""
+	groupHistory(filter: HistoricalGroupOfMemberFilter): [HistoricalGroupOfMember!]!
+
+	"""
+	Groupes de l'utilisateur
+
+	Les rôles passés ne seront pas renvoyés dans cette requête.
+	Pour cela, se référer à `group_history.`
+	"""
+	groups(filter: GroupOfMemberFilter): [GroupOfMember!]!
+
+	"""
+	Compte caché
+
+	Le compte utilisateur sera invisible de toutes les requêtes, sauf admin si le filtre
+	de la requête le demande.
+	"""
+	hidden: Boolean!
+
+	"""
+	Identifiant de l'utilisateur
+
+	Plus communément appelé AriseID, il est composé de 8 caractères maximum formés du nom
+	et du prénom, et de 4 chiffre représentant l'année d'arrivée pour arriver à un identifiant unique.
+
+	Par exemple, `acier2020` ou `dupontj2042`.
+	"""
+	id: Identifier!
+
+	"""
+	Promotion d'origine de l'utilisateur
+
+	La promotion présupposée de l'utilisateur au moment où il rentre à l'école.
+
+	Habituellement `année d'entrée + 3`
+	"""
+	initialPromotion: Int!
+
+	"""
+	Date de la dernière activité via l'API de l'utilisateur
+
+	Sa valeur est un string JSON [RFC3339] représentant le temps entre 1970-01-01T0:0:0Z,
+	mesuré en UTC, et la date/heure.
+	"""
+	lastUsedAt: DateTime
+
+	"""
+	Langue
+
+	Les paramètres régionaux de l'utilisateur, représentés par une balise de
+	langue BCP47 [RFC5646]. Il s'agit généralement d'un code de langue ISO 639-1
+	Alpha-2 [ISO639-1] en minuscules et d'un code de pays ISO 3166-1 Alpha-2
+	[ISO3166-1] en majuscules, séparés par un tiret.
+
+	Par exemple, `en-US` ou `fr-CA`.
+	"""
+	locale: String!
+
+	"""
+	Deuxième(s) prénom(s) de l'utilisateur
+
+	Notez que dans certaines cultures, les personnes peuvent avoir plusieurs seconds prénoms ;
+	tous peuvent être présents, les noms étant séparés par des caractères d'espacement.
+	Notez également que dans certaines cultures, les seconds prénoms ne sont pas utilisés.
+
+	Par exemple, `Robert`
+	"""
+	middleName: String
+
+	"""
+	Nom complet de l'utilisateur
+
+	Présenté sous une forme affichable, avec toutes les parties du nom, **Y COMPRIS** le surnom.
+
+	Par exemple, `Jean "Foobar" Dupont` ou `Jean Dupont`
+	"""
+	name: String!
+
+	"""
+	Surnom de l'utilisateur
+
+	Le surnom qu'a choisi l'utilisateur.
+
+	Par exemple, `Foobar` ou `Jean`
+	"""
+	nickname: String
+
+	"""
+	Groupe courant de l'utilisateur
+
+	Retourne le propriétaire de l'application si ce dernier est un groupe.
+
+	Les rôles passés ne seront pas renvoyés dans cette requête.
+	Pour cela, se référer à `current_group_history.`
+	"""
+	oAuthGroup: GroupOfMember
+
+	"""
+	Hash du mot de passe de l'utilisateur
+
+	Standard actuel : Argon2id
+
+	Par exemple, `$argon2id$v=19$m=19456,t=2,p=1$XMBjfqZtpEyVxZbGNjKPCg$krFvSFSL3XUr6736galD8YVmGgXpqNSc02VyLqFesPY'`
+	"""
+	passwordHash: String
+
+	"""
+	Numéro de téléphone de l'utilisateur
+
+	E.164 [E.164] est le format adopté pour ce champ.
+
+	Par exemple, `+14255551212` ou `+5626872400`.
+	"""
+	phoneNumber: String
+
+	"""
+	Numéro de téléphone de l'utilisateur vérifié
+
+	Vrai si le numéro de téléphone de l'utilisateur a été vérifié, sinon faux.
+	Lorsque cette valeur d'allégation est vraie, cela signifie qu'ARISE a pris
+	des mesures positives pour s'assurer que ce numéro de téléphone était contrôlé
+	par l'utilisateur final au moment où la vérification a été effectuée. Les moyens
+	par lesquels un numéro de téléphone est vérifié sont spécifiques au contexte
+	et dépendent du cadre de confiance ou des accords contractuels dans lesquels
+	les parties opèrent.
+	"""
+	phoneNumberVerified: Boolean!
+
+	"""
+	URL de la photo de trombinoscope de l'utilisateur
+
+	Cette URL fait référence à un fichier image (par exemple, un fichier image PNG,
+	JPEG ou GIF), plutôt qu'à une page Web contenant une image.
+
+	Par exemple, `https://api.iiens.net/rest/v0/photo/acier2020`
+	"""
+	photo: Url!
+
+	"""
+	Hash de la photo de trombinoscope de l'utilisateur
+	"""
+	photoThumbnailHash: Base64
+
+	"""
+	URL de la photo de profil de l'utilisateur
+
+	Cette URL fait référence à un fichier image (par exemple, un fichier image PNG,
+	JPEG ou GIF), plutôt qu'à une page Web contenant une image.
+
+	Par exemple, `https://api.iiens.net/picture/acier2020`
+	"""
+	picture: Url!
+
+	"""
+	Hash de la photo de profil de l'utilisateur
+	"""
+	pictureThumbnailHash: Base64!
+
+	"""
+	Pseudo préféré
+
+	Pseudo abrégé non unique par lequel l'utilisateur souhaite être désigné.
+
+	Surnom si renseigné par l'utilisateur, sinon nom civil par défaut.
+
+	Par exemple, `Foobar` ou `dupontj2042`
+	"""
+	preferredNickname: String!
+
+	"""
+	Nom d'utilisateur préféré
+
+	Nom abrégé non unique par lequel l'utilisateur souhaite être désigné.
+
+	Surnom si renseigné par l'utilisateur, sinon identifiant ARISE par défaut.
+
+	Par exemple, `Foobar` ou `dupontj2042`
+	"""
+	preferredUsername: String!
+
+	"""
+	URL de la page de profil de l'utilisateur
+
+	Page de profil sur [www.iiens.net](https://www.iiens.net).
+
+	Par exemple, `https://www.iiens.net/eleve/acier2020`
+	"""
+	profile: Url!
+
+	"""
+	Promotion effective de l'utilisateur
+
+	Promotion de l'utilisateur en tenant compte des redoublements et autres facteurs.
+	"""
+	promotion: Int!
+
+	"""
+	Accès restreint aux services d'ARISE
+
+	**VRAI** si la période d'essai est arrivée à terme (42 jours) et que l'utilisateur n'est pas
+	membre d'ARISE **ET** de l'AEIIE.
+	"""
+	restrictedAccess: Boolean!
+
+	"""
+	Adresse électronique scolaire de l'utilisateur
+
+	Sa valeur DOIT être conforme à la syntaxe addr-spec de la RFC 5322 [RFC5322].
+
+	Par exemple, `mael.acier@ensiie.eu` ou `dupont.jean@ensiie.eu`.
+	"""
+	schoolEmail: String!
+
+	"""
+	Identifiant de connexion scolaire de l'utilisateur
+
+	Habituellement sous la forme `prenom.nom`
+	"""
+	schoolLogin: String!
+
+	"""
+	Semestre actuel de l'utilisateur
+
+	Compteur de semestres en fonction de l'année de promotion effective.
+	Les redoublements sont pris en compte dans l'évaluation de cette valeur.
+
+	Par exemple, `S1`, `S2`, `S3` *(Le `S` n'est pas ajouté automatiquement)*
+	"""
+	semester: Int!
+
+	"""
+	Compte suspendu
+
+	Le compte utilisateur ne sera plus utilisable pour se connecter aux services d'ARISE.
+	"""
+	suspended: Boolean!
+
+	"""
+	## Parcours
+
+	Par exemple, "FISE", "FISA"
+	"""
+	training: Training!
+
+	"""
+	Nombre de jours restants sur la période d'essai de l'utilisateur
+
+	**42** lors de la création du compte.
+	"""
+	trialPeriodDaysLeft: Int!
+
+	"""
+	Compte UNIX
+	"""
+	unixAccount: UnixAccount
+
+	"""
+	Date de la dernière mise à jour des informations relatives à l'utilisateur
+
+	Sa valeur est un string JSON [RFC3339] représentant le temps entre 1970-01-01T0:0:0Z,
+	mesuré en UTC, et la date/heure.
+	"""
+	updatedAt: DateTime!
+
+	"""
+	UUID du compte de l'utilisateur
+	"""
+	uuid: UUID!
+
+	"""
+	URL du site Web ou du blog de l'utilisateur
+
+	Par exemple, `https://foo.com`
+	"""
+	website: Url
+
+	"""
+	Année en cours de l'utilisateur
+
+	Compteur d'années en fonction de l'année de promotion effective.
+	Les redoublements sont pris en compte dans l'évaluation de cette valeur.
+
+	Par exemple, `1A`, `2A`, `3A` *(Le `A` n'est pas ajouté automatiquement)*
+	"""
+	year: Int!
+
+	"""
+	Fuseau horaire
+
+	Chaîne de caractères provenant de la base de données des fuseaux horaires zoneinfo
+	[zoneinfo] représentant le fuseau horaire de l'utilisateur.
+
+	Par exemple, `Europe/Paris` ou `Amérique/Los_Angeles`.
+	"""
+	zoneinfo: String!
+}
+
+type UserConnection {
+	"""
+	A list of edges.
+	"""
+	edges: [UserEdge!]!
+
+	"""
+	A list of nodes.
+	"""
+	nodes: [User!]!
+
+	"""
+	Information to aid in pagination.
+	"""
+	pageInfo: PageInfo!
+	remainingCount: Int!
+}
+
+"""
+An edge in a connection.
+"""
+type UserEdge {
+	"""
+	A cursor for use in pagination
+	"""
+	cursor: String!
+
+	"""
+	The item at the end of the edge
+	"""
+	node: User!
+	remainingCount: Int!
+}
+
+input UserFilter {
+	accountUuid: UuidFilter
+	aeiieMember: BooleanFilter
+	backgroundThumbnailHash: NullableFilter
+	birthdate: NullableDateFilter
+	createdAt: DateTimeFilter
+	diplomaYearDuration: Int16Filter
+	email: TextFilter
+	emailVerified: BooleanFilter
+	extendedTrialPeriod: BooleanFilter
+	familyName: TextFilter
+	gapYear: Int16Filter
+	gender: GenderFilter
+	givenName: TextFilter
+	givenNameAtBirth: TextFilter
+	givenNameInUse: NullableTextFilter
+	groups: UserGroupsFilter
+	hidden: BooleanFilter
+	id: TextFilter
+	initialPromotion: Int16Filter
+	lastUsedAt: NullableDateTimeFilter
+	locale: TextFilter
+	middleName: NullableTextFilter
+	nickname: NullableTextFilter
+	or: [UserFilter!]
+	phoneNumber: NullableTextFilter
+	phoneNumberVerified: BooleanFilter
+	photoThumbnailHash: NullableFilter
+	pictureThumbnailHash: NullableFilter
+	promotion: Int16Filter
+	public: BooleanFilter
+	restrictedAccess: BooleanFilter
+	schoolLogin: TextFilter
+	semester: Int16Filter
+	suspended: BooleanFilter
+	training: TextFilter
+	trialPeriodDaysLeft: Int16Filter
+	unixAccount: UnixAccountFilter
+	updatedAt: DateTimeFilter
+	uuid: UuidFilter
+	website: NullableTextFilter
+	year: Int16Filter
+	zoneinfo: TextFilter
+}
+
+input UserGroupsFilter {
+	active: BooleanFilter
+	hidden: BooleanFilter
+	id: TextFilter
+	name: TextFilter
+	type: GroupTypeFilter
+	uuid: UuidFilter
+}
+
+input UserId {
+	id: SmolStr
+	personalEmail: String
+	urn: Urn
+	uuid: UUID
+}
+
+input UuidFilter {
+	isIn: [UUID!]!
+}
diff --git a/src/lib/index.ts b/src/lib/index.ts
deleted file mode 100644
index 856f2b6c38aec1085db88189bcf492dbb49a1c45..0000000000000000000000000000000000000000
--- a/src/lib/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..705385da79df790db128b858708751cf3cea8f39
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,13 @@
+export function getRandomItems<T>(array: T[], count: number): T[] {
+	if (count === 0) return [];
+	if (count > array.length) {
+		throw new Error('Count is greater than the size of the set');
+	}
+	const index = Math.floor(Math.random() * array.length);
+	const item = array[index];
+	array.splice(index, 1);
+
+	const items = getRandomItems(array, count - 1);
+	items.push(item);
+	return items;
+}
diff --git a/src/routes/pokemon/+page.svelte b/src/routes/pokemon/+page.svelte
index c55f01e15eec644abf6933a1811143f5f496cc99..fc5adb80d5d6c53eecbae6694a075a1301aac67b 100644
--- a/src/routes/pokemon/+page.svelte
+++ b/src/routes/pokemon/+page.svelte
@@ -249,7 +249,7 @@
 										selected.index === index &&
 										selected.name !== answer.name}
 									class="poke-options-button"
-									on:click={() => selectAnswer({...pokemon, index})}
+									on:click={() => selectAnswer({ ...pokemon, index })}
 									>{prettifyName(pokemon.name)}</button
 								>
 							{/each}
diff --git a/src/routes/pokemon/style.css b/src/routes/pokemon/style.css
index 97b84b54ed7ca559110d1ef0b65ac8c1f39e111b..2df9b7ca43e8cea7cc003fe491810acc316d2f90 100644
--- a/src/routes/pokemon/style.css
+++ b/src/routes/pokemon/style.css
@@ -1,449 +1,457 @@
 @import 'https://fonts.googleapis.com/css?family=VT323';
 * {
-  box-sizing: border-box;
+	box-sizing: border-box;
 }
 
 body {
-  background-color: #f65a52;
-  font-family: "VT323", monospace;
-  font-size: 16px;
-  line-height: 1.875em;
-  color: #333;
+	background-color: #f65a52;
+	font-family: 'VT323', monospace;
+	font-size: 16px;
+	line-height: 1.875em;
+	color: #333;
 }
 
 .container {
-  width: 100%;
-  max-width: 400px;
-  position: relative;
-  margin: 50px auto;
+	width: 100%;
+	max-width: 400px;
+	position: relative;
+	margin: 50px auto;
 }
 
 h2 {
-  font-size: 1.25rem;
-  white-space: nowrap;
+	font-size: 1.25rem;
+	white-space: nowrap;
 }
 
 .spacer {
-  margin-bottom: 0.5rem;
+	margin-bottom: 0.5rem;
 }
 
 .button {
-  padding: 0.5em 1.5em;
-  border-radius: 1rem;
-  border: solid 1px transparent;
-  font-family: "VT323", monospace;
-  font-size: 1.5rem;
-  background-color: #f32c22;
-  color: #333;
-  cursor: pointer;
-  transition: 0.35s;
+	padding: 0.5em 1.5em;
+	border-radius: 1rem;
+	border: solid 1px transparent;
+	font-family: 'VT323', monospace;
+	font-size: 1.5rem;
+	background-color: #f32c22;
+	color: #333;
+	cursor: pointer;
+	transition: 0.35s;
 }
 .button:focus {
-  outline: none;
-  border: 1px dotted #f87f79;
+	outline: none;
+	border: 1px dotted #f87f79;
 }
 .button:not([disabled]):hover {
-  background-color: #333;
-  color: #f65a52;
+	background-color: #333;
+	color: #f65a52;
 }
 
 .poke-section {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  position: relative;
-  max-width: 500px;
-  margin: auto;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	position: relative;
+	max-width: 500px;
+	margin: auto;
 }
 
 .poke-intro-trainer {
-  position: relative;
-  margin-bottom: 1rem;
-  height: 200px;
-  width: 200px;
+	position: relative;
+	margin-bottom: 1rem;
+	height: 200px;
+	width: 200px;
 }
 .poke-intro-trainer .poke-trainer-img {
-  position: absolute;
-  left: 50%;
-  bottom: 0;
-  height: 200px;
-  opacity: 0;
-  transition: 0.4s cubic-bezier(0.22, 0.75, 0.53, 0.99);
+	position: absolute;
+	left: 50%;
+	bottom: 0;
+	height: 200px;
+	opacity: 0;
+	transition: 0.4s cubic-bezier(0.22, 0.75, 0.53, 0.99);
 }
 @media (max-width: 479px) {
-  .poke-intro-trainer .poke-trainer-img {
-    display: none;
-  }
+	.poke-intro-trainer .poke-trainer-img {
+		display: none;
+	}
 }
 .poke-intro-trainer .poke-trainer-img.active {
-  transform: translateX(-50%);
-  opacity: 1;
+	transform: translateX(-50%);
+	opacity: 1;
 }
 .poke-intro-trainer .poke-trainer-img-classic {
-  bottom: 5px;
-  height: 180px;
-  -ms-interpolation-mode: nearest-neighbor;
-      image-rendering: -moz-crisp-edges;
-      image-rendering: pixelated;
-  transform: translateX(-80%);
+	bottom: 5px;
+	height: 180px;
+	-ms-interpolation-mode: nearest-neighbor;
+	image-rendering: -moz-crisp-edges;
+	image-rendering: pixelated;
+	transform: translateX(-80%);
 }
 .poke-intro-trainer .poke-trainer-img-master {
-  transform: translateX(-20%);
+	transform: translateX(-20%);
 }
 
 .poke-ball {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  height: 150px;
-  width: 150px;
-  border-radius: 50%;
-  background-color: #f32c22;
-  transform: translate(-50%, -50%);
-  overflow: hidden;
-}
-.poke-ball::before, .poke-ball::after {
-  content: "";
-  position: absolute;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	height: 150px;
+	width: 150px;
+	border-radius: 50%;
+	background-color: #f32c22;
+	transform: translate(-50%, -50%);
+	overflow: hidden;
+}
+.poke-ball::before,
+.poke-ball::after {
+	content: '';
+	position: absolute;
 }
 .poke-ball::before {
-  z-index: 2;
-  top: 50%;
-  left: 50%;
-  height: 40px;
-  width: 40px;
-  border-radius: 50%;
-  border: solid 6px #f65a52;
-  background-color: #fa9f9b;
-  transform: translate(-50%, -50%);
+	z-index: 2;
+	top: 50%;
+	left: 50%;
+	height: 40px;
+	width: 40px;
+	border-radius: 50%;
+	border: solid 6px #f65a52;
+	background-color: #fa9f9b;
+	transform: translate(-50%, -50%);
 }
 .poke-ball::after {
-  z-index: 1;
-  top: 50%;
-  height: 50%;
-  width: 100%;
-  border-top: solid 6px #f65a52;
-  background-color: #fa9f9b;
+	z-index: 1;
+	top: 50%;
+	height: 50%;
+	width: 100%;
+	border-top: solid 6px #f65a52;
+	background-color: #fa9f9b;
 }
 
 .poke-title {
-  position: absolute;
-  top: -2rem;
+	position: absolute;
+	top: -2rem;
 }
 
 .poke-question {
-  position: absolute;
-  right: calc(100% + 0.5rem);
-  display: flex;
-  flex-direction: column;
-  align-items: flex-end;
+	position: absolute;
+	right: calc(100% + 0.5rem);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
 }
 .poke-question-wrapper {
-  position: relative;
-  width: 250px;
+	position: relative;
+	width: 250px;
 }
 .poke-question-number {
-  font-size: 8rem;
-  line-height: 0.4;
-  color: #fa9f9b;
+	font-size: 8rem;
+	line-height: 0.4;
+	color: #fa9f9b;
 }
 
 .poke-score {
-  position: absolute;
-  top: 6rem;
-  right: calc(100% + 0.5rem);
-  padding-top: 1rem;
-  font-size: 1.25rem;
-  white-space: nowrap;
-  color: #333;
+	position: absolute;
+	top: 6rem;
+	right: calc(100% + 0.5rem);
+	padding-top: 1rem;
+	font-size: 1.25rem;
+	white-space: nowrap;
+	color: #333;
 }
 .poke-score::before {
-  content: "";
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 40px;
-  height: 6px;
-  background-color: #333;
+	content: '';
+	position: absolute;
+	top: 0;
+	right: 0;
+	width: 40px;
+	height: 6px;
+	background-color: #333;
 }
 
 .poke-image {
-  position: relative;
-  z-index: 2;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 250px;
-  height: 250px;
-  border-radius: 1rem;
-  border: solid 6px #333;
-  background-color: #fff;
-  overflow: hidden;
-}
-.poke-image::before, .poke-image::after {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  border-radius: 50%;
+	position: relative;
+	z-index: 2;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	width: 250px;
+	height: 250px;
+	border-radius: 1rem;
+	border: solid 6px #333;
+	background-color: #fff;
+	overflow: hidden;
+}
+.poke-image::before,
+.poke-image::after {
+	content: '';
+	position: absolute;
+	z-index: -1;
+	border-radius: 50%;
 }
 .poke-image::before {
-  width: 100px;
-  height: 100px;
-  background-color: #c5d5ee;
-  opacity: 1;
-  transition: 0.65s ease-in-out;
+	width: 100px;
+	height: 100px;
+	background-color: #c5d5ee;
+	opacity: 1;
+	transition: 0.65s ease-in-out;
 }
 .poke-image::after {
-  width: 100px;
-  height: 100px;
-  border: solid 12px #c5d5ee;
-  transform: scale(0);
-  transition: 0.4s ease-in-out;
+	width: 100px;
+	height: 100px;
+	border: solid 12px #c5d5ee;
+	transform: scale(0);
+	transition: 0.4s ease-in-out;
 }
 .poke-image-img {
-  width: auto;
-  height: 150px;
+	width: auto;
+	height: 150px;
 }
-.poke-image-success::before, .poke-image-error::before {
-  transform: scale(4);
-  opacity: 0.5;
+.poke-image-success::before,
+.poke-image-error::before {
+	transform: scale(4);
+	opacity: 0.5;
 }
-.poke-image-success::after, .poke-image-error::after {
-  transform: scale(1);
+.poke-image-success::after,
+.poke-image-error::after {
+	transform: scale(1);
 }
 .poke-image-success::before {
-  background-color: #7bd55a;
+	background-color: #7bd55a;
 }
 .poke-image-success::after {
-  border-color: #7bd55a;
+	border-color: #7bd55a;
 }
 .poke-image-error::before {
-  background-color: #ff8b62;
+	background-color: #ff8b62;
 }
 .poke-image-error::after {
-  border-color: #ff8b62;
-  width: 10px;
-  border-radius: 1rem;
-  transform: rotate(45deg);
+	border-color: #ff8b62;
+	width: 10px;
+	border-radius: 1rem;
+	transform: rotate(45deg);
 }
 
 .poke-options {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  z-index: 3;
-  top: -30px;
-  padding: 0 20px;
-  margin: 0 auto;
-  width: 170px;
-  border-radius: 1rem;
-  background-color: #333;
+	position: relative;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	z-index: 3;
+	top: -30px;
+	padding: 0 20px;
+	margin: 0 auto;
+	width: 170px;
+	border-radius: 1rem;
+	background-color: #333;
 }
 .poke-options:not(.poke-options-answers) .poke-options-button:not(.selected):hover {
-  background-color: #a9c1e6;
-  transform: translateY(-3px);
+	background-color: #a9c1e6;
+	transform: translateY(-3px);
 }
 .poke-options:not(.poke-options-answers) .poke-options-button:not(.selected):active::before {
-  transform: translate(-50%, -50%) scale(1);
+	transform: translate(-50%, -50%) scale(1);
 }
 .poke-options.poke-options-answers .poke-options-button {
-  cursor: default;
+	cursor: default;
 }
 .poke-options.poke-options-answers .poke-options-button:not(.error):not(.success) {
-  color: #94acbd;
+	color: #94acbd;
 }
 .poke-options-button {
-  position: relative;
-  width: 100%;
-  padding: 0.5em;
-  min-width: 200px;
-  max-height: 48px;
-  border: solid 6px #333;
-  border-radius: 1rem;
-  background-color: #c5d5ee;
-  font-family: "VT323", monospace;
-  font-size: 1.125rem;
-  transition: 0.45s;
-  cursor: pointer;
-  overflow: hidden;
+	position: relative;
+	width: 100%;
+	padding: 0.5em;
+	min-width: 200px;
+	max-height: 48px;
+	border: solid 6px #333;
+	border-radius: 1rem;
+	background-color: #c5d5ee;
+	font-family: 'VT323', monospace;
+	font-size: 1.125rem;
+	transition: 0.45s;
+	cursor: pointer;
+	overflow: hidden;
 }
 .poke-options-button:focus {
-  outline: none;
+	outline: none;
 }
 .poke-options-button::before {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  left: 50%;
-  top: 50%;
-  height: 200px;
-  width: 200px;
-  border-radius: 50%;
-  background-color: #94acbd;
-  transform: translate(-50%, -50%) scale(0);
-  transition: 0.2s ease-in-out;
+	content: '';
+	position: absolute;
+	z-index: -1;
+	left: 50%;
+	top: 50%;
+	height: 200px;
+	width: 200px;
+	border-radius: 50%;
+	background-color: #94acbd;
+	transform: translate(-50%, -50%) scale(0);
+	transition: 0.2s ease-in-out;
 }
 .poke-options-button:not(:last-child) {
-  margin-bottom: 3px;
+	margin-bottom: 3px;
 }
 .poke-options-button.selected {
-  background-color: #94acbd;
+	background-color: #94acbd;
 }
 .poke-options-button.error {
-  background-color: #ff8b62;
+	background-color: #ff8b62;
 }
 .poke-options-button.success {
-  background-color: #7bd55a;
+	background-color: #7bd55a;
 }
 
 .poke-buttons {
-  text-align: center;
+	text-align: center;
 }
 @media (min-width: 480px) {
-  .poke-buttons {
-    position: absolute;
-    top: 20px;
-    left: 100%;
-  }
-  .poke-buttons .button {
-    padding-left: calc(1em + 10px);
-    border-top-left-radius: 0;
-    border-bottom-left-radius: 0;
-    transform: translateX(-10px);
-  }
+	.poke-buttons {
+		position: absolute;
+		top: 20px;
+		left: 100%;
+	}
+	.poke-buttons .button {
+		padding-left: calc(1em + 10px);
+		border-top-left-radius: 0;
+		border-bottom-left-radius: 0;
+		transform: translateX(-10px);
+	}
 }
 .poke-buttons .button {
-  padding: 1em;
-  width: 110px;
-  height: 100px;
-  color: #fff;
+	padding: 1em;
+	width: 110px;
+	height: 100px;
+	color: #fff;
 }
 .poke-buttons .button[disabled] {
-  color: #fa9f9b;
-  opacity: 0.7;
-  cursor: default;
+	color: #fa9f9b;
+	opacity: 0.7;
+	cursor: default;
 }
 .poke-buttons .button:not([disabled]):hover {
-  transform: translateX(0);
+	transform: translateX(0);
 }
 .poke-buttons .button:not(:last-child) {
-  margin-bottom: 6px;
+	margin-bottom: 6px;
 }
 
 .poke-final {
-  text-align: center;
+	text-align: center;
 }
 .poke-final-score {
-  display: block;
-  position: relative;
-  margin-bottom: 1rem;
+	display: block;
+	position: relative;
+	margin-bottom: 1rem;
 }
 .poke-final-score::before {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  top: 50%;
-  left: 50%;
-  height: 100px;
-  width: 100px;
-  border-radius: 50%;
-  border: solid 12px #f32c22;
-  transform: translate(-50%, -50%);
-  opacity: 0.3;
-  -webkit-animation: grow 2s infinite ease-in-out;
-          animation: grow 2s infinite ease-in-out;
+	content: '';
+	position: absolute;
+	z-index: -1;
+	top: 50%;
+	left: 50%;
+	height: 100px;
+	width: 100px;
+	border-radius: 50%;
+	border: solid 12px #f32c22;
+	transform: translate(-50%, -50%);
+	opacity: 0.3;
+	-webkit-animation: grow 2s infinite ease-in-out;
+	animation: grow 2s infinite ease-in-out;
 }
 .poke-final-score-number {
-  font-size: 8rem;
-  line-height: 0.4;
-  color: #fa9f9b;
+	font-size: 8rem;
+	line-height: 0.4;
+	color: #fa9f9b;
 }
 
 .poke-classic .poke-image-img {
-  -ms-interpolation-mode: nearest-neighbor;
-      image-rendering: -moz-crisp-edges;
-      image-rendering: pixelated;
+	-ms-interpolation-mode: nearest-neighbor;
+	image-rendering: -moz-crisp-edges;
+	image-rendering: pixelated;
 }
 
-.animate-section-enter-active, .animate-section-leave-active {
-  transition: 0.4s ease-in-out;
+.animate-section-enter-active,
+.animate-section-leave-active {
+	transition: 0.4s ease-in-out;
 }
-.animate-section-enter, .animate-section-leave-to {
-  opacity: 0;
+.animate-section-enter,
+.animate-section-leave-to {
+	opacity: 0;
 }
 .animate-section-enter .poke-final-score-number {
-  transform: translateY(-30px);
+	transform: translateY(-30px);
 }
 .animate-section-leave-active {
-  transform: translateX(-30%);
+	transform: translateX(-30%);
 }
 .animate-section-enter-active {
-  transition-delay: 0.1s;
-  position: absolute;
-  top: 0;
-  left: 50%;
-  transform: translateX(-50%);
+	transition-delay: 0.1s;
+	position: absolute;
+	top: 0;
+	left: 50%;
+	transform: translateX(-50%);
 }
 
 .animate-options-enter-active {
-  transition: 0.4s ease-in-out;
+	transition: 0.4s ease-in-out;
 }
 .animate-options-enter-active:nth-child(4) {
-  transition-delay: 0s;
+	transition-delay: 0s;
 }
 .animate-options-enter-active:nth-child(5) {
-  transition-delay: 0.2s;
+	transition-delay: 0.2s;
 }
 .animate-options-enter-active:nth-child(6) {
-  transition-delay: 0.4s;
+	transition-delay: 0.4s;
 }
 .animate-options-enter-active:nth-child(7) {
-  transition-delay: 0.6s;
+	transition-delay: 0.6s;
 }
 .animate-options-enter-active:nth-child(8) {
-  transition-delay: 0.8s;
+	transition-delay: 0.8s;
 }
 .animate-options-enter {
-  transform: rotateX(-45deg);
-  transform-origin: top center;
-  opacity: 0;
+	transform: rotateX(-45deg);
+	transform-origin: top center;
+	opacity: 0;
 }
 .animate-options-leave-active {
-  position: absolute;
-  z-index: -1;
-  transition: 0.8s ease-in-out;
+	position: absolute;
+	z-index: -1;
+	transition: 0.8s ease-in-out;
 }
-.animate-options-leave-active[data-index="0"] {
-  top: 0;
+.animate-options-leave-active[data-index='0'] {
+	top: 0;
 }
-.animate-options-leave-active[data-index="1"] {
-  top: 51px;
+.animate-options-leave-active[data-index='1'] {
+	top: 51px;
 }
-.animate-options-leave-active[data-index="2"] {
-  top: 102px;
+.animate-options-leave-active[data-index='2'] {
+	top: 102px;
 }
-.animate-options-leave-active[data-index="3"] {
-  top: 153px;
+.animate-options-leave-active[data-index='3'] {
+	top: 153px;
 }
 .animate-options-leave-to {
-  opacity: 0;
+	opacity: 0;
 }
 
 @-webkit-keyframes grow {
-  0%, 100% {
-    transform: translate(-50%, -50%) scale(1);
-  }
-  50% {
-    transform: translate(-50%, -50%) scale(0.6);
-  }
+	0%,
+	100% {
+		transform: translate(-50%, -50%) scale(1);
+	}
+	50% {
+		transform: translate(-50%, -50%) scale(0.6);
+	}
 }
 
 @keyframes grow {
-  0%, 100% {
-    transform: translate(-50%, -50%) scale(1);
-  }
-  50% {
-    transform: translate(-50%, -50%) scale(0.6);
-  }
-}
\ No newline at end of file
+	0%,
+	100% {
+		transform: translate(-50%, -50%) scale(1);
+	}
+	50% {
+		transform: translate(-50%, -50%) scale(0.6);
+	}
+}
diff --git a/src/routes/who/+page.svelte b/src/routes/who/+page.svelte
index ec453151a1f65489c99b06e9b32ceb637f7369e9..155c82935faf5efb3bcd11832a0885fd224cad64 100644
--- a/src/routes/who/+page.svelte
+++ b/src/routes/who/+page.svelte
@@ -158,7 +158,7 @@
 	<div class:poke-classic={classic} class="container">
 		<transition name="animate-section">
 			{#if !isPlaying && !isDone}
-				<Trainer on:start-game={(e) => startGame(e.detail)}/>
+				<Trainer on:start-game={(e) => startGame(e.detail)} />
 			{/if}
 		</transition>
 
diff --git a/src/routes/who/Trainer.svelte b/src/routes/who/Trainer.svelte
index 891d14324024ccf54411d9504a293479dc428b4b..f15186420b5407372d3f74c157515adc8330d69f 100644
--- a/src/routes/who/Trainer.svelte
+++ b/src/routes/who/Trainer.svelte
@@ -2,8 +2,8 @@
 	import { createEventDispatcher } from 'svelte';
 
 	const dispatch = createEventDispatcher<{
-        "start-game": number;
-    }>();
+		'start-game': number;
+	}>();
 
 	let trainerHovered: string | null = null;
 
diff --git a/src/routes/who/style.css b/src/routes/who/style.css
index 876b1bf76fc1ab6dbca854130fe3657a4c65c0c1..f5855316cffe00d7dd8affcbf50e1187c570e9f6 100644
--- a/src/routes/who/style.css
+++ b/src/routes/who/style.css
@@ -1,450 +1,458 @@
 /* @import 'https://fonts.googleapis.com/css?family=VT323'; */
 @import 'https://fonts.googleapis.com/css?family=Londrina+Solid|Nunito:400,300';
 * {
-  box-sizing: border-box;
+	box-sizing: border-box;
 }
 
 body {
-  background-color: #f65a52;
-  font-family: "Londrina Solid", monospace;
-  font-size: 16px;
-  line-height: 1.875em;
-  color: #333;
+	background-color: #f65a52;
+	font-family: 'Londrina Solid', monospace;
+	font-size: 16px;
+	line-height: 1.875em;
+	color: #333;
 }
 
 .container {
-  width: 100%;
-  max-width: 400px;
-  position: relative;
-  margin: 50px auto;
+	width: 100%;
+	max-width: 400px;
+	position: relative;
+	margin: 50px auto;
 }
 
 h2 {
-  font-size: 1.25rem;
-  white-space: nowrap;
+	font-size: 1.25rem;
+	white-space: nowrap;
 }
 
 .spacer {
-  margin-bottom: 0.5rem;
+	margin-bottom: 0.5rem;
 }
 
 .button {
-  padding: 0.5em 1.5em;
-  border-radius: 1rem;
-  border: solid 1px transparent;
-  font-family: "Londrina Solid", monospace;
-  font-size: 1.5rem;
-  background-color: #f32c22;
-  color: #333;
-  cursor: pointer;
-  transition: 0.35s;
+	padding: 0.5em 1.5em;
+	border-radius: 1rem;
+	border: solid 1px transparent;
+	font-family: 'Londrina Solid', monospace;
+	font-size: 1.5rem;
+	background-color: #f32c22;
+	color: #333;
+	cursor: pointer;
+	transition: 0.35s;
 }
 .button:focus {
-  outline: none;
-  border: 1px dotted #f87f79;
+	outline: none;
+	border: 1px dotted #f87f79;
 }
 .button:not([disabled]):hover {
-  background-color: #333;
-  color: #f65a52;
+	background-color: #333;
+	color: #f65a52;
 }
 
 .poke-section {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  position: relative;
-  max-width: 500px;
-  margin: auto;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	position: relative;
+	max-width: 500px;
+	margin: auto;
 }
 
 .poke-intro-trainer {
-  position: relative;
-  margin-bottom: 1rem;
-  height: 200px;
-  width: 200px;
+	position: relative;
+	margin-bottom: 1rem;
+	height: 200px;
+	width: 200px;
 }
 .poke-intro-trainer .poke-trainer-img {
-  position: absolute;
-  left: 50%;
-  bottom: 0;
-  height: 200px;
-  opacity: 0;
-  transition: 0.4s cubic-bezier(0.22, 0.75, 0.53, 0.99);
+	position: absolute;
+	left: 50%;
+	bottom: 0;
+	height: 200px;
+	opacity: 0;
+	transition: 0.4s cubic-bezier(0.22, 0.75, 0.53, 0.99);
 }
 @media (max-width: 479px) {
-  .poke-intro-trainer .poke-trainer-img {
-    display: none;
-  }
+	.poke-intro-trainer .poke-trainer-img {
+		display: none;
+	}
 }
 .poke-intro-trainer .poke-trainer-img.active {
-  transform: translateX(-50%);
-  opacity: 1;
+	transform: translateX(-50%);
+	opacity: 1;
 }
 .poke-intro-trainer .poke-trainer-img-classic {
-  bottom: 5px;
-  height: 180px;
-  -ms-interpolation-mode: nearest-neighbor;
-      image-rendering: -moz-crisp-edges;
-      image-rendering: pixelated;
-  transform: translateX(-80%);
+	bottom: 5px;
+	height: 180px;
+	-ms-interpolation-mode: nearest-neighbor;
+	image-rendering: -moz-crisp-edges;
+	image-rendering: pixelated;
+	transform: translateX(-80%);
 }
 .poke-intro-trainer .poke-trainer-img-master {
-  transform: translateX(-20%);
+	transform: translateX(-20%);
 }
 
 .poke-ball {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  height: 150px;
-  width: 150px;
-  border-radius: 50%;
-  background-color: #f32c22;
-  transform: translate(-50%, -50%);
-  overflow: hidden;
-}
-.poke-ball::before, .poke-ball::after {
-  content: "";
-  position: absolute;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	height: 150px;
+	width: 150px;
+	border-radius: 50%;
+	background-color: #f32c22;
+	transform: translate(-50%, -50%);
+	overflow: hidden;
+}
+.poke-ball::before,
+.poke-ball::after {
+	content: '';
+	position: absolute;
 }
 .poke-ball::before {
-  z-index: 2;
-  top: 50%;
-  left: 50%;
-  height: 40px;
-  width: 40px;
-  border-radius: 50%;
-  border: solid 6px #f65a52;
-  background-color: #fa9f9b;
-  transform: translate(-50%, -50%);
+	z-index: 2;
+	top: 50%;
+	left: 50%;
+	height: 40px;
+	width: 40px;
+	border-radius: 50%;
+	border: solid 6px #f65a52;
+	background-color: #fa9f9b;
+	transform: translate(-50%, -50%);
 }
 .poke-ball::after {
-  z-index: 1;
-  top: 50%;
-  height: 50%;
-  width: 100%;
-  border-top: solid 6px #f65a52;
-  background-color: #fa9f9b;
+	z-index: 1;
+	top: 50%;
+	height: 50%;
+	width: 100%;
+	border-top: solid 6px #f65a52;
+	background-color: #fa9f9b;
 }
 
 .poke-title {
-  position: absolute;
-  top: -2rem;
+	position: absolute;
+	top: -2rem;
 }
 
 .poke-question {
-  position: absolute;
-  right: calc(100% + 0.5rem);
-  display: flex;
-  flex-direction: column;
-  align-items: flex-end;
+	position: absolute;
+	right: calc(100% + 0.5rem);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
 }
 .poke-question-wrapper {
-  position: relative;
-  width: 250px;
+	position: relative;
+	width: 250px;
 }
 .poke-question-number {
-  font-size: 8rem;
-  line-height: 0.4;
-  color: #fa9f9b;
+	font-size: 8rem;
+	line-height: 0.4;
+	color: #fa9f9b;
 }
 
 .poke-score {
-  position: absolute;
-  top: 6rem;
-  right: calc(100% + 0.5rem);
-  padding-top: 1rem;
-  font-size: 1.25rem;
-  white-space: nowrap;
-  color: #333;
+	position: absolute;
+	top: 6rem;
+	right: calc(100% + 0.5rem);
+	padding-top: 1rem;
+	font-size: 1.25rem;
+	white-space: nowrap;
+	color: #333;
 }
 .poke-score::before {
-  content: "";
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 40px;
-  height: 6px;
-  background-color: #333;
+	content: '';
+	position: absolute;
+	top: 0;
+	right: 0;
+	width: 40px;
+	height: 6px;
+	background-color: #333;
 }
 
 .poke-image {
-  position: relative;
-  z-index: 2;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 250px;
-  height: 250px;
-  border-radius: 1rem;
-  border: solid 6px #333;
-  background-color: #fff;
-  overflow: hidden;
-}
-.poke-image::before, .poke-image::after {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  border-radius: 50%;
+	position: relative;
+	z-index: 2;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	width: 250px;
+	height: 250px;
+	border-radius: 1rem;
+	border: solid 6px #333;
+	background-color: #fff;
+	overflow: hidden;
+}
+.poke-image::before,
+.poke-image::after {
+	content: '';
+	position: absolute;
+	z-index: -1;
+	border-radius: 50%;
 }
 .poke-image::before {
-  width: 100px;
-  height: 100px;
-  background-color: #c5d5ee;
-  opacity: 1;
-  transition: 0.65s ease-in-out;
+	width: 100px;
+	height: 100px;
+	background-color: #c5d5ee;
+	opacity: 1;
+	transition: 0.65s ease-in-out;
 }
 .poke-image::after {
-  width: 100px;
-  height: 100px;
-  border: solid 12px #c5d5ee;
-  transform: scale(0);
-  transition: 0.4s ease-in-out;
+	width: 100px;
+	height: 100px;
+	border: solid 12px #c5d5ee;
+	transform: scale(0);
+	transition: 0.4s ease-in-out;
 }
 .poke-image-img {
-  width: auto;
-  height: 150px;
+	width: auto;
+	height: 150px;
 }
-.poke-image-success::before, .poke-image-error::before {
-  transform: scale(4);
-  opacity: 0.5;
+.poke-image-success::before,
+.poke-image-error::before {
+	transform: scale(4);
+	opacity: 0.5;
 }
-.poke-image-success::after, .poke-image-error::after {
-  transform: scale(1);
+.poke-image-success::after,
+.poke-image-error::after {
+	transform: scale(1);
 }
 .poke-image-success::before {
-  background-color: #7bd55a;
+	background-color: #7bd55a;
 }
 .poke-image-success::after {
-  border-color: #7bd55a;
+	border-color: #7bd55a;
 }
 .poke-image-error::before {
-  background-color: #ff8b62;
+	background-color: #ff8b62;
 }
 .poke-image-error::after {
-  border-color: #ff8b62;
-  width: 10px;
-  border-radius: 1rem;
-  transform: rotate(45deg);
+	border-color: #ff8b62;
+	width: 10px;
+	border-radius: 1rem;
+	transform: rotate(45deg);
 }
 
 .poke-options {
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  z-index: 3;
-  top: -30px;
-  padding: 0 20px;
-  margin: 0 auto;
-  width: 170px;
-  border-radius: 1rem;
-  background-color: #333;
+	position: relative;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	z-index: 3;
+	top: -30px;
+	padding: 0 20px;
+	margin: 0 auto;
+	width: 170px;
+	border-radius: 1rem;
+	background-color: #333;
 }
 .poke-options:not(.poke-options-answers) .poke-options-button:not(.selected):hover {
-  background-color: #a9c1e6;
-  transform: translateY(-3px);
+	background-color: #a9c1e6;
+	transform: translateY(-3px);
 }
 .poke-options:not(.poke-options-answers) .poke-options-button:not(.selected):active::before {
-  transform: translate(-50%, -50%) scale(1);
+	transform: translate(-50%, -50%) scale(1);
 }
 .poke-options.poke-options-answers .poke-options-button {
-  cursor: default;
+	cursor: default;
 }
 .poke-options.poke-options-answers .poke-options-button:not(.error):not(.success) {
-  color: #94acbd;
+	color: #94acbd;
 }
 .poke-options-button {
-  position: relative;
-  width: 100%;
-  padding: 0.5em;
-  min-width: 200px;
-  max-height: 48px;
-  border: solid 6px #333;
-  border-radius: 1rem;
-  background-color: #c5d5ee;
-  font-family: "Londrina Solid", monospace;
-  font-size: 1.125rem;
-  transition: 0.45s;
-  cursor: pointer;
-  overflow: hidden;
+	position: relative;
+	width: 100%;
+	padding: 0.5em;
+	min-width: 200px;
+	max-height: 48px;
+	border: solid 6px #333;
+	border-radius: 1rem;
+	background-color: #c5d5ee;
+	font-family: 'Londrina Solid', monospace;
+	font-size: 1.125rem;
+	transition: 0.45s;
+	cursor: pointer;
+	overflow: hidden;
 }
 .poke-options-button:focus {
-  outline: none;
+	outline: none;
 }
 .poke-options-button::before {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  left: 50%;
-  top: 50%;
-  height: 200px;
-  width: 200px;
-  border-radius: 50%;
-  background-color: #94acbd;
-  transform: translate(-50%, -50%) scale(0);
-  transition: 0.2s ease-in-out;
+	content: '';
+	position: absolute;
+	z-index: -1;
+	left: 50%;
+	top: 50%;
+	height: 200px;
+	width: 200px;
+	border-radius: 50%;
+	background-color: #94acbd;
+	transform: translate(-50%, -50%) scale(0);
+	transition: 0.2s ease-in-out;
 }
 .poke-options-button:not(:last-child) {
-  margin-bottom: 3px;
+	margin-bottom: 3px;
 }
 .poke-options-button.selected {
-  background-color: #94acbd;
+	background-color: #94acbd;
 }
 .poke-options-button.error {
-  background-color: #ff8b62;
+	background-color: #ff8b62;
 }
 .poke-options-button.success {
-  background-color: #7bd55a;
+	background-color: #7bd55a;
 }
 
 .poke-buttons {
-  text-align: center;
+	text-align: center;
 }
 @media (min-width: 480px) {
-  .poke-buttons {
-    position: absolute;
-    top: 20px;
-    left: 100%;
-  }
-  .poke-buttons .button {
-    padding-left: calc(1em + 10px);
-    border-top-left-radius: 0;
-    border-bottom-left-radius: 0;
-    transform: translateX(-10px);
-  }
+	.poke-buttons {
+		position: absolute;
+		top: 20px;
+		left: 100%;
+	}
+	.poke-buttons .button {
+		padding-left: calc(1em + 10px);
+		border-top-left-radius: 0;
+		border-bottom-left-radius: 0;
+		transform: translateX(-10px);
+	}
 }
 .poke-buttons .button {
-  padding: 1em;
-  width: 110px;
-  height: 100px;
-  color: #fff;
+	padding: 1em;
+	width: 110px;
+	height: 100px;
+	color: #fff;
 }
 .poke-buttons .button[disabled] {
-  color: #fa9f9b;
-  opacity: 0.7;
-  cursor: default;
+	color: #fa9f9b;
+	opacity: 0.7;
+	cursor: default;
 }
 .poke-buttons .button:not([disabled]):hover {
-  transform: translateX(0);
+	transform: translateX(0);
 }
 .poke-buttons .button:not(:last-child) {
-  margin-bottom: 6px;
+	margin-bottom: 6px;
 }
 
 .poke-final {
-  text-align: center;
+	text-align: center;
 }
 .poke-final-score {
-  display: block;
-  position: relative;
-  margin-bottom: 1rem;
+	display: block;
+	position: relative;
+	margin-bottom: 1rem;
 }
 .poke-final-score::before {
-  content: "";
-  position: absolute;
-  z-index: -1;
-  top: 50%;
-  left: 50%;
-  height: 100px;
-  width: 100px;
-  border-radius: 50%;
-  border: solid 12px #f32c22;
-  transform: translate(-50%, -50%);
-  opacity: 0.3;
-  -webkit-animation: grow 2s infinite ease-in-out;
-          animation: grow 2s infinite ease-in-out;
+	content: '';
+	position: absolute;
+	z-index: -1;
+	top: 50%;
+	left: 50%;
+	height: 100px;
+	width: 100px;
+	border-radius: 50%;
+	border: solid 12px #f32c22;
+	transform: translate(-50%, -50%);
+	opacity: 0.3;
+	-webkit-animation: grow 2s infinite ease-in-out;
+	animation: grow 2s infinite ease-in-out;
 }
 .poke-final-score-number {
-  font-size: 8rem;
-  line-height: 0.4;
-  color: #fa9f9b;
+	font-size: 8rem;
+	line-height: 0.4;
+	color: #fa9f9b;
 }
 
 .poke-classic .poke-image-img {
-  -ms-interpolation-mode: nearest-neighbor;
-      image-rendering: -moz-crisp-edges;
-      image-rendering: pixelated;
+	-ms-interpolation-mode: nearest-neighbor;
+	image-rendering: -moz-crisp-edges;
+	image-rendering: pixelated;
 }
 
-.animate-section-enter-active, .animate-section-leave-active {
-  transition: 0.4s ease-in-out;
+.animate-section-enter-active,
+.animate-section-leave-active {
+	transition: 0.4s ease-in-out;
 }
-.animate-section-enter, .animate-section-leave-to {
-  opacity: 0;
+.animate-section-enter,
+.animate-section-leave-to {
+	opacity: 0;
 }
 .animate-section-enter .poke-final-score-number {
-  transform: translateY(-30px);
+	transform: translateY(-30px);
 }
 .animate-section-leave-active {
-  transform: translateX(-30%);
+	transform: translateX(-30%);
 }
 .animate-section-enter-active {
-  transition-delay: 0.1s;
-  position: absolute;
-  top: 0;
-  left: 50%;
-  transform: translateX(-50%);
+	transition-delay: 0.1s;
+	position: absolute;
+	top: 0;
+	left: 50%;
+	transform: translateX(-50%);
 }
 
 .animate-options-enter-active {
-  transition: 0.4s ease-in-out;
+	transition: 0.4s ease-in-out;
 }
 .animate-options-enter-active:nth-child(4) {
-  transition-delay: 0s;
+	transition-delay: 0s;
 }
 .animate-options-enter-active:nth-child(5) {
-  transition-delay: 0.2s;
+	transition-delay: 0.2s;
 }
 .animate-options-enter-active:nth-child(6) {
-  transition-delay: 0.4s;
+	transition-delay: 0.4s;
 }
 .animate-options-enter-active:nth-child(7) {
-  transition-delay: 0.6s;
+	transition-delay: 0.6s;
 }
 .animate-options-enter-active:nth-child(8) {
-  transition-delay: 0.8s;
+	transition-delay: 0.8s;
 }
 .animate-options-enter {
-  transform: rotateX(-45deg);
-  transform-origin: top center;
-  opacity: 0;
+	transform: rotateX(-45deg);
+	transform-origin: top center;
+	opacity: 0;
 }
 .animate-options-leave-active {
-  position: absolute;
-  z-index: -1;
-  transition: 0.8s ease-in-out;
+	position: absolute;
+	z-index: -1;
+	transition: 0.8s ease-in-out;
 }
-.animate-options-leave-active[data-index="0"] {
-  top: 0;
+.animate-options-leave-active[data-index='0'] {
+	top: 0;
 }
-.animate-options-leave-active[data-index="1"] {
-  top: 51px;
+.animate-options-leave-active[data-index='1'] {
+	top: 51px;
 }
-.animate-options-leave-active[data-index="2"] {
-  top: 102px;
+.animate-options-leave-active[data-index='2'] {
+	top: 102px;
 }
-.animate-options-leave-active[data-index="3"] {
-  top: 153px;
+.animate-options-leave-active[data-index='3'] {
+	top: 153px;
 }
 .animate-options-leave-to {
-  opacity: 0;
+	opacity: 0;
 }
 
 @-webkit-keyframes grow {
-  0%, 100% {
-    transform: translate(-50%, -50%) scale(1);
-  }
-  50% {
-    transform: translate(-50%, -50%) scale(0.6);
-  }
+	0%,
+	100% {
+		transform: translate(-50%, -50%) scale(1);
+	}
+	50% {
+		transform: translate(-50%, -50%) scale(0.6);
+	}
 }
 
 @keyframes grow {
-  0%, 100% {
-    transform: translate(-50%, -50%) scale(1);
-  }
-  50% {
-    transform: translate(-50%, -50%) scale(0.6);
-  }
-}
\ No newline at end of file
+	0%,
+	100% {
+		transform: translate(-50%, -50%) scale(1);
+	}
+	50% {
+		transform: translate(-50%, -50%) scale(0.6);
+	}
+}
diff --git a/src/routes/wip/+page.server.ts b/src/routes/wip/+page.server.ts
index 83cf5a1be15dd4f60dcae45fea7264b47d269f42..588151c905c0636630e7513be7e03724bd3e5473 100644
--- a/src/routes/wip/+page.server.ts
+++ b/src/routes/wip/+page.server.ts
@@ -1,43 +1,124 @@
 import { superValidate } from 'sveltekit-superforms';
 import { zod } from 'sveltekit-superforms/adapters';
-import { schema } from './schema';
-import { fail } from '@sveltejs/kit';
+import { gameStateSchema, schema } from './schema';
+import type { GameState } from './schema';
+import { fail, redirect, type Cookies } from '@sveltejs/kit';
+import { GetPromotionStore, UserDetailsStore } from '$houdini';
+import { pageIterator } from '$lib/graphql/query';
+import { getRandomItems } from '$lib/utils';
+import Cryptr from 'cryptr';
 
 type Option = {
 	value: string;
 	label: string;
 };
 
-export async function load() {
-	const form = await superValidate(zod(schema));
+const cryptr = new Cryptr('myTotallySecretKey');
+
+function loadGameState(cookies: Cookies): GameState | null {
+	const cookie = cookies.get('qui-est-ce');
+	if (!cookie) return null;
 
-	const options: Option[] = [
-		{ value: 'bassu', label: 'Bassu' },
-		{ value: 'kiwi', label: 'Kiwi' },
-		{ value: 'isaiah', label: 'Isaiah' },
-		{ value: 'lorocas', label: 'Lorocas' }
-	];
+	const decryptedString = cryptr.decrypt(cookie);
+	const gameState = gameStateSchema.safeParse(JSON.parse(decryptedString));
 
-	return { form, options };
+	if (!gameState.success) return null;
+	return gameState.data;
+}
+
+function saveGameState(gameState: GameState, cookies: Cookies) {
+	const encryptedString = cryptr.encrypt(JSON.stringify(gameState));
+	cookies.set('qui-est-ce', encryptedString, { path: '/' });
+}
+
+export async function load(event) {
+	let gameState = loadGameState(event.cookies);
+
+	if (gameState === null) {
+		gameState = {
+			state: 'next',
+			history: [],
+			step: 0,
+			points: 0,
+			options: [],
+			solution: ''
+		};
+	}
+
+	if (gameState.state === 'next') {
+		const pagination = pageIterator(event, GetPromotionStore, { promotion: 2023 });
+
+		const promotion = await Array.fromAsync(pagination);
+		const all = new Set(promotion.map((p) => p.id));
+
+		const previous = new Set([]);
+		const available = all.difference(previous);
+
+		gameState.options = getRandomItems(Array.from(available), 4);
+		gameState.solution = getRandomItems([...gameState.options], 1)[0];
+		gameState.state = 'playing';
+		gameState.step++;
+	}
+
+	const details = await new UserDetailsStore().fetch({
+		event,
+		variables: { uuidList: gameState.options }
+	});
+	const users = details.data?.page.nodes ?? [];
+
+	const options: Option[] = users.map((node) => ({ label: node.nickname!, value: node.id })) ?? [];
+
+	console.log(gameState);
+
+	saveGameState(gameState, event.cookies);
+
+	const photo = users.find((node) => node.id === gameState.solution)?.photo!;
+
+	const form = await superValidate(zod(schema));
+
+	return { form, options, score: gameState.points, step: gameState.step, photo };
 }
 
 export const actions = {
-	async results({ request }) {
-		const form = await superValidate(request, zod(schema));
+	async results(event) {
+		const form = await superValidate(event, zod(schema));
 		console.log(form);
 
 		if (!form.valid) {
 			return fail(400, { form });
 		}
 
+		let gameState = loadGameState(event.cookies);
+
+		if (gameState === null) {
+			return redirect(307, '/wip');
+		}
+
+		if (form.data.choice === gameState.solution) {
+			gameState.points += 10;
+		}
+		gameState.history.push(gameState.solution);
+
+		saveGameState(gameState, event.cookies);
+
 		return {
 			form,
-			solution: 'bassu'
+			solution: gameState.solution
 		};
 	},
 
-	async next() {
+	async next(event) {
 		const form = await superValidate(zod(schema));
+
+		let gameState = loadGameState(event.cookies);
+
+		if (gameState === null) {
+			return redirect(307, '/wip');
+		}
+
+		gameState.state = 'next';
+		saveGameState(gameState, event.cookies);
+
 		return { form };
 	}
 };
diff --git a/src/routes/wip/+page.svelte b/src/routes/wip/+page.svelte
index eb338f293c635a56988ef2c227ba12e183e1671d..edd92b7c9084b65d3af12c3cd8b4538bbfb61f10 100644
--- a/src/routes/wip/+page.svelte
+++ b/src/routes/wip/+page.svelte
@@ -12,9 +12,7 @@
 
 	const { form: formData, enhance } = form2;
 
-	const question = 1;
 	const questionAmount = 10;
-	const score = 35;
 </script>
 
 <form method="post" use:enhance>
@@ -23,19 +21,19 @@
 			<h1 class="absolute top-[-2rem] text-xl">Qui est-ce ?</h1>
 			<div class="relative w-[250px]">
 				<span class="absolute right-[calc(100%_+_0.5rem)] flex flex-col items-end">
-					<span class="text-9xl leading-[0.4] text-[#fa9f9b]"> {question} </span>
+					<span class="text-9xl leading-[0.4] text-[#fa9f9b]"> {data.step} </span>
 					<span> / {questionAmount} </span>
 				</span>
 				<span
 					class="absolute right-[calc(100%_+_0.5rem)] top-24 whitespace-nowrap pt-4 text-xl text-[#333] before:absolute before:right-0 before:top-0 before:h-1.5 before:w-10 before:bg-[#333] before:content-['']"
 				>
-					{score}
+					{data.score}
 					<small>pts</small>
 				</span>
 				<div
 					class="relative z-[2] flex h-[250px] w-[250px] items-center justify-center overflow-hidden rounded-2xl border-6 border-[#333] border-[solid] bg-white before:absolute before:z-[-1] before:h-[100px] before:w-[100px] before:rounded-[50%] before:bg-[#c5d5ee] before:opacity-100 before:transition-[0.65s] before:duration-[ease-in-out] before:content-[''] after:absolute after:z-[-1] after:h-[100px] after:w-[100px] after:scale-0 after:rounded-[50%] after:border-[#c5d5ee] after:border-[solid] after:transition-[0.4s] after:duration-[ease-in-out] after:content-['']"
 				>
-					<img src={'/bassu.jpg'} alt="Pas de triche" class="w-auto" />
+					<img src={data.photo} alt="Pas de triche" class="w-auto" />
 				</div>
 				<Fieldset form={form2} name="choice">
 					<transition-group
@@ -58,7 +56,7 @@
 									<Label
 										tabindex={0}
 										data-solution={form?.solution || null}
-										class="focus:border-[#888] relative block cursor-pointer select-none overflow-hidden rounded-2xl border-6 border-[#333] border-[solid] bg-[#c5d5ee] p-[0.5em] text-center text-lg transition-[0.45s] [font-family:'Londrina_Solid',monospace] before:absolute before:left-2/4 before:top-2/4 before:z-[-1] before:h-[200px] before:w-[200px] before:-translate-x-2/4 before:-translate-y-2/4 before:scale-0 before:rounded-[50%] before:bg-[#94acbd] before:transition-[0.2s] before:duration-[ease-in-out] before:content-[''] hover:translate-y-[-3px] hover:bg-[#a9c1e6] focus:[outline:none] active:before:-translate-x-2/4 active:before:-translate-y-2/4 active:before:scale-100 peer-checked:bg-[#94acbd] data-[solution]:cursor-default data-[solution]:text-[#94acbd] peer-checked:data-[solution]:bg-[#ff8b62] peer-checked:data-[solution]:text-inherit peer-data-[valid]:!bg-[#7bd55a] peer-data-[valid]:text-inherit"
+										class="relative block cursor-pointer select-none overflow-hidden rounded-2xl border-6 border-[#333] border-[solid] bg-[#c5d5ee] p-[0.5em] text-center text-lg transition-[0.45s] [font-family:'Londrina_Solid',monospace] before:absolute before:left-2/4 before:top-2/4 before:z-[-1] before:h-[200px] before:w-[200px] before:-translate-x-2/4 before:-translate-y-2/4 before:scale-0 before:rounded-[50%] before:bg-[#94acbd] before:transition-[0.2s] before:duration-[ease-in-out] before:content-[''] hover:translate-y-[-3px] hover:bg-[#a9c1e6] focus:border-[#888] focus:[outline:none] active:before:-translate-x-2/4 active:before:-translate-y-2/4 active:before:scale-100 peer-checked:bg-[#94acbd] data-[solution]:cursor-default data-[solution]:text-[#94acbd] peer-checked:data-[solution]:bg-[#ff8b62] peer-checked:data-[solution]:text-inherit peer-data-[valid]:!bg-[#7bd55a] peer-data-[valid]:text-inherit"
 									>
 										{option.label}
 									</Label>
diff --git a/src/routes/wip/schema.ts b/src/routes/wip/schema.ts
index f551c4aebbe85bfa8eb3e6bcb2319432152ff2c2..8f04be14e2b62c9d9993694b67063b1c28a0eb9c 100644
--- a/src/routes/wip/schema.ts
+++ b/src/routes/wip/schema.ts
@@ -1,5 +1,16 @@
 import { z } from 'zod';
 
 export const schema = z.object({
-	choice: z.string(),
+	choice: z.string()
 });
+
+export const gameStateSchema = z.object({
+	history: z.array(z.string()),
+	step: z.number(),
+	points: z.number(),
+	options: z.array(z.string()),
+	state: z.enum(['playing', 'solution', 'next']),
+	solution: z.string()
+});
+
+export type GameState = z.infer<typeof gameStateSchema>;
diff --git a/svelte.config.js b/svelte.config.js
index 4a82086e244a6077387aabcca1636d349cb0c159..5ed5ae93b8cbecc1d49d446c45a67af6f54d0d15 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -8,10 +8,10 @@ const config = {
 	preprocess: vitePreprocess(),
 
 	kit: {
-		// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
-		// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
-		// See https://kit.svelte.dev/docs/adapters for more information about adapters.
-		adapter: adapter()
+		adapter: adapter(),
+		alias: {
+			$houdini: './$houdini'
+		}
 	}
 };
 
diff --git a/tsconfig.json b/tsconfig.json
index fc93cbd9409f9809256c4c763bf205bba17f0a44..bba4bd67ac935c7f0b3f8a0a46c03e2d4496f02d 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,11 +9,7 @@
 		"skipLibCheck": true,
 		"sourceMap": true,
 		"strict": true,
-		"moduleResolution": "bundler"
+		"moduleResolution": "bundler",
+		"rootDirs": [".", "./.svelte-kit/types", "./$houdini/types"]
 	}
-	// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
-	// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
-	//
-	// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
-	// from the referenced tsconfig.json - TypeScript does not merge them in
 }
diff --git a/vite.config.ts b/vite.config.ts
index bbf8c7da43f0080dc6b9fb275f9583b7c17f1506..521b2406b49542745d6bcbba0974478173c82875 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,7 @@
 import { sveltekit } from '@sveltejs/kit/vite';
+import houdini from 'houdini/vite';
 import { defineConfig } from 'vite';
 
 export default defineConfig({
-	plugins: [sveltekit()]
+	plugins: [houdini(), sveltekit()]
 });