# Handoff: Menüführung — Text reduzieren (Splatter Tennis) ## Overview Die funktionalen Screens von Splatter Tennis (Splash, PICK A GAME, MATCH SETUP, DIFFICULTY, TRAINING, VS A FRIEND und die In-Game-Modals) tragen heute mehr Text als eine Entscheidung braucht — auf `PICK A GAME` rund 60 Wörter, verteilt auf drei Rubriken mit Straps plus sechs Poster mit je einer Regelzeile. Das Ergebnis dieser Session: eine reduzierte Fassung aller funktionalen Screens (~12–18 Wörter auf der Wand) und ein Wechsel der aufrechten Display-Schrift von **Bungee** auf **Jersey 15**. Gameplay, Stats-Screens und Canvas-HUD sind bewusst NICHT Teil dieses Pakets. ## About the Design Files `Menues.dc.html` ist eine **Design-Referenz in HTML** — ein Prototyp, der Aussehen und Struktur zeigt, kein Produktionscode zum Kopieren. Splatter Tennis ist Vanilla JS ohne Build-Step (`CLAUDE.md`, Punkt 5: „Zero dependencies, no build step, no framework"), also wird die Fassung in der bestehenden Umgebung nachgebaut: DOM-Menüs in `index.html` + `src/main.js`, Canvas-Modals in `src/render.js`, Texte in `src/config.js`. Kein neues Framework, keine neuen Dateien ausser der Schriftdatei. Die Datei enthält mehrere Runden untereinander, neueste oben. Verbindlich ist **Runde 5** („GESETZT — ALLE SCREENS IN JERSEY 15"). Darunter liegen als Historie: Runde 4 (Schrift-Kandidaten Bebas Neue / Oswald / Jersey 15), Runde 3a (derselbe Satz in Bebas), Runde 2 (Bungee vs. Bangers), Runde 1 (die zwei Grundrichtungen) und ganz unten der **IST-Stand**, pixelgenau aus dem heutigen `index.html` nachgebaut — nützlich als Vor/Nach-Vergleich. ## Fidelity **High-fidelity.** Palette, Masse, Schriftgrössen und Copy sind aus dem Repo übernommen (`index.html` CSS, `config.MODES` / `MENU` / `COURTS` / `LEAGUES` / `OPPONENTS`, `beats.js` Modal-Payloads). Alle Sprites/Poster im Prototyp sind echte Repo-Assets. Übernahme darf 1:1 erfolgen. ## Die drei Register (die eigentliche Entscheidung) | Rolle | heute | neu | |---|---|---| | benennen — Screen-Titel, Rubriken, Karten- und Poster-Namen | Bungee | **Jersey 15** | | rufen — einmal pro Screen: Fixture, Primär-Button, Modal-Headline | Bangers | Bangers (unverändert) | | informieren — Zahlen, Labels, Chips | Silkscreen | Silkscreen (unverändert) | Begründung Bungee → Jersey 15: Bungee ist Single-Weight und sehr fett/blockig; Jersey 15 sitzt auf demselben Pixelraster wie Silkscreen (wirkt als Schriftfamilie mit der Sprite-Art), läuft proportional und deutlich leichter. Beide sind SIL OFL. **Grössen-Umrechnung Bungee → Jersey 15: ca. +40 %** (Jersey sitzt tiefer im Kasten und läuft leichter). Konkrete Werte im Abschnitt „Design Tokens". ## Schrift einbauen 1. `Jersey15-Regular.ttf` von Google Fonts holen, wie Bungee subsetten (`pyftsubset`, Befehl steht in `PROJECT.md`) → `assets/fonts/Jersey15-Subset.ttf`. 2. In `index.html` das `@font-face` von Bungee ersetzen (`?v=` mitziehen wie bei den anderen drei): ```css @font-face { font-family: 'Jersey 15'; font-weight: 400; font-display: block; src: url('assets/fonts/Jersey15-Subset.ttf?v=87') format('truetype'); } ``` 3. Alle `Bungee`-Vorkommen in `index.html` auf `'Jersey 15'` umstellen (Liste unten), `Bungee-Subset.ttf` und `Bungee-OFL.txt` löschen, `Jersey15-OFL.txt` daneben legen. 4. `main.js?v=N` in `index.html` bumpen (`CLAUDE.md`, Punkt 4). ## Screens / Views ### 1 — Splash (`#splash` in index.html) **Zweck:** Eintritt; die Plakette zeigt wer du bist, der Primär-Button das nächste Fixture. Änderungen: - **Plakette (`#plaque`)** wird eine Zeile statt vier Grid-Areas: Wappen links (50px), rechts Name + `LEVEL 12` in einer Baseline-Zeile, darunter der XP-Balken mit der Ziel-Zahl rechts. **`#plGoal` entfällt komplett** — „WIN THE CUP TO REACH LEAGUE 4" sagt der Fixture-Button darunter schon. Grid-Areas `name/lv/bar/goal` werden zu einem Flex-Row mit zwei Spalten. - Name: Jersey 15 21px, `#ccd7ca`, `letter-spacing:0.5px` (war Silkscreen 700 13px) - `LEVEL 12`: Silkscreen 700 10px, `#bcd455` (war Bangers 26px als `#plLv` — der Ruf gehört dem Button unten, nicht der Plakette) - Balken unverändert (6px, `#2c4238` Rahmen, Gradient `#3f9560 → #bcd455 55% → #e8b23a`) - **Fixture-Button (`#mStart`)** bleibt Bangers 26px; die zweite Zeile (`#msLine`) verliert das Rundenpräfix: `ROUND ONE: THE STRAWBERRY SELLER` statt `4 ROUNDS · ROUND ONE: THE STRAWBERRY SELLER`. → in `config.cupFixture` die `line` kürzen (`rounds.length + ' ROUNDS · '` fällt weg; die Rundenzahl steht im Modal). - **`#mOther`** verliert seinen Strap (`CLIMB · TRAINING · GORE · A FRIEND` löschen), Label auf Jersey 15 19px. - **`#mStats` / `#mRanks`**: Straps ersetzt durch je eine Zahl aus lokalem Stand — `STATS / 4 812` (Kill-Count, wie heute) und `BOARD / BEST 612` (eigener Bestwert; **kein** Weltrang, der Splash lädt `/data/scores.json` nicht). Label Jersey 15 16px, Zahl Silkscreen 700 10px `#bcd455`. - Logo, Slogan (`Fifteen. Thirty. Forty. Dead.`), `#build` unverändert. ### 2 — PICK A GAME (`#stmode` / `#modeList`, gebaut in `main.buildTops`) **Zweck:** einen Modus wählen. Das ist der Screen, der „erschlägt". - **Rubriken** (`#modeList .mgroup > header`) verlieren ihren Strap (``) und werden ein Wort: `THE SEASON → SEASON`, `THE TRAINING → TRAINING`, `OUT OF COMPETITION → CASUAL`. Jersey 15 17px, `#bcd455`, `letter-spacing:2px`. → `config.MENU`: `sub` der drei `group`-Rows löschen, `group`-Text kürzen; in `main.buildTops` das `` aus dem `header`-Template nehmen. - **Poster** tragen nur noch den Namen — `${card.sub}` fällt in `main.banner()` weg. Damit werden die `sub`-Felder in `config.MODES` für die Wand irrelevant (Aufbewahren lohnt: die Modals nutzen dieselben Sätze). - **Hero-Poster** (das Fixture, `mode:'cup'`) ist der einzige mit einer zweiten Zeile und dem einzigen Ruf: Name in **Bangers 32px**, darunter die Fixture-Zeile in Silkscreen 10px `#f0cfc4`. Höhe 142px. `.way`-Ribbon („THE WAY UP") unverändert. - **Übrige Poster** in Jersey 15, Höhe gestaffelt statt einheitlich 148px: - TRAINING-Raum: zwei Poster **nebeneinander**, je `flex:1`, Höhe 106px, Name 27px - CASUAL-Raum: drei Poster gestapelt, Höhe 84px, Name 30px - **Regeln, die wirklich anders sind, bleiben als Chip** (`.last`-Optik, Silkscreen 8px, acid, `rgba(0,0,0,0.72)` Grund): `18 RUNGS` auf TRAINING, `LAST` auf dem zuletzt gespielten. - Ergebnis: der ganze Wall passt in 844px (heute zwei Bildschirme). Gerechnet mit Header 64 + Padding 14 + 142/106/84 + Gaps 17/8. ### 3 — MATCH SETUP (`#setup`) - `

01 · PLAYER

` / `02 · COURT` → Rubrik-Zeile wie auf der Wand: Jersey 15 17px acid, Text `PLAYER` / `COURT`. Die Nummerierung entfällt (es sind zwei Blöcke auf einem Screen, keine Schritte). - Über der Platzliste steht die Legende **einmal**: rechts in der Rubrik-Zeile `BOUNCE · PACE` (Silkscreen 8px, dim). - **Platz-Zeilen** (`#courtList button.court`) verlieren `${court.sub}` — Name (Jersey 15 19px) plus die zwei Meter, die dasselbe wortlos sagen. Bildhöhe 66 → 60px. - **Gesperrte Plätze** (`.court.lock`) behalten Bild und Namen, statt einer Erklärung nur `LEAGUE 4` in `#cf5a4e`, Silkscreen 9px. - **Tour-Tiles**: `MEN'S SINGLES` / `WOMEN'S SINGLES` → `MEN'S` / `WOMEN'S`, Jersey 15 16px. - **`#sSum`** (die Zusammenfassungszeile über SERVE UP) entfällt — alle drei genannten Werte sind auf dem Screen sichtbar markiert. - `SERVE UP` bleibt Bangers 26px auf `#a4231f`. ### 4 — DIFFICULTY (`#stsub` / `#modeSub button.chip`, aus `config.DIFFS`) - Chip wird zweispaltig: Name links (Jersey 15 30px), **drei Pips** rechts (je 10×10px, kein Radius) — 1/2/3 gefüllt. EASY/MEDIUM acid `#bcd455`, HARD `#cf5a4e`; leere Pips `rgba(255,255,255,0.12)`. - Kein Erklärtext. Padding 26px/16px, `gap:10px`. - `LAST`-Chip bleibt oben rechts im gewählten. ### 5 — TRAINING (`#train` / `#trainList`, gebaut in `main.buildTrain`) - `

PICK YOUR OPPONENTONE GAME · GO AGAIN

` entfällt — der Screen-Titel `TRAINING` genügt. - **Revanche-Tile** (`profile.prof.out`) wird eine **breite Zeile über dem Raster**: Kopf 52px links, Name (Jersey 15 24px), darunter `PUT YOU OUT` (statt `PUT YOU OUT OF THE CUP`) in `#e08a7c`, Chevron rechts. Rahmen `#a4231f`, Grund `rgba(164,35,31,0.16)`. - **Übrige Rungs** bleiben das 2-spaltige Raster: Kopf 44px, Name Jersey 15 17px zentriert, Rangnummer zweistellig (`01`, `02`, …) statt `No. 1` — Silkscreen 8px dim. - **Letzte Kachel** ist ein Ziel, keine Liste: gestrichelter Rahmen `#2c4238`, `LEAGUE 4` und `13 — 15` in `#4a5a52`. (Heute endet die Liste einfach am Band-Cap.) ### 6 — VS A FRIEND (`#vs` / `#vsPick`) - Die drei `

` mit Straps (`01 · YOUR NAME / ON THEIR SCOREBOARD`, `02 · HOST A MATCH / THEY COME TO YOU`, `OR JOIN THEIRS / WITH THEIR CODE`) werden drei Wörter: **`YOU` / `HOST` / `JOIN`**, Jersey 15 17px acid. - Host-Buttons: `CLASSIC TENNIS` / `DEATH MATCH` → `CLASSIC` / `DEATH MATCH` nebeneinander, Jersey 15 19px, Padding 20px. - Join-Zeile: Code-Feld plus Button `GO` (statt `JOIN` — daneben steht die Rubrik JOIN), Feld-Letterspacing 6px. - `#vsErr`-Meldungen unverändert (`THE CODE IS FOUR LETTERS`). ### 7 — In-Game-Modals (`render.drawModal`, Payloads aus `beats.js`) Die Box-Sprache aus dem 2026-08-31-Pass bleibt (`MODAL_PAD 14`, `MODAL_GAP 13`, `MODAL_BTN 30`, `MODAL_FOOT 8`, Breite `min(W-44, 244)`, Fill `rgba(8,22,16,0.93)`, Edge `rgba(120,150,130,0.45)`, Rules `rgba(120,150,130,0.22)`). Reduziert wird der **Inhalt einer Row**: - **Eine Regel pro Row, keine Unterzeile.** Heute rendert `modalRows` pro `m.rules`-Eintrag `lead` + `sub`; der `sub` formuliert in fast allen Fällen den `lead` nur um (`YOUR LEAGUE, ROUND BY ROUND` / `WIN IT ALL — MOVE UP A LEAGUE`). Neu: **eine** Zeile, die das Ergebnis nennt — `WIN IT ALL — MOVE UP A LEAGUE`. Also in `beats.js` die `rules`-Arrays auf je einen String reduzieren und `modalRows` entsprechend vereinfachen; `m.body` bleibt als Fallback unverändert. - **Blut ist Pips, kein Satz**: Row `POINTS DRAW BLOOD` (gekürzt von `ITS POINTS DRAW BLOOD`) plus `TUNE.matchPlayerHp` Pips **in derselben Zeile rechts**, statt Zeile + Unterzeile (`AT ZERO YOU ARE EATEN`) + Pips darunter. - **Masthead**: Titel Bangers 27px in der Modal-Farbe, darunter EINE Silkscreen-10px-Zeile. Beim Rules-Modal ist das die Sachzeile (`LEAGUE 3 · 4 ROUNDS`) — hier landet die Rundenzahl, die aus `cupFixture.line` genommen wurde. Beim Walk-on bleibt es `foe.line` (`CREAM WITH THAT?`). - **Walk-on** (Gegner): Kopf-Sprite 56px, Name, `foe.line`, dann EINE Row mit den zwei Leveln gegenüber (`LEVEL 14` hot links, `YOURS 12` dim rechts) — statt zwei Textzeilen plus Split-Figur. - Footer-Button unverändert (`WALK ON` / `GOT IT` / `PLAY ON`, `m.btn` überschreibt). ### 8 — Quit-Modal (`#qAsk` / `.qBox`) Unverändert übernehmen — es ist schon lean. Nur `LEAVE THE MATCH?` von Silkscreen 12px auf **Bangers 20px** (es ist eine Frage, die ruft), `PLAY ON` auf Jersey 15 17px. ## Interactions & Behavior Keine Änderung an Navigation oder Zustandslogik. Alles Bestehende bleibt: - `go(stage)` / `#intro[data-stage]`, ein Overlay, vier Panels - Tap auf ein Poster antwortet UND geht weiter (`main.buildTops`), `pickMode` schreibt `localStorage.mode` - Die Deal-in-Animation (`#intro.deal`, `dealIn` 0.28s `cubic-bezier(0.34,1.56,0.64,1)`, Delays 0.02/0.09/0.16/0.23/0.30s, `.mgroup`-Staffel 0.09/0.17/0.25s) gilt weiter — die Rubriken bleiben `
`, damit sie greift - `prefers-reduced-motion: reduce` schaltet sie weiter ab - Modal-Dismiss-Regeln unverändert (`m.in < 1` schluckt die Gesture, Tap überall entlässt, `G.seen.` gated) - Swipe-links-zurück im Shell bleibt unberührt ## State Management Nichts Neues. Zwei Werte werden neu GELESEN, beide existieren schon: - `profile.prof.out` (der Rung, der dich aus dem Cup geworfen hat) → Revanche-Zeile; wird bereits von `buildTrain` genutzt - der eigene Bestwert für die BOARD-Kachel → aus `stats` (lokal), **nicht** vom Server ## Design Tokens Unverändert aus `index.html`: ``` --ink #0e1714 --panel rgba(42,62,54,0.42) --line #2c4238 --acid #bcd455 --cream #ccd7ca --dim #6c7f74 --red #a4231f --bad #cf5a4e --solid #16211d Poster-Veil: linear-gradient(0deg, rgba(6,10,8,0.94) 0%, rgba(6,10,8,0.86) 22%, rgba(6,10,8,0.5) 58%, rgba(6,10,8,0) 100%) — Höhe 52–62 % je Kachelhöhe Modal: fill rgba(8,22,16,0.93) · edge rgba(120,150,130,0.45) · rule rgba(120,150,130,0.22) Rahmen: 2px überall in den Menüs, 1px im Canvas-Modal ``` Typografie neu (Bungee-Wert → Jersey-15-Wert): ``` Screen-Titel (.hdr h2) 23px → 33px/1 letter-spacing 1px Rubrik / Raum-Label 15px → 17px letter-spacing 2px Poster-Name, Hero — → Bangers 32px/0.95 (der eine Ruf) Poster-Name, TRAINING-Paar 17px → 27px/1 Poster-Name, CASUAL-Stapel 19px → 30px/1 Karten-/Platz-Name 15px → 22px Difficulty-Chip 22px → 30px Training-Tile-Name 12px → 17px/1 Sekundär-Button 13px → 18px Zahlen, Labels, Chips Silkscreen 8–10px (unverändert) Primär-Button Bangers 26px (unverändert) ``` ## Assets Alle Assets im Prototyp stammen aus dem Repo, unverändert — nichts muss neu gezeichnet werden: `assets/logo.png`, `menu_bg.png`, `mode_cup|arcade|train|gore|classic|vs.png`, `court_clay|hard|grass|gloom.png`, `badge_l3.png`, `z_{sh,bb,sp,gk,rm,st,ma,ru,sb,sg,lj,cu}head.png`, `fonts/{Silkscreen-Regular,Silkscreen-Bold,Bangers-Regular}.ttf`. NEU nötig: `fonts/Jersey15-Subset.ttf` (Google Fonts, SIL OFL) — ersetzt `Bungee-Subset.ttf`. ## Files - `Menues.dc.html` — der Prototyp. Runde 5 oben ist verbindlich; darunter Runde 4 (Schriftwahl), 3a (Bebas-Fassung), 2 (Bungee vs. Bangers), 1 (Grundrichtungen), IST-Stand. - `support.js` — Runtime des Prototyps, für die Implementierung irrelevant. - `assets/` — die Repo-Assets, die der Prototyp lädt. Betroffene Repo-Dateien: `index.html` (CSS + Markup der Panels), `src/main.js` (`banner`, `buildTops`, `buildSub`, `buildTrain`, `refreshSplash`), `src/config.js` (`MENU`-Gruppen, `cupFixture.line`, ggf. `MODES.sub`), `src/render.js` (`modalRows`, `modalLayout`), `src/beats.js` (die `rules`-Payloads). ## Vor dem Commit `node test.js` muss grün sein (`CLAUDE.md`, Punkt 2) — die Fixture-Deskriptor-Tests hängen an `cupFixture`, also die `line`-Kürzung dort mit anpassen. `main.js?v=N` bumpen.