diff --git a/assets/menu_bg.png b/assets/menu_bg.png index ced0e2e..9426b91 100644 Binary files a/assets/menu_bg.png and b/assets/menu_bg.png differ diff --git a/index.html b/index.html index cb008bd..ffa82d1 100644 --- a/index.html +++ b/index.html @@ -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 @@
-
-
BEST RUN—
-
WORLD RANK—
-

Fifteen. Thirty. Forty. Dead.

@@ -311,6 +309,6 @@
- + diff --git a/main.js b/main.js index 2876e5c..3d87ce4 100644 --- a/main.js +++ b/main.js @@ -111,13 +111,12 @@ function summary() { + `${best ? best + (best === 1 ? ' GAME' : ' GAMES') + ' TO BEAT' : 'FIRST RUN'}`; } -// 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(); } diff --git a/render.js b/render.js index 916d599..599c8b1 100644 --- a/render.js +++ b/render.js @@ -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 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