Redraw the backdrop and cool the menus down
The court art had three faults: a hard-edged pale panel sitting on the near half, mown stripes that stopped and restarted where the court began, and the blood pool stranded behind the baseline where a portrait phone cropped it off. Redrawn with the stripes running unbroken the full depth and the stain out on the court where it can be seen. The picture also read as zoomed in, which was not the art but the crop: at 4:6 `cover` was throwing away a third of the width on a phone. The sky is extended to 1:2 instead, so the whole arena survives the crop. Menu palette rotated off olive towards a cool desaturated blue-green with much quieter borders, and the boxed BEST RUN / WORLD RANK bar is gone from the splash — it sat on the best part of the art to say what the two buttons under it already said. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f3b0b791d6
commit
c5edc94b73
4 files changed
+31
-34
No files matched your search
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 107 KiB |
+25
-27
@@ -66,11 +66,15 @@
|
||||
/* ---- THE MENU PALETTE. The canvas HUD keeps its own blue-grey and gold; the menus are
|
||||
deliberately a different room — dark court green, one acid accent, and one crimson reserved for
|
||||
the thing you actually came here to press. Five names so a repaint is five edits, not fifty. */
|
||||
:root { --ink:#0d1408; --panel:rgba(30,45,18,0.55); --line:#3e5a26; --acid:#c8e04a;
|
||||
--cream:#dfe6c9; --dim:#7f9160; --red:#b3201c; --bad:#e0564a;
|
||||
/* COOL, not olive. The first pass sat around hue 80 — yellow-green — and read loud and warm next
|
||||
to the art. These are the same idea rotated towards blue-green and desaturated: the field is
|
||||
nearly neutral, the borders are quiet, and the acid accent is the only thing allowed to be
|
||||
bright, so it still means "this is the number / this is selected". */
|
||||
:root { --ink:#0e1714; --panel:rgba(42,62,54,0.42); --line:#2c4238; --acid:#bcd455;
|
||||
--cream:#ccd7ca; --dim:#6c7f74; --red:#a4231f; --bad:#cf5a4e;
|
||||
/* the same green as --panel over --ink, but OPAQUE: the pinned header and footer have
|
||||
content scrolling underneath them and a translucent bar shows it through */
|
||||
--solid:#161f0d; }
|
||||
--solid:#16211d; }
|
||||
/* touch-action is pan-y, not none: the BODY COUNT list is taller than a phone and has to scroll.
|
||||
align-content is `safe center` rather than place-items — plain centring on a grid whose item
|
||||
overflows pushes the top of the item out of the scrollport, where nothing can reach it. */
|
||||
@@ -80,8 +84,12 @@
|
||||
/* the SPLASH sits on the court; the other stages are lists and want a flat field behind them.
|
||||
The art is dimmed here rather than baked dark, so the same file can be reused at another level. */
|
||||
#intro[data-stage="splash"] {
|
||||
background: linear-gradient(rgba(9,14,7,0.55), rgba(9,14,7,0.88)),
|
||||
url('assets/menu_bg.png?v=85') 50% 46% / cover no-repeat var(--ink);
|
||||
/* Three stops, not two. The logo needs a dark, quiet band behind it and the blood needs to be
|
||||
READABLE, and those pull opposite ways — so the scrim is heaviest at the top, lifts over the
|
||||
court where the stain is, and comes back down only far enough to seat the buttons. */
|
||||
background: linear-gradient(rgba(12,20,17,0.60) 0%, rgba(12,20,17,0.30) 46%,
|
||||
rgba(12,20,17,0.58) 100%),
|
||||
url('assets/menu_bg.png?v=86') 50% 42% / cover no-repeat var(--ink);
|
||||
image-rendering: pixelated; }
|
||||
#intro .card { font: 400 11px Silkscreen, monospace; color: var(--cream); text-align: center;
|
||||
line-height: 1.9; padding: 16px 14px 20px; width: min(370px, 94vw);
|
||||
@@ -100,10 +108,10 @@
|
||||
#intro .logo { display: block; width: min(300px, 74vw); height: auto; margin: 0 auto 6px;
|
||||
image-rendering: pixelated; filter: drop-shadow(0 3px 0 rgba(0,0,0,0.45)); }
|
||||
/* the slogan carries most of the gap between the logo's blood and the first button */
|
||||
#intro .slogan { margin: 8px 0 20px; font-size: 11px; color: #b6a0a4; letter-spacing: 0.5px; }
|
||||
#intro .slogan { margin: 8px 0 20px; font-size: 11px; color: #a8a09e; letter-spacing: 0.5px; }
|
||||
#intro .slogan b { color: #c9564a; }
|
||||
/* which build is served — filled in by main.js, empty (and invisible) if the header is missing */
|
||||
#build { margin: 14px 0 0; font-size: 9px; color: #4d5a3c; letter-spacing: 0.5px; }
|
||||
#build { margin: 14px 0 0; font-size: 9px; color: #4a5a52; letter-spacing: 0.5px; }
|
||||
/* ONE overlay, four panels, swapped by #intro[data-stage]. Keeping the backdrop and the card frame
|
||||
alive across every screen is the point — a screen that unmounts the whole overlay flashes the
|
||||
court through between menus. */
|
||||
@@ -134,15 +142,9 @@
|
||||
.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;
|
||||
-webkit-tap-highlight-color: transparent; }
|
||||
/* the splash stat bar and the BODY COUNT headline row: boxed figures sharing one frame */
|
||||
.bar, .figs { display: flex; border: 2px solid var(--line); background: var(--panel); }
|
||||
/* the bar is the splash's top band — pinned like a header, and the safe-area inset is its job
|
||||
because nothing else on that screen sits above it */
|
||||
.bar { flex: none; margin-top: max(12px, env(safe-area-inset-top)); }
|
||||
.bar .fig { padding: 6px 10px; text-align: left; }
|
||||
.figs .fig { padding: 7px 6px; text-align: center; }
|
||||
.fig { flex: 1; min-width: 0; }
|
||||
.bar .fig + .fig { text-align: right; }
|
||||
/* the BODY COUNT headline row: three boxed figures sharing one frame */
|
||||
.figs { display: flex; border: 2px solid var(--line); background: var(--panel); }
|
||||
.fig { flex: 1; min-width: 0; padding: 7px 6px; text-align: center; }
|
||||
.fig + .fig { border-left: 2px solid var(--line); }
|
||||
.fig b { display: block; font: 400 8px Silkscreen, monospace; color: var(--dim); letter-spacing: 1px; }
|
||||
.fig i { display: block; font: 700 15px Silkscreen, monospace; color: var(--acid);
|
||||
@@ -156,7 +158,7 @@
|
||||
/* The generated button plates are gone with the tutorial. The primary is a flat crimson block
|
||||
now: it survives being resized, which a fixed-size plate crop never did, and the mockup's
|
||||
hierarchy is carried by colour rather than by art. */
|
||||
.btn.big { padding: 13px 10px 11px; background: var(--red); border-color: #6d1310; color: #f6e7dc;
|
||||
.btn.big { padding: 13px 10px 11px; background: var(--red); border-color: #69201c; color: #f6e7dc;
|
||||
font: 400 26px Bangers, cursive; letter-spacing: 2px; line-height: 1.15; }
|
||||
.two { display: flex; gap: 8px; }
|
||||
.two .btn { margin-bottom: 0; }
|
||||
@@ -168,7 +170,7 @@
|
||||
#tours button { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||||
padding: 8px 6px 10px; background: var(--panel); border: 2px solid var(--line);
|
||||
color: inherit; font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
||||
#tours button.on { border-color: var(--acid); background: rgba(200,224,74,0.10); }
|
||||
#tours button.on { border-color: var(--acid); background: rgba(188,212,85,0.09); }
|
||||
#tours img { height: 88px; width: auto; display: block; image-rendering: pixelated; }
|
||||
#tours b { font: 400 10px Silkscreen, monospace; color: var(--cream); letter-spacing: 0.5px; }
|
||||
/* a row, not a stacked tile: three 3:2 tiles stacked fill a phone twice over, and the art still
|
||||
@@ -177,7 +179,7 @@
|
||||
margin: 0 0 8px; padding: 7px; background: var(--panel);
|
||||
border: 2px solid var(--line); color: inherit; font: inherit; text-align: left;
|
||||
cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
||||
#courtList button.court.on { border-color: var(--acid); background: rgba(200,224,74,0.10); }
|
||||
#courtList button.court.on { border-color: var(--acid); background: rgba(188,212,85,0.09); }
|
||||
#courtList img { width: 92px; height: 66px; display: block; image-rendering: pixelated; flex: none;
|
||||
object-fit: cover; }
|
||||
#courtList .cb { flex: 1; min-width: 0; }
|
||||
@@ -190,7 +192,7 @@
|
||||
.meter u { flex: none; width: 40px; text-decoration: none; }
|
||||
.meter s { flex: 1; height: 5px; background: rgba(0,0,0,0.45); text-decoration: none; display: block; }
|
||||
.meter s > i { display: block; height: 100%; background: var(--acid); }
|
||||
.meter.b s > i { background: #5aa9d0; }
|
||||
.meter.b s > i { background: #4e8fa6; }
|
||||
/* the commit strip. #intro is the scrollport, so sticky resolves against it and the button stays
|
||||
reachable however long the list above grows. */
|
||||
/* margin-top:auto pins it to the BOTTOM of a short screen; sticky keeps it there on a long one */
|
||||
@@ -211,7 +213,7 @@
|
||||
/* one opponent. The sprite is the walk frame the game already draws, sat on its own baseline. */
|
||||
.zrow { display: flex; align-items: center; gap: 10px; padding: 7px 9px; margin-bottom: 6px;
|
||||
border: 2px solid var(--line); background: var(--panel); text-align: left; }
|
||||
.zrow.boss { border-color: #7d2420; background: rgba(120,26,22,0.28); }
|
||||
.zrow.boss { border-color: #6e2a24; background: rgba(110,42,36,0.22); }
|
||||
.zrow img { width: 34px; height: 46px; object-fit: contain; object-position: bottom;
|
||||
image-rendering: pixelated; flex: none; }
|
||||
.zb { flex: 1; min-width: 0; display: grid; grid-template-columns: 1fr auto; gap: 0 8px;
|
||||
@@ -239,7 +241,7 @@
|
||||
#stFoot i { font: 700 13px Silkscreen, monospace; color: var(--acid); font-style: normal; }
|
||||
.rkrow { display: flex; align-items: center; gap: 10px; padding: 8px 10px; margin-bottom: 5px;
|
||||
border: 2px solid var(--line); background: var(--panel); text-align: left; }
|
||||
.rkrow.me { border-color: var(--acid); background: rgba(200,224,74,0.12); }
|
||||
.rkrow.me { border-color: var(--acid); background: rgba(188,212,85,0.11); }
|
||||
.rkrow b { flex: none; width: 60px; font: 700 10px Silkscreen, monospace; color: var(--dim); }
|
||||
.rkrow.me b { color: var(--acid); }
|
||||
.rkrow span { flex: 1; min-width: 0; font: 400 9px Silkscreen, monospace; color: var(--cream);
|
||||
@@ -258,10 +260,6 @@
|
||||
<!-- three bands: the figures at the top, the title floating in the middle, the buttons under
|
||||
your thumb. Same anchoring as every other screen. -->
|
||||
<div id="splash">
|
||||
<div class="bar">
|
||||
<div class="fig"><b>BEST RUN</b><i id="sBest">—</i></div>
|
||||
<div class="fig"><b>WORLD RANK</b><i id="sRank">—</i></div>
|
||||
</div>
|
||||
<div class="mid">
|
||||
<img class="logo" src="assets/logo.png" alt="Splatter Tennis">
|
||||
<p class="slogan">Fifteen. Thirty. Forty. <b>Dead.</b></p>
|
||||
@@ -311,6 +309,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="main.js?v=122"></script>
|
||||
<script type="module" src="main.js?v=123"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -111,13 +111,12 @@ function summary() {
|
||||
+ `<span>${best ? best + (best === 1 ? ' GAME' : ' GAMES') + ' TO BEAT' : 'FIRST RUN'}</span>`;
|
||||
}
|
||||
|
||||
// the splash figures. Re-read every time the splash comes up, because a run has just ended and
|
||||
// both numbers have moved.
|
||||
// The splash carries ONE number, on the STATS button. A boxed BEST RUN / WORLD RANK bar across the
|
||||
// top came out of the mockup and went again: it sat on the best part of the art and said twice what
|
||||
// the two buttons underneath already say. Re-read whenever the splash comes back up, because a run
|
||||
// has just ended and the count has moved.
|
||||
function refreshSplash() {
|
||||
const { kills, best } = stats.total();
|
||||
document.getElementById('sBest').textContent = best ? best + (best === 1 ? ' GAME' : ' GAMES') : '—';
|
||||
document.getElementById('sRank').textContent = '#' + stats.rank(kills).toLocaleString('en-GB');
|
||||
document.getElementById('mKills').textContent = kills.toLocaleString('en-GB') + ' KILLS';
|
||||
document.getElementById('mKills').textContent = stats.total().kills.toLocaleString('en-GB') + ' KILLS';
|
||||
summary();
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ function drawRunOff() {
|
||||
// Asset cache-buster — bump with main.js?v=N (see the img.src note below). EXPORTED because the
|
||||
// menu screens hang <img> tags on the same files, and two copies of this number is one copy too
|
||||
// many: the one that does not get bumped is the one that serves a cached 404.
|
||||
export const SPRV = 85;
|
||||
export const SPRV = 86;
|
||||
const SPR = {};
|
||||
for (const n of ['p_idle1','p_idle2','p_bounce','p_bounce2','p_toss','p_smash','p_run1','p_run2','p_run3','p_run4','p_swing1','p_swing2','p_die1','p_die2','p_die3','p_die4','p_die5',
|
||||
'p_pull', // the serve wind-up: the racket dropping behind him while the pull is drawn
|
||||
|
||||
Reference in new issue
Block a user