RetourOrévia
A0-03Le build web et le site
Jouer
ACTE 0 · CHAPITRE 3

Le build web et le site

Ce jeu se joue au clavier ou à la manette, sur un ordinateur

Cliquez dans le jeu pour prendre la main

Le jeu ne se charge pas ? Ouvrir le build seul

Ce qui est nouveau

Le jeu est sur le web. Il est vide, c'est une scène avec un ciel, mais on peut l'ouvrir dans un navigateur. La version 0, la démo d'origine, tourne aussi dans le navigateur. Et ce site existe : un article par chapitre, avec sa démo jouable en haut.

Ce qui a été fait

Ce qui cloche encore

Le site tourne seulement sur mon ordinateur pour l'instant. Le nom de domaine et le serveur viendront plus tard ; la page ne changera pas. La version 0 a toujours ses bugs : c'est voulu, elle sert de point de comparaison.

Comment c'est fait, et pourquoi

Pourquoi publier un jeu vide

Un jeu qu'on ne peut pas essayer n'existe pas. Alors avant d'écrire la moindre ligne de code de jeu, on fabrique la commande qui met le jeu sur le web. Si elle casse un jour, on le saura le soir même, pas six mois plus tard. Le premier jet a mis un mois à sortir en version web, parce que rien n'avait été prévu pour.

La commande de build

Unity sait fabriquer une version web, mais il faut cliquer dans des menus. On veut une seule commande. Elle lance Unity sans interface, construit le jeu, et dépose le résultat dans un dossier au nom du chapitre. Elle vérifie aussi que la page du chapitre existe. Pas de page, pas de vedette sur le site.

PROMPT
Où on en est : C:\Atelier\Jeu est un projet Unity 6 avec URP, vide, sous git. Le
module WebGL est installé. C'est un jeu vidéo en 3D construit chapitre par chapitre ;
chaque chapitre a un identifiant comme A0-03 et un titre dans
C:\Atelier\FeuilleDeRoute\feuille-de-route.json (actes[].chapitres[] avec id et
titre). Chaque chapitre se termine par un build web déposé dans
C:\Atelier\Versions\<identifiant>\, à côté des précédents, avec une lettre
Versions\<identifiant>\lettre.md que le site public lit. La lettre est écrite avant
le tournage dans C:\Atelier\FeuilleDeRoute\pages\<identifiant>.md.

Ce que je veux : un script d'éditeur Unity, Assets/Jeu/Editeur/Construction.cs, avec
une classe statique Construction et une méthode Web qui construit le projet pour WebGL
dans le dossier passé par l'argument de ligne de commande -dossier, avec les scènes
cochées dans les réglages de build, compression gzip et repli de décompression
activé. Et un script PowerShell à la racine du projet, construire-web.ps1, qui prend
un identifiant, trouve l'exécutable de Unity d'après ProjectSettings/ProjectVersion.txt,
lance Unity en mode batch sans interface avec cette méthode, écrit le journal dans
C:\Atelier\Versions\<identifiant>.log, affiche la durée et la taille à la fin, et
s'arrête avec une erreur lisible si Unity échoue ou si le projet est ouvert. Avant le
build, si Versions\<identifiant>\lettre.md n'existe pas, il la crée en copiant
FeuilleDeRoute\pages\<identifiant>.md s'il existe, sinon un gabarit vide avec les
sections « Ce qui est nouveau », « Ce qui a été fait », « Ce qui cloche encore »,
« Comment c'est fait, et pourquoi », et etat à a_completer. Il ne touche jamais à une
lettre existante.
Une option -Developpement fait un build de développement, où le pupitre des scénarios d'essai existe, dans le même dossier ; le script le dit à la fin et rappelle de relancer sans l'option avant de publier.

Plus tard : le script sera appelé par un skill de clôture d'épisode ; il doit pouvoir
être relancé sur le même identifiant sans rien demander.

Ce que je ne veux pas : pas de build Windows, rien dans Versions à part le build, son
journal et sa lettre.

Quand c'est fait, lance .\construire-web.ps1 A0-03 et montre-moi la sortie, le dossier
C:\Atelier\Versions\A0-03 et la lettre.

Le build prend deux ou trois minutes la première fois. Unity traduit tout le jeu en WebAssembly, un langage que le navigateur sait exécuter vite.

Le build de Solea

La version 0 n'avait jamais été prévue pour le web. La faire tourner dans un navigateur, c'est régler quatre pièges d'un coup : le son, qui n'est compilé que si on le demande ; les shaders, que le web ne trouve pas s'ils ne sont pas inclus ; un compilateur qui tombe en panne sous OneDrive ; et un cache qui garde parfois une vieille version. Le menu les règle tous, dans l'ordre, et écrit un petit compte rendu.

PROMPT
Où on en est : C:\Users\neobo\OneDrive - Studio\Unity\Orevia\Greybox est le projet
Unity 6 (URP) de la version 0 d'Orévia, le premier jet : la scène Solea, FMOD pour le
son, des contenus chargés par Addressables, Unity Localization pour les textes. Il est
sous git dans le dossier Orevia au-dessus. Le module WebGL est installé, OneDrive est
en pause, Unity est ouvert sur le projet et le MCP Unity est branché dessus. Les
scripts d'éditeur vivent dans Assets/Orevia/Scripts/Editor, sous le menu « Orevia ».
Il existe peut-être déjà un script de construction web dans ce dossier : si oui,
relis-le, garde-le, et complète seulement ce qui manque dans la liste qui suit. Le
build doit arriver dans C:\Atelier\Versions\version-0 ; sa lettre.md existe déjà, ne
la touche pas.

Ce que je veux : un menu « Orevia/Build/Construire la version 0 (WebGL) » dans
Assets/Orevia/Scripts/Editor/ConstructionWeb.cs, espace de noms Orevia.Construction
(pas Orevia.Editor : il entre en collision avec UnityEditor.Editor), qui fait tout
dans l'ordre et sans question :
- enregistrer les assets ;
- définir le symbole de compilation OREVIA_FMOD pour la cible WebGL, avec
  NamedBuildTarget.WebGL : le pont FMOD n'est compilé que sous ce symbole, et sans lui
  le jeu sort muet ;
- ajouter aux Always Included Shaders des réglages graphiques les shaders que le code
  cherche par nom à l'exécution, Orevia/UI/Carte et Universal Render Pipeline/Unlit :
  sur le web, Shader.Find rend null pour tout shader qui n'est pas inclus, et la carte
  du niveau laisse alors le terrain sur la mauvaise couche ;
- désactiver la compilation Burst pour ce build : sous OneDrive, le compilateur Burst
  échoue avec le code 4551 ;
- construire le contenu Addressables avant le joueur ;
- construire le joueur WebGL avec les scènes cochées, compression gzip et repli de
  décompression, dans C:\Atelier\Versions\version-0 ;
- écrire construction.txt à côté, avec la date, la durée et la taille.
Un second menu « Orevia/Build/Construire la version 0 (WebGL, reconstruction
complète) » fait la même chose avec BuildOptions.CleanBuildCache, pour le jour où le
cache laisse un wasm périmé. Si le build échoue sur « scripts have compile errors »
juste après une « Sharing violation » dans le journal de l'éditeur, c'est OneDrive
qui a touché un fichier de Library : il suffit de recompiler les scripts et de
relancer, dis-le dans le message d'erreur.

Plus tard : ce menu ne sert qu'à la version 0 ; le nouveau projet a sa propre commande
en ligne de commande.

Ce que je ne veux pas : aucune modification de la scène ni du gameplay, pas de build
Windows, pas de script PowerShell : c'est un menu d'éditeur, parce que Unity doit
rester ouvert avec le MCP.

Quand c'est fait, lance le menu par le MCP. Si l'appel expire, c'est normal : le build
dure cinq à dix minutes ; attends la fin en relisant construction.txt. Puis montre-moi
construction.txt, le contenu de C:\Atelier\Versions\version-0 et la taille du dossier
Build.

Le build de Solea dure cinq à dix minutes. Quand l'IA dit que l'appel a expiré, ce n'est pas une panne : Unity travaille encore.

Le site

Le site vit à part : un autre dossier, un autre conteneur, un autre port. Il ne sait rien du jeu. Il lit des pages écrites en Markdown, une par chapitre, et il les affiche. Quand un chapitre est publié, un dossier et une page de plus, et le site se met à jour tout seul. Les données d'un côté, ce qui les affiche de l'autre : c'est la même idée que pour le jeu.

Le site est fait de briques. Un cadre dessiné, une étiquette déchirée, un bouton, une carte de chapitre : chaque brique est une petite fonction qu'on réutilise partout. Une page ne fait qu'assembler des briques. Et il n'y a aucune image dans le design : les couleurs, le trait tremblé, l'île et la mer sont décrits dans le prompt, en mots et en chiffres. C'est voulu. Si vous copiez ces prompts chez vous, vous obtenez le même site.

Le site se fait en quatre prompts, un par écran, parce qu'un prompt qui demande tout d'un coup finit toujours par oublier quelque chose. Chaque prompt commence par dire où on en est, et demande à l'IA de relire le code existant avant d'écrire.

L'accueil

PROMPT
Où on en est : C:\Atelier\Site est vide. C:\Atelier\Versions contient un dossier par
version web du jeu, chacun avec un index.html de build Unity WebGL compressé en gzip
(dossier Build à côté) et une lettre.md : un en-tête YAML entre deux lignes « --- »
(id, titre, video, etat qui vaut a_completer ou publiee, et parfois d'autres champs
qu'il faut tolérer), puis des sections Markdown de niveau « ## » : « Ce qui est
nouveau », « Ce qui a été fait », « Ce qui cloche encore », « Comment c'est fait, et
pourquoi », « Ce qui a mordu », « Sources ». Les identifiants sont de la forme A0-03 :
acte 0, chapitre 3. La version d'origine s'appelle version-0. Le jeu s'appelle
Orévia ; c'est un RPG en 3D que je refais chapitre par chapitre avec une IA, en vidéo,
et le site raconte ça. Un Docker tourne déjà sur le port 8091 : ne pas y toucher. Tu
n'as aucune image, aucune maquette, aucun fichier de référence : tout le design est
décrit dans ce prompt, et tout se fait en CSS, SVG et JavaScript. Quelqu'un qui copie
ce prompt chez lui doit obtenir le même site que moi.

Ce que je veux, pour ce premier prompt : le Docker, l'organisation du code, et
l'accueil complet. Les autres pages viendront par d'autres prompts, dans la même
organisation.

1. L'infrastructure. Un docker-compose.yml avec l'image php:8.4-apache, le port 8092,
Site/www comme racine web, et C:\Atelier\Versions monté en lecture seule sous
/var/www/html/versions. Un .htaccess qui active la réécriture, sert un build Unity
WebGL gzip (Content-Encoding gzip pour les .gz ; types application/wasm pour
.wasm.gz, application/javascript pour .js.gz, application/octet-stream pour .data.gz)
et envoie les adresses propres vers index.php, sauf les fichiers qui existent.
Markdown rendu par Parsedown, un seul fichier déposé dans www/lib. Pas d'autre
dépendance que la police Google Fonts.

2. L'organisation du code, pour que tout soit réutilisable. Le principe : chaque
morceau visible du site est un composant, une fonction PHP dans un fichier à son nom
sous www/composants, qui reçoit un tableau associatif de paramètres et retourne une
chaîne HTML, sans jamais rien afficher directement. Les pages ne font qu'assembler
des composants. Ajouter un composant, c'est ajouter un fichier. Précisément :
- www/index.php : le routeur, sans framework. Pour l'instant une route, / ; une
  adresse inconnue répond 404 avec la même mise en page, un titre « Page perdue en
  mer » et un bouton vers l'accueil.
- www/lib/versions.php : la couche de données. Elle lit les dossiers de
  /var/www/html/versions qui contiennent un index.html, parse la lettre.md (en-tête
  YAML à plat, clé: valeur, champs inconnus gardés tels quels dans le tableau), coupe
  le corps en sections par titre « ## », et expose : la liste des versions triée du
  plus récent au plus ancien par acte puis numéro de chapitre, version-0 toujours en
  dernier ; la dernière version publiée (etat publiee, hors version-0) ; le résumé
  d'une version, c'est-à-dire le premier paragraphe de « Ce qui est nouveau », coupé
  proprement à trois lignes d'environ 90 caractères ; l'adresse de jeu d'une version,
  /versions/<id>/index.html pour l'instant.
- www/gabarits/page.php : la mise en page commune, qui reçoit le titre, la description
  et le contenu : doctype, lang fr, meta viewport, title « <page> · Orévia », meta
  description, la police, la feuille www/css/site.css, le bloc SVG des filtres (voir
  le point 4) juste après l'ouverture du body, le contenu, le pied de page, et le
  script www/js/site.js en fin de body.
- www/composants/ : un fichier par composant, avec leurs paramètres : cadre (contenu
  HTML, fond, épaisseur de trait, ombre, inclinaison en degrés, classes en plus) ;
  etiquette (texte, fond, couleur du texte, rotation) ; touche (adresse, sur-titre,
  titre, variante laiton ou magenta) ; carte_chapitre (id, titre, ligne d'acte,
  résumé, liens jouer/vidéo/lire, dernier oui/non, sombre oui/non) ; diorama (rien) ;
  marque (rien) ; pied (rien) ; bouton_simple (texte, adresse). Chaque fichier de
  composant commence par un commentaire de trois lignes : à quoi il sert, ses
  paramètres, un exemple d'appel.
- www/pages/ : accueil.php et erreur.php. Chacune assemble des composants et rien
  d'autre.
- www/css/site.css : une seule feuille, écrite à la main, sans préprocesseur. Les
  variables en tête, puis une section par composant, chaque section précédée d'un
  commentaire au nom du composant. Aucun style en ligne dans le HTML, sauf les
  positions des pièces du diorama (nuages, vagues, barque) et les variables CSS
  passées par un composant.
- www/js/site.js : le seul JavaScript du site. Pour l'instant, les bords déchirés.
- www/contenu/accueil.md : l'en-tête YAML porte etiquette (« CRÉER UN RPG »), titre
  (« Je refais mon RPG avec une IA et sans rien cacher »), sous_titre (« Chaque
  épisode est une vidéo qui finit par un build jouable. Et tous les prompts sont
  prêts à copier. ») et bandeau (« UN SYSTÈME PAR SOIRÉE »). L'accueil les lit là.
- www/images/ : les captures du jeu, c'est tout. Le design n'utilise aucune image.

3. Les variables du design, déclarées sur :root et utilisées partout, jamais une
couleur en dur ailleurs que dans le diorama : --encre #15161F ; --papier #EFE4C8 ;
--papier-sombre #E2D3AE ; --laiton #D1A445 ; --laiton-fonce #9A6B22 ; --nuit
#1E2B4D ; --ciel #4FA0D6 ; --ciel-clair #BFE0F2 ; --patine #3E8C86 ; --magenta
#D8457E ; --titre « Luckiest Guy », avec repli Impact puis cursive ; --texte « Barlow
Condensed », avec repli Arial Narrow puis sans-serif. Google Fonts : Luckiest Guy, et
Barlow Condensed en 400, 600, 700, 800. Le body : marge 0, fond encre, texte papier,
police texte 19px, interligne 1.45. Tout en box-sizing border-box. Les liens héritent
la couleur.

4. Les deux filtres SVG, définis une fois dans un <svg width="0" height="0"
style="position:absolute"> en tête du body. Filtre crayon : feTurbulence type
fractalNoise, baseFrequency 0.035, numOctaves 2, seed 3, puis feDisplacementMap sur
SourceGraphic avec scale 6. Filtre crayon2 : baseFrequency 0.06, numOctaves 2, seed 9,
scale 4. Ils donnent le trait tremblé d'un dessin à la main.

5. Le cadre dessiné, la classe dessin, la brique de tout le site. L'élément est en
position relative avec isolation isolate. Son ::before, en position absolue inset 0
et z-index -1, porte le fond var(--fond, papier), une bordure var(--trait, 4px) solid
encre, une ombre portée var(--ombre, 9px 9px 0 nuit) sans flou, et le filtre crayon.
Son ::after, mêmes inset et z-index, n'a qu'une bordure de 1.5px encre, décalée de
translate(3px, -2px) rotate(-0.5deg), opacité 0.7, filtre crayon2 : c'est le trait
repassé une seconde fois. Chaque usage règle --fond, --trait et --ombre. Pour un cadre
rond, les deux pseudo-éléments prennent border-radius 50%. Aucun dégradé dans un
cadre, aucune trame de points, jamais.

6. L'étiquette en papier déchiré, la classe dechire. Inline-block, position relative,
isolation isolate. Son ::before, inset -5px -9px, z-index -1, fond var(--fond, nuit),
clip-path var(--bord), filtre crayon2 suivi d'un drop-shadow(3px 3px 0 encre). Le
JavaScript calcule --bord pour chaque élément dechire de la page, dans l'ordre, avec
la graine 17 + 31 × index : un générateur congruentiel s = (s × 9301 + 49297) mod
233280, aléa = s / 233280 ; une phase = aléa × 6 ; le bord haut parcourt x de 0 à 100
par pas de 1.5 + aléa × 2.5, avec y = 9 + 7 × sin(x / 9 + phase) + aléa × 10 ; le
bord bas, même parcours, y = 100 - (9 + 7 × sin(x / 7 + 2 × phase)) - aléa × 10 ; on
ferme par le point 100% 10% en haut, 100% 88% en bas, trois points sur le côté droit
(97 + aléa × 3 % à 30 %, 95 + aléa × 4 % à 55 %, 97 + aléa × 3 % à 78 %) et trois sur
le côté gauche (aléa × 3 % à 70 %, 1 + aléa × 4 % à 45 %, aléa × 3 % à 22 %) ; le
polygone va haut, droite, bas à l'envers, gauche. Sans JavaScript, l'étiquette reste
un rectangle : acceptable.

7. Le diorama, en haut de l'accueil : un bloc ciel en position relative, overflow
hidden, isolation isolate, dont le fond empile quatre dégradés, du dessus au dessous :
radial-gradient(ellipse 60% 40% at 70% 85%, rgba(209,164,69,.55), transparent 70%) ;
radial-gradient(ellipse 80% 50% at 20% 20%, rgba(255,255,255,.55), transparent 60%) ;
radial-gradient(ellipse 50% 30% at 85% 35%, rgba(255,255,255,.35), transparent 60%) ;
linear-gradient(180deg, #2E6FB0 0%, ciel 35%, ciel-clair 70%, #E9D9B4 100%). Toutes
les pièces qui suivent sont en position absolue, z-index 0, et l'en-tête qui vient
par-dessus est en z-index 1.
- Le soleil : un cadre dessin rond de 150px, fond laiton, trait 4px, ombre 6px 6px 0
  laiton-fonce, à right 14% et top 7%. Six rayons : des barres de 260 × 5px, fond
  laiton, opacité 0.55, centrées sur le soleil, tournées de 0, 30, 60, 90, 120 et 150
  degrés, filtre crayon2.
- Trois nuages : des pilules blanches de 34px de haut, border-radius 40px, opacité
  0.9, filtre crayon, ombre 4px 4px 0 rgba(30,43,77,.18), avec une bosse ::before
  ronde (left 22%, top -55%, width 45%, height 130%). Positions : left 8% top 14%
  largeur 170px ; left 42% top 9% largeur 120px opacité 0.75 ; left 68% top 30%
  largeur 210px.
- La mer lointaine : une bande de left -2% à right -2%, bottom 12%, hauteur 10%,
  dégradé vertical de #9AD6F0 à #5FAEDC, filtre crayon2. Deux petites vagues blanches
  dessus : left 3% bottom 18.5% largeur 40px, et left 94% bottom 17% largeur 36px.
- Un îlot au loin : left 94%, largeur 64px, bottom 15%, hauteur 4%, fond nuit,
  clip-path polygon(0 100%, 22% 40%, 48% 0, 72% 50%, 100% 100%), filtre crayon2.
- L'île : trois plans de montagnes en papier découpé, de left 0 à right 0, bottom
  13%, filtre crayon2, qui ne touchent jamais les bords pour qu'on voie l'eau de
  chaque côté. Plan 1, le plus loin : hauteur 24%, fond #6FB3D9, opacité 0.9,
  clip-path polygon(46% 100%, 50% 60%, 56% 44%, 62% 56%, 68% 30%, 74% 46%, 80% 36%,
  86% 58%, 90% 100%). Plan 2 : hauteur 22%, fond patine, polygon(10% 100%, 14% 62%,
  20% 44%, 27% 56%, 33% 30%, 40% 48%, 46% 38%, 54% 60%, 61% 46%, 69% 66%, 76% 100%).
  Plan 3, le plus proche : hauteur 12%, fond nuit, polygon(8% 100%, 14% 80%, 22% 56%,
  30% 70%, 38% 42%, 47% 64%, 55% 38%, 64% 60%, 72% 50%, 80% 72%, 88% 84%, 92% 100%).
- La mer proche, par-dessus les montagnes : left -2% à right -2%, bottom -8px,
  hauteur 15%, dégradé vertical #3A86C6 0%, #26568A 40%, nuit 72%, encre 100%, filtre
  crayon2. Elle finit dans la couleur du fond de page : aucune coupure entre le ciel
  et la suite. Sept vagues dessus, des arcs de 12px de haut avec une bordure haute de
  3px en rgba(239,228,200,.9), border-radius 50% 50% 0 0 / 100% 100% 0 0, filtre
  crayon2 : left 5% bottom 11% largeur 80px ; 31% 10% 60px ; 58% 11.5% 90px ; 84% 10%
  70px ; 14% 6.5% 110px ; 45% 7% 70px ; 72% 6% 120px.
- Une barque : un cadre dessin de 46 × 14px, fond laiton-fonce, trait 3px, sans ombre,
  coins du bas arrondis de 14px, à left 22% bottom 8%, tournée de -3deg, avec une
  voile : un triangle de 15px de base et 32px de haut en papier, bord gauche vertical,
  cerné d'encre par drop-shadow, posé à left 16px et bottom 12px de la coque.

8. L'en-tête de l'accueil, dans le ciel : largeur maximale 1100px centrée, padding
56px 24px 270px pour laisser l'île respirer sous les boutons.
- La marque : en flex, écart 18px, marge basse 44px. Le nom « Orévia » en police titre
  44px, couleur papier, contour -webkit-text-stroke 2px encre, ombre de texte 4px 4px
  0 nuit. À côté, l'étiquette déchirée fond laiton avec le texte de l'en-tête
  (« CRÉER UN RPG ») en texte 800 14px, espacement des lettres 0.4em, couleur encre,
  padding 4px 14px.
- L'accroche : un cadre dessin de 760px de large au plus, padding 26px 34px 20px,
  tourné de -1.6deg, trait 5px, ombre 12px 12px 0 nuit. Un bandeau déchiré posé sur
  son coin haut gauche (left 18px, top -18px, rotation -3deg) avec le bandeau en texte
  800 15px, espacement 0.3em, couleur laiton. Le h1 en police titre 52px, interligne
  1.02, couleur nuit, ombre de texte 4px 4px 0 magenta, contour 1px encre. Le
  sous-titre en texte 600 21px, interligne 1.3, couleur encre, marge haute 14px.
- Les deux touches : une grille de deux colonnes égales, écart 30px, marge haute
  52px, largeur maximale 980px. Chaque touche est un lien, cadre dessin en flex,
  centré verticalement, écart 16px, padding 18px 24px 16px 20px, fond laiton, texte
  encre, trait 4px, ombre « 0 10px 0 laiton-fonce, 0 10px 0 4px encre » (une épaisseur
  de touche de clavier), rotation -1.5deg, transition transform 0.12s. La variante
  magenta (la démo d'origine) : fond magenta, texte papier, ombre « 0 10px 0 #8E2550,
  0 10px 0 4px encre », rotation 1.2deg. Au survol, la touche s'enfonce :
  translateY(6px) rotate(0) et l'ombre passe à 4px. À gauche, un rond « lecture » de
  58px, cadre dessin rond fond papier, trait 3px, ombre 4px 4px 0 encre, avec un
  triangle SVG de 26px (path M5 3l16 9-16 9z) rempli encre, décalé de 4px à droite.
  Le texte en colonne : un sur-titre en texte 800 13px, espacement 0.3em, majuscules,
  opacité 0.85 (« Chapitre A0-04 · le contrat », « Version 0 · bugs compris ») et le
  titre en police titre 27px, interligne 1.05, ombre de texte 2px 2px 0
  rgba(21,22,31,.18) (« Tester la dernière version », « Tester la démo d'origine »).
  En coin haut droit, une étiquette déchirée fond papier (right -14px, top -22px,
  rotation 8deg) avec « JOUER » en texte 800 12px, espacement 0.3em, encre.

9. La frise des chapitres, sous le ciel : un main de 1100px au plus, padding 58px 24px
80px. Un titre de section en texte 800 15px, espacement 0.4em, majuscules, laiton,
marge basse 26px : « Les chapitres · du plus récent au plus ancien ». Une grille
repeat(auto-fill, minmax(300px, 1fr)), écart 34px 30px. Chaque carte est un lien vers
l'article (/article/<id>, la page viendra), cadre dessin, padding 26px 24px 20px, texte
encre, tournée de -0.8deg, de 0.9deg pour les cartes paires, de -1.3deg pour les
multiples de trois ; au survol, droite et remontée de 3px. En coin haut gauche (left
12px, top -16px, rotation -3deg) l'identifiant dans une étiquette déchirée laiton,
police titre 19px, encre ; magenta avec texte papier pour la dernière version publiée.
Puis la ligne d'acte en texte 800 12px, espacement 0.3em, majuscules, patine
(« Acte 0 · Les fondations » ; si la lettre a un champ acte, on l'utilise, sinon
« Acte N ») ; le titre en police titre 30px, interligne 1.05, encre, ombre de texte
3px 3px 0 ciel ; le résumé en texte 500 18px, interligne 1.35, couleur #2a2616 ; une
ligne de liens en texte 800 13px, espacement 0.25em, majuscules, magenta : Jouer (le
build), Vidéo si le champ video est rempli, Lire (l'article). La carte de version-0 a
le fond papier-sombre et son étiquette en laiton-fonce, avec « V0 » et la ligne « Le
premier jet ». Une version en a_completer apparaît avec une étiquette « PAGE À
VENIR » à la place du résumé, sans lien Lire, et n'est jamais choisie comme dernière
version.

10. Le pied de page, sur toutes les pages : centré, padding 40px 24px 60px, couleur
#9a9a9a, texte 500 16px : « Orévia · Créer un RPG ».

11. Téléphone d'abord. Sous 640px : le nom de la marque à 34px, le h1 de l'accueil à
38px, les touches sur une colonne, leur titre à 23px. Jamais de défilement
horizontal, jamais moins de 16px de marge latérale.

12. Les petites choses qui comptent : chaque bouton a un aria-label ; le focus clavier
est visible, contour 3px magenta décalé de 2px ; avec prefers-reduced-motion, pas de
transition ; chaque page a son title et sa meta description ; tous les textes du site
sont en français avec leurs accents ; aucun texte n'est en dur dans une page quand il
vient d'une lettre ou de accueil.md.

Plus tard : la page d'un chapitre, l'écran de jeu avec un bouton retour, un catalogue
des composants ; un champ pour laisser son mail, une page porte ouverte pour les
artistes, un texte sur ce que la télémétrie mesure ; le même Docker tournera sur un
serveur public. Un second thème ne touchera que les variables de :root. Le code
tolère des champs inconnus dans les en-têtes.

Ce que je ne veux pas : pas de base de données, pas de copie des builds dans Site, pas
de génération de fichiers, pas de framework, pas d'étape de build (ni npm, ni Sass),
pas de compte, pas d'image pour le design, pas de police d'icônes, pas de CDN autre
que Google Fonts, pas de trame de points, pas de dégradé dans un cadre, pas de
bordure gauche colorée, pas d'étoile ni d'animation qui tourne, pas de style en ligne
hors diorama.

Quand c'est fait : docker compose up -d, puis montre-moi http://localhost:8092. Je
dois voir le diorama, cliquer sur les deux boutons et jouer, voir version-0 et A0-03
dans la liste, et voir le tout tenir à la largeur d'un téléphone. Donne-moi aussi la
liste des fichiers créés, une ligne chacun.

La page d'un chapitre

PROMPT
Où on en est : le site tourne sur http://localhost:8092 avec son accueil, fait au
prompt précédent : un Docker php:8.4-apache, C:\Atelier\Versions monté en lecture
seule sous /var/www/html/versions, un routeur www/index.php, une couche de données
www/lib/versions.php qui lit les lettre.md, des composants PHP sous www/composants
(une fonction par fichier, qui retourne du HTML), une seule feuille www/css/site.css
avec les variables de couleur en tête, un seul script www/js/site.js. Les cartes de
l'accueil pointent déjà vers /article/<id>, qui n'existe pas encore. Commence par
lire www en entier et garde cette organisation : mêmes variables, mêmes classes
dessin et dechire, nouveaux composants dans de nouveaux fichiers. Tu n'as aucune image
ni maquette : tout est décrit ici.

Ce que je veux : la page d'un chapitre, /article/<id>, qui rend la lettre entière, sur
le fond encre de la page, dans une colonne de 860px au plus, marge 60px auto 0,
padding 0 24px 90px.

1. La couche de données gagne : les sections de la lettre dans l'ordre, chacune avec
son titre et son Markdown ; le précédent et le suivant d'un identifiant dans l'ordre
de la liste (du plus récent au plus ancien, version-0 en dernier) ; le libellé du
bandeau, « ACTE 0 · CHAPITRE 4 » déduit de l'identifiant, ou « LE PREMIER JET » pour
version-0. Un identifiant inconnu, ou une lettre en a_completer, donne la page 404.

2. Un fichier www/lib/markdown.php rend le corps avec Parsedown, puis remplace chaque
bloc de code <pre><code> par le composant prompt, texte échappé conservé. Les liens de
« Sources » s'ouvrent dans un nouvel onglet.

3. La navigation, un composant navigation_chapitres (précédent ou null, suivant ou
null), en haut et en bas de l'article : une grille 1fr auto 1fr, écart 16px, marge
basse 40px. Deux boutons, cadres dessin en flex, écart 14px, padding 12px 18px 10px,
trait 4px, transition 0.12s : le précédent à gauche, fond patine, texte papier, ombre
« 0 7px 0 #27605C, 0 7px 0 4px encre » ; le suivant à droite, fond laiton, texte
encre, ombre « 0 7px 0 laiton-fonce, 0 7px 0 4px encre », contenu inversé (flèche à
droite, texte aligné à droite). Au survol, translateY(4px) et ombre à 3px. La flèche :
un rond dessin de 46px, fond papier, trait 3px, ombre 3px 3px 0 encre, le caractère
← ou → en police titre 30px, couleur patine pour le précédent, encre pour le suivant.
À côté : le sens en texte 800 12px, espacement 0.3em, majuscules, opacité 0.8
(« Chapitre précédent · A0-03 ») et le titre du chapitre en police titre 22px, sur une
ligne, coupé par des points de suspension. Entre les deux, « Tous les chapitres »,
lien vers l'accueil en texte 800 12px, espacement 0.3em, majuscules, laiton, papier au
survol. Si le précédent ou le suivant n'existe pas, sa case reste vide et la grille
ne bouge pas.

4. Le titre, un composant titre_de_page (bandeau, titre) : un cadre dessin, padding
22px 30px 16px, rotation -1.2deg, trait 5px, ombre 10px 10px 0 nuit, avec un bandeau
déchiré (left 16px, top -16px, rotation -3deg) en texte 800 14px, espacement 0.3em,
laiton. Le h1 en police titre 46px, interligne 1.02, nuit, ombre 4px 4px 0 magenta,
contour 1px encre.

5. La feuille, un composant feuille (HTML), le corps de la lettre : un cadre dessin,
padding 34px 38px 30px, trait 4px, ombre 10px 10px 0 nuit, texte encre. Les titres de
section en police titre 30px, interligne 1.1, nuit, ombre 3px 3px 0 ciel, marge 30px
0 10px, le premier sans marge haute. Les sous-titres en texte 800 15px, espacement
0.3em, majuscules, patine, marge 26px 0 8px. Paragraphes et puces en texte 500 19px,
interligne 1.45, couleur #2a2616 ; listes avec 1.2em de retrait, 4px entre les puces.
Les liens dans la feuille en magenta soulignés. Le code en ligne sur fond
papier-sombre avec 0.3em de padding horizontal.

6. Le prompt, un composant prompt (texte), pour chaque bloc de code : un cadre dessin
fond nuit, trait 3px, ombre 6px 6px 0 magenta, texte papier, padding 14px 18px 12px,
marge 16px 0 22px. Une étiquette déchirée laiton « PROMPT » en coin (left 10px, top
-14px, rotation -2deg), texte 800 12px, espacement 0.35em, encre. Le texte dans un
pre en police texte 500 16px, interligne 1.5, pre-wrap, replié à max-height 4.6em avec
overflow hidden et un ::after de 2.2em en dégradé de transparent vers nuit collé en
bas ; la classe ouvert enlève la limite et le dégradé. Sous le texte, deux boutons en
flex, écart 10px : « Voir plus » (fond papier, rotation -1deg) et « Copier » (fond
laiton, rotation 1deg), en texte 800 13px, espacement 0.25em, majuscules, encre,
bordure 2px encre, padding 6px 12px 4px, ombre 3px 3px 0 encre ; enfoncés de 2px au
clic. Un prompt de trois lignes ou moins n'a ni dégradé ni « Voir plus ».

7. Dans www/js/site.js, deux comportements de plus, et rien d'autre : « Voir plus »
bascule la classe ouvert et devient « Voir moins » ; « Copier » met le texte du pre,
sans les espaces de bord, dans le presse-papiers, affiche « Copié » 1,6 seconde,
puis redevient « Copier ».

8. Sous 640px : la feuille en padding 26px 20px, le h1 à 34px, la navigation sur une
colonne avec « Tous les chapitres » en dernier. La meta description de la page est
le résumé du chapitre. Les règles de l'accueil tiennent toujours : aria-label sur
chaque bouton, focus visible, pas de transition avec prefers-reduced-motion, aucun
texte en dur qui vienne d'une lettre.

Plus tard : l'écran de jeu, /jouer/<id>, avec un bouton retour vers cet article ; un
catalogue des composants. Les composants d'aujourd'hui y seront montrés tels quels.

Ce que je ne veux pas : pas de base de données, pas de framework, pas d'image, pas de
JavaScript au-delà des trois comportements (bords déchirés, voir plus, copier), pas de
style en ligne, pas de CSS dupliqué : ce qui existe pour l'accueil est réutilisé.

Quand c'est fait, montre-moi http://localhost:8092/article/A0-03. Je dois lire la
lettre entière, déplier et copier un prompt, aller au chapitre précédent et revenir,
ouvrir l'article de version-0, voir la page 404 sur /article/rien, et voir le tout
tenir à la largeur d'un téléphone. Donne-moi la liste des fichiers créés ou modifiés,
une ligne chacun.

L'écran de jeu

Un jeu qu'on lance sans pouvoir revenir, c'est une porte sans poignée. Le prompt ci-dessous a d'abord fait un écran à part : le build dans un cadre, une barre au-dessus avec Retour et Plein écran.

En l'essayant, on a changé d'avis. Le jeu est mieux dans la page du chapitre, juste sous le titre. On joue, et la page raconte ce qui a été fait, sans aller et venir. L'ancienne adresse mène maintenant là. Au passage, le site a cessé d'afficher la page d'Unity : il lance le build sur son propre canevas, sans le logo ni le bouton d'Unity. Le build, lui, n'a pas bougé d'un octet.

PROMPT
Où on en est : le site tourne sur http://localhost:8092, avec l'accueil et la page
d'un chapitre, faits aux prompts précédents, dans une organisation en composants PHP
(www/composants, une fonction par fichier), une seule feuille www/css/site.css et un
seul script www/js/site.js. Les liens « Jouer » ouvrent pour l'instant le build Unity
tel quel, /versions/<id>/index.html : la page d'origine de Unity, avec sa barre de
chargement, sans aucun moyen de revenir au site, sans titre, sans plein écran. Les
builds sont en lecture seule : on ne touche jamais à leurs fichiers. Commence par
lire www en entier et garde cette organisation. Tu n'as aucune image ni maquette.

Ce que je veux : un écran de jeu, /jouer/<id>, dans la mise en page du site, qui
encadre le build sans le modifier.

1. La page est faite de deux zones, sur toute la hauteur de la fenêtre, sans
défilement : une barre en haut, et le jeu en dessous qui prend tout le reste.
Pas de diorama, pas de pied de page.

2. La barre, un composant barre_de_jeu (id, titre, sous-titre, adresse de l'article) :
fond encre, hauteur 64px, padding 0 16px, en flex, centré verticalement, écart 14px,
avec un trait de 3px laiton en bas, filtre crayon2. De gauche à droite : un bouton
« Retour » (cadre dessin, fond patine, texte papier, trait 3px, ombre « 0 5px 0
#27605C, 0 5px 0 3px encre », padding 8px 14px 6px, texte 800 13px, espacement 0.3em,
majuscules, avec le caractère ← devant ; au survol translateY(3px) et ombre à 2px) qui
mène à l'article du chapitre ; le nom « Orévia » en police titre 24px, papier, contour
1.5px encre, ombre 3px 3px 0 nuit ; puis le chapitre : l'identifiant dans une
étiquette déchirée laiton (police titre 15px, encre) et le titre du chapitre en
police titre 20px, papier, sur une ligne coupée par des points de suspension ; et,
poussés à droite, deux boutons : « Plein écran » (même dessin que Retour, fond laiton,
texte encre, ombre laiton-fonce) et « Lire la page » (fond papier, texte encre,
ombre nuit), qui mène à l'article. Sous 640px, le titre du chapitre disparaît et
« Lire la page » aussi : il reste Retour, Orévia, l'identifiant et Plein écran.

3. Le jeu : une iframe sur /versions/<id>/index.html, sans bordure, largeur et hauteur
100% de sa zone, fond encre, avec allow="fullscreen; pointer-lock; autoplay" et
allowfullscreen. La zone qui contient l'iframe a position relative, et c'est elle qui
passe en plein écran, pas l'iframe seule, pour que le navigateur garde le son. Une
ligne d'aide sous la barre, dans la zone de jeu, posée en haut à droite en
semi-transparence (fond rgba(21,22,31,.7), texte papier 600 14px, padding 6px 12px,
coins arrondis 6px), affiche les commandes lues dans le champ commandes de l'en-tête
de la lettre quand il existe (par exemple « ZQSD ou flèches · Espace · Échap pour le
menu »), et « Cliquez dans le jeu pour prendre la main » sinon. Elle s'efface au
premier clic dans la page.

4. Le JavaScript de cet écran, dans site.js, un seul comportement de plus : le bouton
« Plein écran » appelle requestFullscreen sur la zone de jeu et devient « Quitter »
tant qu'on y est ; au chargement, et à chaque clic sur la zone, l'iframe reçoit le
focus pour que le clavier aille au jeu. Rien d'autre : le chargement, la barre de
progression et le jeu lui-même restent ceux du build Unity, dans l'iframe.

5. Sur un écran de moins de 900px de large, un bandeau en haut de la zone de jeu dit
« Ce jeu se joue au clavier ou à la manette, sur un ordinateur » (fond laiton, texte
encre, 600 15px, padding 8px 16px, centré) ; le jeu se charge quand même en dessous.
Un lien « Le jeu ne se charge pas ? Ouvrir le build seul », en texte 600 14px,
laiton, souligné, posé en bas à droite de la zone en semi-transparence comme l'aide,
ouvre /versions/<id>/index.html dans un nouvel onglet.

6. Tous les liens « Jouer » du site (les deux touches de l'accueil, les cartes, et
tout lien de jeu dans un article) pointent maintenant vers /jouer/<id>. Le titre de
la page est « Jouer · <titre> · Orévia », sa meta description le résumé du chapitre.
Un identifiant inconnu donne la page 404. Les règles tiennent : aria-label sur chaque
bouton, focus visible, pas de transition avec prefers-reduced-motion.

Plus tard : la télémétrie se branchera sur cet écran, et un bouton « Signaler un
bug » viendra dans la barre ; laisse la barre extensible, avec un emplacement vide
à droite avant Plein écran.

Ce que je ne veux pas : aucune modification des fichiers des builds, pas de copie du
build dans Site, pas de réécriture de la page index.html de Unity, pas de JavaScript
au-delà du plein écran et du focus, pas d'image.

Quand c'est fait, montre-moi http://localhost:8092/jouer/version-0 : la barre, le
jeu qui se charge et répond au clavier après un clic, le plein écran qui marche et se
quitte avec Échap, Retour qui ramène à l'article, et l'accueil dont les deux boutons
mènent ici. Donne-moi la liste des fichiers créés ou modifiés, une ligne chacun.

Les briques et le catalogue

PROMPT
Où on en est : le site tourne sur http://localhost:8092 avec trois écrans faits aux
prompts précédents : l'accueil, la page d'un chapitre /article/<id>, l'écran de jeu
/jouer/<id>. Le code est censé être en composants PHP sous www/composants, une
fonction par fichier qui retourne du HTML, avec une seule feuille www/css/site.css et
un seul script www/js/site.js. Commence par lire www en entier, puis fais la liste de
ce qui s'écarte de cette organisation : du HTML écrit directement dans une page, un
style en ligne hors diorama, une règle CSS dupliquée, une couleur en dur, un texte en
dur qui vient d'une lettre. Tu n'as aucune image ni maquette.

Ce que je veux : finir l'organisation et la rendre visible, sans rien changer à
l'écran.

1. Tout ce que tu as listé rentre dans le rang : chaque morceau visible devient un
composant s'il ne l'est pas déjà, dans la liste suivante, un fichier par nom : cadre,
etiquette, touche, carte_chapitre, navigation_chapitres, titre_de_page, feuille,
prompt, barre_de_jeu, diorama, marque, pied, bouton_simple. Chaque fichier commence
par un commentaire de trois lignes : à quoi il sert, ses paramètres, un exemple
d'appel. Les pages (www/pages) n'ont plus une balise HTML en dur : elles assemblent.
La feuille CSS a une section par composant, dans l'ordre de la liste, chacune
précédée d'un commentaire au nom du composant, les variables en tête, et plus une
couleur en dur hors diorama.

2. Une page catalogue, /composants, non reliée depuis l'accueil mais depuis un lien
discret dans le pied de page : elle affiche chaque composant de la liste, un par
bloc, avec des données d'exemple, et en dessous, dans un petit cartouche (texte 600
14px, couleur #9a9a9a), son nom de fichier, ses paramètres et son exemple d'appel,
lus dans le commentaire d'en-tête du fichier. Les composants qui ont des variantes
(touche laiton et magenta, navigation avec et sans suivant, carte dernière, sombre
et à venir, prompt court et long) les montrent toutes. Le diorama y est montré en
petit, dans une boîte de 420px de haut. Cette page sert à vérifier le design d'un
coup d'œil et à montrer aux gens comment réutiliser une brique.

3. Une passe sur les règles communes, à vérifier page par page et à corriger : chaque
bouton a un aria-label ; le focus clavier est visible, contour 3px magenta décalé de
2px ; avec prefers-reduced-motion, aucune transition ; chaque page a son title et sa
meta description ; aucun défilement horizontal à 360px de large ; aucun texte en dur
qui vienne d'une lettre ou de accueil.md ; les champs inconnus d'un en-tête ne cassent
rien ; www/js/site.js ne fait que quatre choses : bords déchirés, voir plus, copier,
plein écran et focus du jeu.

4. Un fichier www/README.md d'une page, en français : comment le site lit les
versions, comment ajouter un composant, comment ajouter une page, et la liste des
routes.

Plus tard : chaque nouveau bloc du site naîtra comme composant et entrera au
catalogue le jour même ; un second thème ne touchera que les variables de :root.

Ce que je ne veux pas : aucun changement visible sur l'accueil, l'article et l'écran
de jeu ; pas de framework, pas d'outil de build, pas de base de données, pas d'image,
pas de nouvelle dépendance.

Quand c'est fait, montre-moi http://localhost:8092/composants, puis l'accueil,
l'article A0-03 et l'écran de jeu de version-0, pour que je vérifie que rien n'a
bougé. Donne-moi la liste de ce que tu as déplacé ou corrigé, une ligne chacun, et le
contenu du README.

Comment on vérifie

On ouvre le site, on clique sur « Tester la dernière version » : la scène vide, un ciel. On clique sur « Tester la démo d'origine » : Solea se charge en haut de sa page, on clique dedans, on joue, on passe en plein écran, on revient. On ouvre l'article du chapitre, on déplie un prompt, on le copie. On ouvre la page des briques, pour voir chaque composant seul. Et on réduit la fenêtre à la largeur d'un téléphone : tout doit tenir.

Ce qui a mordu

Sources