The leagues, the polish round: the way-up ribbon, upright chevrons, the breathing badges, the card counts up

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-29 15:51:59 +02:00
1 parent 87ac3294bc
commit fa6db7f77b
4 files changed
+113 -42

No files matched your search

+43 -25
View File
@@ -209,8 +209,10 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
background: var(--solid);
padding-top: max(10px, env(safe-area-inset-top)); }
.hdr h2 { padding-left: 2px; }
/* UPRIGHT, not Bangers: the announcement face is drawn slanted, and a slanted arrow read as
italic on every screen. Silkscreen's ASCII < > are straight pixel arrows. */
.chev { width: 34px; height: 34px; flex: none; background: none; border: 2px solid var(--line);
color: var(--acid); font: 400 20px Bangers, cursive; line-height: 1; cursor: pointer;
color: var(--acid); font: 700 14px Silkscreen, monospace; line-height: 1; cursor: pointer;
-webkit-tap-highlight-color: transparent; }
/* the BODY COUNT headline row: three boxed figures sharing one frame */
.figs { display: flex; border: 2px solid var(--line); background: var(--panel); }
@@ -291,6 +293,13 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
#cdBadges { display: flex; gap: 10px; align-items: flex-end; }
#cdBadges img { height: 46px; image-rendering: pixelated; }
#cdBadges img.dark { filter: grayscale(1) brightness(0.3); }
/* earned badges BREATHE — a subtle gold halo, CSS-only (the menu parks the game loop, CSS
animations do not care). The dark slots stay dead, which is the point of them. */
#cdBadges img:not(.dark), #cdBadge {
animation: badgeGlow 2.6s ease-in-out infinite; }
@keyframes badgeGlow {
0%, 100% { filter: drop-shadow(0 0 2px rgba(232, 178, 58, 0.25)); }
50% { filter: drop-shadow(0 0 7px rgba(232, 178, 58, 0.75)); } }
.rkbadge { height: 15px; margin-right: 6px; image-rendering: pixelated; flex: none; }
/* THE LEVEL STRIP: the one meta number over a thin bar, riding the splash foot. The fill colour
is the acid accent for now; the leagues will hand it the skill colour in phase 3. */
@@ -301,8 +310,9 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
level+league over the bar, and the standing GOAL LINE ("win the X cup to advance"), with a
chevron that says it opens. The splash stays two buttons + one plaque; the badge COLLECTION
stays on the card, one (now discoverable) tap away. */
#plaque { display: flex; align-items: center; gap: 10px; width: 100%; margin: 0 0 10px;
padding: 8px 10px; background: var(--solid); border: 2px solid var(--line);
#plaque { display: flex; align-items: center; gap: 12px; width: 100%; margin: 0 0 10px;
padding: 12px 14px; background: var(--solid); border: 2px solid var(--line);
border-radius: 8px;
cursor: pointer; text-align: left; -webkit-tap-highlight-color: transparent; }
#plBadge { height: 34px; flex: none; image-rendering: pixelated; }
#plBody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
@@ -313,20 +323,27 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
letter-spacing: 1px; white-space: nowrap; }
#plGoal { font: 400 8px Silkscreen, monospace; font-style: normal; color: var(--acid);
letter-spacing: 1px; }
#plaque em { flex: none; font: 400 22px Bangers, cursive; font-style: normal; color: var(--acid); }
#plaque em { flex: none; font: 700 16px Silkscreen, monospace; font-style: normal; color: var(--acid); }
#plRow s, #cdBar s, #cdCareer s { flex: 1; height: 6px; background: rgba(255,255,255,0.10);
border: 1px solid var(--line); text-decoration: none; display: block; }
#cdBar s { height: 8px; }
#cdCareer { display: flex; align-items: center; gap: 8px; }
#cdCareer u { font: 700 8px Silkscreen, monospace; color: var(--dim); text-decoration: none;
letter-spacing: 1px; }
/* the mode picker's WAY-UP tag: the standing answer to "how do I advance", worn by the cup */
.way { position: absolute; top: 8px; left: 8px; padding: 3px 6px; font: 700 8px Silkscreen, monospace;
font-style: normal; color: var(--ink); background: var(--acid); letter-spacing: 1px;
text-decoration: none; }
/* the WAY-UP RIBBON: the standing answer to "how do I advance", worn as a banderole wrapped
over the poster's corner (Oliver: bigger, red, thick white type, light shadow). The poster
clips its ends diagonally (overflow hidden + the -45deg rotation), which is what sells the
wrap. */
.way { position: absolute; top: 20px; left: -42px; width: 150px; transform: rotate(-45deg);
padding: 6px 0; font: 700 11px Silkscreen, monospace; font-style: normal;
text-align: center; color: #fff; background: var(--red);
border-top: 1px solid rgba(255,255,255,0.35); border-bottom: 1px solid #69201c;
text-shadow: 1px 1px 0 rgba(0,0,0,0.55); box-shadow: 0 2px 6px rgba(0,0,0,0.4);
letter-spacing: 1px; text-decoration: none; }
#plRow s i, #cdBar s i, #cdCareer s i { display: block; height: 100%; width: 0%;
background: linear-gradient(90deg, #3f9560, var(--acid) 55%, #e8b23a);
transition: width 0.4s ease; }
#cdBar s i, #cdCareer s i { transition: width 0.7s cubic-bezier(0.2, 0.7, 0.3, 1); }
/* KIT and SKIN: a row of colour squares, under a label that names whatever is selected. That is
the whole UI — the pick has no meters and no art to show, and a labelled tile per colour would
be five times the screen for a choice you make by looking at the colour. */
@@ -391,7 +408,8 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
a live answer to the header's question; tapping answers and advances. The only memory shown is
the LAST tag — an indicator, not a selection. A border that means "SERVE UP will use this"
exists solely on the final screen, where a confirm button actually follows. */
#modeList button.mode, #modeSub button.mode { display: flex; align-items: flex-end; width: 100%; min-height: 96px;
#modeList button.mode, #modeSub button.mode { overflow: hidden; /* clips the WAY-UP ribbon's ends */
display: flex; align-items: flex-end; width: 100%; min-height: 96px;
margin: 0 0 8px; padding: 7px 9px; background-size: cover; background-position: center bottom;
image-rendering: pixelated; border: 2px solid var(--line); color: inherit; font: inherit;
text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
@@ -425,8 +443,8 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
#courtList img { width: 92px; height: 66px; display: block; image-rendering: pixelated; flex: none;
object-fit: cover; }
#courtList .cb, #modeList .cb, #modeSub .cb { flex: 1; min-width: 0; }
#courtList b, #modeList b, #modeSub b { display: block; font: 400 12px Silkscreen, monospace; color: var(--cream); letter-spacing: 0.5px; }
#courtList i, #modeList i, #modeSub i { display: block; margin: 3px 0 6px; font: 400 9px Silkscreen, monospace; color: var(--dim); font-style: normal; }
#courtList b, #modeList b, #modeSub b { display: block; font: 700 15px Silkscreen, monospace; color: var(--cream); letter-spacing: 0.5px; }
#courtList i, #modeList i, #modeSub i { display: block; margin: 4px 0 6px; font: 400 10px Silkscreen, monospace; color: #c9c4b8; font-style: normal; }
/* The two meters read straight off the court's own bounce numbers (see main.js), so they cannot
drift away from what the ball actually does on it. */
.meter { display: flex; align-items: center; gap: 6px; margin-top: 4px;
@@ -587,7 +605,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
<span id="plRow"><u id="plLv">LEVEL 1</u><s><i id="plFill"></i></s></span>
<i id="plGoal"></i>
</span>
<em>&rsaquo;</em>
<em>&gt;</em>
</button>
<button class="btn big" id="mStart">PLAY MATCH</button>
<div class="two">
@@ -605,15 +623,15 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
who-and-where with SERVE UP. Every step comes up pre-selected from last time, so a
returning player is three taps from serving. -->
<div id="stmode">
<div class="hdr"><button class="chev" id="mBack">&lsaquo;</button><h2>PICK A GAME</h2></div>
<div class="hdr"><button class="chev" id="mBack">&lt;</button><h2>PICK A GAME</h2></div>
<div id="modeList"></div>
</div>
<div id="stsub">
<div class="hdr"><button class="chev" id="subBack">&lsaquo;</button><h2 id="subTitle">DIFFICULTY</h2></div>
<div class="hdr"><button class="chev" id="subBack">&lt;</button><h2 id="subTitle">DIFFICULTY</h2></div>
<div id="modeSub"></div>
</div>
<div id="setup">
<div class="hdr"><button class="chev" id="sBack">&lsaquo;</button><h2>MATCH SETUP</h2></div>
<div class="hdr"><button class="chev" id="sBack">&lt;</button><h2>MATCH SETUP</h2></div>
<!-- ONE PLAYER BLOCK. It was four stacked sections — two big portrait tiles, then the live
player, then a kit row, then a skin row — which said the same thing twice (the tiles were
a second, FRONT-FACING drawing of a man who does not react to any of the colours) and ran
@@ -644,7 +662,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
keys), so the two screens cannot disagree about who you are. Rebuilt on every visit by
main.buildPlayer, so the .on states track the setup screen's. -->
<div id="player">
<div class="hdr"><button class="chev" id="pBack">&lsaquo;</button><h2>YOUR PLAYER</h2></div>
<div class="hdr"><button class="chev" id="pBack">&lt;</button><h2>YOUR PLAYER</h2></div>
<h3>NAME<span>YOURS ON EVERY BOARD</span></h3>
<div id="nameRow">
<input id="pName" class="tin" maxlength="12" autocomplete="off" spellcheck="false">
@@ -684,7 +702,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
main.buildCard from the profile + stats; the honours row shows every badge slot, earned
bright and unearned dark. -->
<div id="card">
<div class="hdr"><button class="chev" id="cdBack">&lsaquo;</button><h2 id="cdName">YOUR PLAYER</h2></div>
<div class="hdr"><button class="chev" id="cdBack">&lt;</button><h2 id="cdName">YOUR PLAYER</h2></div>
<div id="cdTop">
<canvas id="cdLook" width="96" height="224"></canvas>
<div id="cdMeta">
@@ -710,7 +728,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
(main.js hands the settled rungs to startMatch). The rows are built in main.js from
config.CUP_ROUNDS, so a round added there needs no markup. -->
<div id="bracket">
<div class="hdr"><button class="chev" id="brBack">&lsaquo;</button><h2>THE DRAW</h2></div>
<div class="hdr"><button class="chev" id="brBack">&lt;</button><h2>THE DRAW</h2></div>
<h3>FOUR ROUNDS<span id="brState">DRAWING…</span></h3>
<div id="cupRows"></div>
<div class="foot">
@@ -721,7 +739,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
<!-- THE TRAINING COURT's opponent picker: the whole ladder face-up, tap a body to drill it.
Built in main.js from config's roster (matchFoe/LADDER), so a new rung needs no markup. -->
<div id="train">
<div class="hdr"><button class="chev" id="trBack">&lsaquo;</button><h2>TRAINING</h2></div>
<div class="hdr"><button class="chev" id="trBack">&lt;</button><h2>TRAINING</h2></div>
<h3>PICK YOUR OPPONENT<span>ONE GAME · GO AGAIN</span></h3>
<div id="trainList"></div>
</div>
@@ -730,7 +748,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
a socket is open, so the room code and its SHARE button stand where the buttons were and
the game stays parked behind this overlay until the room fills (main.js, THE LOBBY). -->
<div id="vs">
<div class="hdr"><button class="chev" id="vsBack">&lsaquo;</button><h2>VS A FRIEND</h2></div>
<div class="hdr"><button class="chev" id="vsBack">&lt;</button><h2>VS A FRIEND</h2></div>
<div id="vsPick">
<h3>01 · YOUR NAME<span>ON THEIR SCOREBOARD</span></h3>
<input id="vsName" class="tin" maxlength="12" placeholder="YOUR NAME" autocomplete="off"
@@ -758,7 +776,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
</div>
<!-- BODY COUNT. Every row in here is built by stats.js from the run records in localStorage. -->
<div id="stats">
<div class="hdr"><button class="chev" id="stBack">&lsaquo;</button><h2 id="stTitle">BODY COUNT</h2></div>
<div class="hdr"><button class="chev" id="stBack">&lt;</button><h2 id="stTitle">BODY COUNT</h2></div>
<!-- WHICH MODE, then which period. The three modes do not measure the same thing (see the
record-shape note in stats.endRun), so this strip changes the whole screen under it —
the title, the three figures, the roster's heading and the footer. -->
@@ -783,7 +801,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
a ranking. Same tab strip as the stats screen's, and stats.drawRanks rewrites the column
heading with the tab, because the number means something different on each one. -->
<div id="ranks">
<div class="hdr"><button class="chev" id="rkBack">&lsaquo;</button><h2>WORLD BOARD</h2></div>
<div class="hdr"><button class="chev" id="rkBack">&lt;</button><h2>WORLD BOARD</h2></div>
<div class="tabs" id="rkModes">
<button class="on" data-m="g">SPLATTER</button>
<button data-m="z">TENNIS</button>
@@ -796,7 +814,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
at it, which is the whole point: it replaced a triple-tap on the in-play scoreboard that
players kept tripping by accident. -->
<div id="secret">
<div class="hdr"><button class="chev" id="scBack">&lsaquo;</button><h2>BACK ROOM</h2></div>
<div class="hdr"><button class="chev" id="scBack">&lt;</button><h2>BACK ROOM</h2></div>
<div class="foot bare">
<button class="btn big" id="scWeapons">WEAPON TEST</button>
<button class="btn big" id="scBosses">BOSS TEST</button>
@@ -808,7 +826,7 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
<!-- one tile per chapter boss, built from config.bossArt in main.js: adding a design to the
roster puts it on this screen and never touches this markup -->
<div id="bosses">
<div class="hdr"><button class="chev" id="bsBack">&lsaquo;</button><h2>BOSS TEST</h2></div>
<div class="hdr"><button class="chev" id="bsBack">&lt;</button><h2>BOSS TEST</h2></div>
<h3>PICK A CHAPTER<span>FULL ARSENAL</span></h3>
<div id="bossList"></div>
</div>
@@ -828,6 +846,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
</div>
</div>
</div>
<script type="module" src="src/main.js?v=316"></script>
<script type="module" src="src/main.js?v=317"></script>
</body>
</html>