Partage la police JetBrains Mono (déjà utilisée sur /admin) via un
module commun src/lib/fonts.ts, et l'applique à l'entrée "Admin" du
menu déroulant. Remplace son icône générique par une nouvelle icône
terminal (IconTerminal), cohérente avec la rupture volontaire de
thème sur cette page technique.
Déplace La Roulette de sa page dédiée vers une section de /char (tout
l'outillage de la soirée du Tribunal au même endroit) et retire la
route /roulette. Corrige un bug de superposition où le pointeur doré
de la roulette passait devant le menu déroulant du header (isolation
de contexte d'empilement manquante). Renomme "Le Conseil des
Archontes" en "Admin" avec une police monospace, rupture volontaire
avec le thème grec pour cette page technique.
Page /char (16:9, projetée) pour piloter les deux jeux physiques du
Tribunal : Le Char (2 colonnes de 3 emplacements côte à côte avec un
VS et une illustration de char vu du dessus, sélection en un clic,
duels, compteur et classement des passages) et Le Gardien du Silence
(tirage aléatoire parmi les Citoyens, minuteur avec reroll auto et
manuel). Banque de questions gérée séparément sur /char/questions
(pensée pour être pilotée depuis un téléphone pendant que /char est
projetée depuis un ordinateur).
À partir du score 10, la vitesse de défilement augmente et l'écart
entre les colonnes se resserre progressivement, jusqu'à un palier à
score 50 (vitesse ×1.6, écart réduit à 75%) pour ne pas devenir
injouable sur une longue partie. Le gap est maintenant stocké par
colonne (plutôt qu'une constante globale) pour que les colonnes déjà
en place ne changent pas de taille rétroactivement.
- Écran de fin (score + Rejouer) déplacé en overlay par-dessus le jeu
(fond assombri, carte animée, confettis) plutôt qu'en dessous.
- Battement d'ailes repensé : cycle continu en boucle (visible même au
repos, sans taper) + battement plus ample au tap qui prend le dessus
brièvement. L'ancienne version ne bougeait qu'au moment du tap et
retombait trop vite pour être perceptible en jeu réel.
- Corrige une aile qui semblait ne pas bouger : les deux ailes utilisaient
des teintes différentes et l'une passait partiellement derrière le
corps ; elles sont maintenant identiques et toutes deux dessinées
au-dessus du corps. Corrige au passage un signe de rotation inversé.
- Corrige le contraste de plusieurs textes (« Ton record », instructions,
pied de page) qui utilisaient une couleur prévue pour du texte sur
marbre alors qu'ils s'affichent sur le fond sombre de la page.
Après retour d'expérience, la course de char (vue du dessus, piste
générée par jour) laisse place à un Flappy Bird grec plus simple et
plus lisible sur téléphone : Icare vole entre des colonnes de temple,
touche l'écran pour battre des ailes, échec net au premier contact
(score remis à zéro).
Record personnel all-time (plus de piste quotidienne ni de fantômes) :
les scores se figent dès que la date du Tribunal est atteinte, puis les
gloires du top 3 sont attribuées automatiquement via une tâche
planifiée pg_cron, comme pour l'ancienne Course du Char.
schema.sql nettoie explicitement l'ancienne Course du Char (tables,
RPC, tâche planifiée) avant de poser le nouveau schéma, puisqu'elle
avait déjà été appliquée en prod.
Premier mini-jeu compétitif du Tribunal (/course) : piste 2D générée
de façon déterministe à partir de la date du jour (identique pour tout
le monde, jamais stockée côté serveur), pilotage au joystick tactile
virtuel, murs + obstacles qui ralentissent temporairement sans jamais
stopper la course, fantômes du top 3 du jour. Chacun rejoue autant
qu'il veut, seul le meilleur temps compte.
Moteur de jeu maison (src/lib/chariot/) : canvas + requestAnimationFrame,
sans dépendance externe, dans la continuité de l'existant (roulette).
Navigation : nouvelle entrée "La Course du Char" (IconChariot), route
protégée par le proxy comme le reste de l'app.
computeRanksBy(items, comparator) remplace la logique de classement
"ex-aequo partagent le rang" en la rendant générique ; computeRanks
devient un simple appel avec le comparateur points desc, sans rien
changer pour l'appelant existant. Podium accepte maintenant un prop
renderValue (+ valueLabel optionnel) à la place du "points/gloires" en
dur, pour pouvoir afficher un classement par temps (Course du Char)
avec le même composant que le classement par points.
- chariot_runs : meilleur temps + tracé fantôme par joueur et par jour,
verrouillée comme points_log (écriture uniquement via RPC).
- submit_chariot_run() : calcule la date côté serveur, borne le temps,
n'écrase que si strictement meilleur.
- close_daily_chariot_race() + chariot_race_closes : clôture la journée
précédente et attribue les gloires du top 3 automatiquement, sans
intervention d'un Archonte — planifiée via pg_cron (à activer une
fois côté Dashboard Supabase).
- points_log.judge_id devient nullable (un point auto n'a pas de juge) ;
Le Crieur affiche "Le Tribunal" pour ces décrets-là.
Un seul round sur toute la semaine : plus besoin de pouvoir
réinitialiser le repère de classement depuis l'admin. La logique des
flèches de progression (previous_rank, reset_rank_reference) reste en
place, simplement plus rien ne la déclenche.
Le lien d'invitation Supabase redirige avec les tokens dans le FRAGMENT
d'URL (#access_token=...&refresh_token=...), jamais dans un paramètre
?code=. Un fragment n'est jamais envoyé au serveur par le navigateur
(une route serveur ne peut donc pas le lire), et le client
@supabase/ssr ne le détecte pas automatiquement (orienté sessions par
cookies). /signup parse maintenant ce fragment et appelle
auth.setSession() explicitement avant de vérifier l'utilisateur.
Supprime la route src/app/auth/callback créée par erreur sur
l'hypothèse d'un flux PKCE (?code=) qui ne correspond pas à ce que
Supabase envoie réellement pour ce type de lien.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ferme l'auto-inscription publique : l'app repose désormais sur le
mécanisme d'invitation natif de Supabase (Authentication → Users →
Invite user) plutôt que sur un formulaire /signup ouvert à tous.
- /login : retire le lien « Créer un compte ».
- /signup : entièrement repensée. Ce n'est plus un formulaire d'auto-
inscription (email + mot de passe) mais une page de FINALISATION de
compte invité. Au montage, vérifie s'il existe une session (établie
par le clic sur le lien d'invitation reçu par email) :
* pas de session → message « invitation requise » ;
* session + profil déjà finalisé (pseudo réel, pas le défaut
"Nouveau membre" posé par le trigger handle_new_user) → redirige
directement vers le leaderboard ;
* session + profil à finaliser → formulaire pseudo + mot de passe
+ photo, qui appelle `updateUser({ password })` puis met à jour
`profiles` (au lieu de `signUp()`).
- proxy : /signup retiré de AUTH_PATHS — un compte fraîchement invité a
déjà une session active et doit pouvoir rester sur cette page pour la
finaliser, sans être renvoyé au leaderboard par le redirect prévu pour
les utilisateurs déjà connectés.
- README/CLAUDE.md : documentent la désactivation de "Allow new users
to sign up", la configuration Site URL/Redirect URLs (→ /signup) pour
que le lien d'invitation atterrisse au bon endroit, et le flux
d'invitation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace le laurier dessiné à la main par le logo fourni (badge circulaire
« L'Agora ») comme identité visuelle principale : favicon (icon.png),
icône iOS (apple-icon.png, remplace icon.svg), marque du header, page
login, page signup, et grand format en en-tête du classement.
- components/agora-logo.tsx : composant partagé, `unoptimized` sur
next/image — l'optimiseur d'images de Next.js aplatit systématiquement
la transparence de ce fichier (PNG ou WebP) lors du redimensionnement
à la volée, donc on sert l'asset statique tel quel.
- Asset retravaillé (public/logo-agora.webp) : le fichier fourni n'avait
pas de vraie transparence (damier dessiné en dur, y compris dans les
zones "vides" de l'illustration) et le cercle était mal centré dans
son canevas carré (anneau doré rogné en haut). Recadré, recentré avec
une marge uniforme, fond extérieur en transparence réelle, fond
intérieur aplati en blanc crème (#F4ECD8, le token "marble" existant)
pour se fondre dans les cartes marbre de l'app.
- Header : bandeau renommé « LE TRIBUNAL » → « L'AGORA » (à gauche) ;
les icônes du menu déroulant restent inchangées (icônes distinctes
par page, nécessaires pour s'y retrouver).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Boutons de validation des points :
- Les icônes ✓/✕ étaient trop grandes (40px) et dupliquées entre le
podium et le reste du classement. Factorisées dans un composant
partagé PointsConfirmControls (32px, icône 16px, olive/oxblood,
aria-label + tooltip), utilisé à l'identique par JudgePointControls
sur le podium ET dans la liste.
Nouvelle page /calendrier — "Le Calendrier des Dieux" :
- Modèle de données : days (date, dieu, domaine), events (titre, type,
heure, lieu, created_by forcé par trigger), settings (ligne unique,
date du Tribunal). RLS : lecture ouverte à tout authentifié, écriture
(insert/update/delete) réservée au rôle judge par policies dédiées —
jamais un simple masquage des boutons côté client.
- 8 préréglages de divinités (Dionysos, Arès, Athéna, Aphrodite, Hermès,
Poséidon, Hadès, Zeus) avec domaine, couleur d'accent et emblème SVG ;
un Archonte peut aussi saisir une divinité libre.
- Bannière avec compte à rebours avant la date du Tribunal, éditable par
les Archontes. Journées en cartes (jour courant mis en évidence,
jours passés estompés, jour du Tribunal en accent oxblood), liste
d'événements avec icône par type (activité/défi/épreuve/tribunal).
Mode édition (ajout/modification/suppression avec confirmation)
réservé aux Archontes. Realtime sur les trois tables.
- Ajouté au menu déroulant du header, route protégée par le proxy.
Rendu des icônes dynamiques (GodEmblem, EventTypeIcon) via branchement
JSX explicite plutôt que variable de composant résolue à l'exécution,
pour respecter react-hooks/static-components.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Upload de photo (signup + profil) :
- Remplace le contrôle natif du navigateur par un composant entièrement
en français : input masqué (sr-only) déclenché par un vrai bouton
(icône appareil photo + « Choisir une photo »/« Changer ma photo »),
nom du fichier sélectionné affiché (tronqué) avec une croix pour le
retirer, « Aucune photo sélectionnée » sinon.
Header : plus jamais masqué sur mobile
- "LE TRIBUNAL" et le chip utilisateur (photo + pseudo + points) restent
visibles à toutes les tailles, y compris 320px — tailles de police et
troncature au lieu de `hidden`. Vérifié sans débordement horizontal à
320/375/414/768px.
Navigation : tout dans le menu déroulant
- Retire les onglets du header (ne garde que logo + chip). Le menu
déroulant du chip contient désormais toutes les pages (Le Classement,
La Roulette, Le Crieur, Mon profil, Le Conseil des Archontes si juge)
avec une icône chacune (components/icons.tsx), fermeture au clic
extérieur et à Échap, entrée de la page courante surlignée,
déconnexion séparée en oxblood.
Podium :
- Contrôles de points en icônes ✓/✕ rondes (40×40, olive/oxblood) sous
la carte du membre, aria-label + tooltip — uniquement sur le podium,
le reste du classement garde ses contrôles textuels.
- Refonte visuelle : socles marbre veiné avec arête lumineuse
(.pedestal-marble), chiffres romains gravés (toRoman(), .engraved),
couronne de laurier + halo doré sur le 1er, accents argent/bronze sur
2e/3e, points en gros chiffres dorés, animation d'entrée décalée par
socle (~600ms, respecte prefers-reduced-motion).
Nouvelle page /roulette :
- Tirage aléatoire uniforme calculé avant l'animation (le random ne sert
qu'à choisir, l'animation ne fait que le mettre en scène). Carrousel
horizontal des avatars avec décélération (cubic-bezier long), repère
central doré, révélation finale (couronne + halo + zoom) avec le
pseudo en Cinzel. Filtre Citoyens/tous, bouton Rejouer, dernier élu
affiché, prefers-reduced-motion affiche le résultat directement.
Route protégée par le proxy comme les autres pages.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace le thème générique (tokens navy/gold/ivory re-thématisables par
édition) par une direction artistique fixe et codée en dur : tribunal
égéen de nuit — marbre, or, mer sombre, sceaux de cire.
Fondations (globals.css, layout.tsx) :
- Polices next/font/google : Cinzel (titres, tracking large), Cormorant
Garamond (citations italiques), Manrope (interface).
- Nouveaux tokens couleur : ink/ink-2 (fond mer de nuit), marble/marble-2
(surfaces claires), gold/gold-bright (accents), oxblood (blâmes/erreurs),
olive (honneurs/succès), sea, text-marble/text-mut (texte sur marbre —
jamais de doré en texte courant, contraste insuffisant).
- Classes utilitaires : .marble-surface (texture par dégradés, pas
d'image de bruit), .meander-divider (frise à méandre grecque en SVG
data-URI).
Composants d'ornement (aucune image bitmap) :
- components/laurel-wreath.tsx : couronne de laurier calculée par
trigonométrie (deux arcs symétriques de feuilles), réutilisée en logo,
favicon (icon.svg régénéré avec les mêmes coordonnées) et couronne du
rang 1 du podium.
- components/diamond-divider.tsx : séparateur ◆ avec filets dégradés.
Header global (components/header.tsx, remplace nav-bar.tsx) :
- Logo laurier + "LE TRIBUNAL" à gauche (cliquable vers le leaderboard).
- Chip utilisateur à droite : avatar (bordure dorée), pseudo, badge de
rôle (Archonte/Citoyen), points de gloire, menu déroulant (Mon profil,
Le Conseil des Archontes si juge, Se déconnecter).
- Onglets L'Agora / Le Crieur, frise à méandre en séparateur.
- Responsive : nom du site et pseudo masqués sous le breakpoint sm,
l'avatar reste toujours visible.
Vocabulaire diégétique (toujours avec le terme fonctionnel à côté) :
juges → Archontes, membres → Citoyens, leaderboard → L'Agora, journal →
Le Crieur (décrets avec sceau de cire oxblood), points positifs/négatifs
→ Honneurs/Blâmes, points → gloires, section admin → Le Conseil des
Archontes.
Avatar : bordure dorée, couleur de fallback dérivée d'un hash du pseudo
(au lieu d'une couleur fixe).
Toutes les pages (login, signup, leaderboard/podium, journal, admin,
profil) sont passées sur les nouveaux tokens/polices/vocabulaire.
README.md et CLAUDE.md : section "Ré-thématisation" remplacée par
"Direction artistique" documentant la palette, les polices et les
ornements ; toute mention du système de re-thème par édition retirée.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Design & UX :
- Renomme "Administration" en "Admin" (nav, page, docs).
- Logo original (marteau de juge stylisé navy/or) en favicon (src/app/icon.svg,
remplace le favicon.ico par défaut de create-next-app) et dans la navbar
(components/logo.tsx).
- Navbar : menu hamburger sur mobile (les liens débordaient à partir de
~4 entrées sur petit écran).
- Leaderboard : lignes en deux niveaux sur mobile (infos membre / contrôles
juges) au lieu d'un seul flex-wrap qui devenait illisible.
- Contrôles de points juges (JudgePointControls) : boutons et champ montant
réduits en mode compact pour tenir dans les colonnes du podium sur petit
écran.
- Podium : scroll horizontal de secours (overflow-x-auto) si le contenu
dépasse malgré tout sur très petits écrans.
- Table admin : pseudo/avatar sur une ligne, rôle/statut/actions sur une
autre en mobile (le flex-1 sur l'input pseudo n'avait aucun effet en
layout colonne).
Correctif sécurité/fonctionnel :
- storage.objects n'avait que des policies INSERT et UPDATE pour le bucket
avatars, pas de policy SELECT. Postgres a besoin de lire la ligne
existante pour évaluer la clause USING d'un UPDATE : sans SELECT, tout
remplacement de photo (upsert) échouait avec "new row violates row-level
security policy", même si les policies INSERT/UPDATE étaient correctes.
Ajout d'une policy SELECT publique sur le bucket avatars (cohérent avec
le fait que le bucket est déjà public en lecture).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
V2 — authentification et administration :
- Authentification par email + mot de passe (email confirmé, un compte
par email), abandon de l'email interne dérivé du pseudo.
- Rôles public/judge sur profiles, section Administration (juges
uniquement, vérifiée côté serveur) pour gérer les membres.
- Verrou de pseudo : modifiable une fois par son propriétaire puis figé,
contournable par un juge.
- RLS étendue par un trigger BEFORE UPDATE (enforce_profile_update) pour
verrouiller les colonnes sensibles (role, points, pseudo_locked, pseudo
figé) — la RLS seule ne peut pas exprimer une règle par colonne.
V3 — points, journal, podium, progression, photo :
- RPC award_points() (SECURITY DEFINER) : seul point d'écriture de la
colonne points, vérifie le rôle juge côté serveur, delta signé sans
plancher à 0, trace chaque opération dans points_log.
- Leaderboard temps réel (Supabase Realtime) avec podium top 3
(égalités gérées), flèches de progression (previous_rank), et
contrôles de points juges avec confirmation explicite (plus de
debounce auto) avant envoi.
- Page /journal ("le crieur") : fil live et public des attributions de
points.
- Recadrage photo carré + compression client (react-easy-crop + canvas)
au signup et sur le profil.
- Passe de polish visuel : cartes/boutons cohérents, lien actif dans la
nav, podium retravaillé.
schema.sql, README.md et CLAUDE.md mis à jour en conséquence (schéma
idempotent, instructions SMTP/rôles/migration, conventions RLS+trigger
documentées pour les futures colonnes sensibles).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Next.js 16 + Tailwind + Supabase (Auth, Postgres, Storage). Connexion
par pseudo/mot de passe via email interne dérivé, RLS avec points
protégés au niveau colonne, pages login/signup/leaderboard/profile.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>