diff --git a/index.html b/index.html index 66ed417..5d36856 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - + @@ -165,6 +165,6 @@ - + diff --git a/nginx.conf b/nginx.conf index 68b99f2..701d72e 100644 --- a/nginx.conf +++ b/nginx.conf @@ -8,12 +8,15 @@ server { gzip_types text/css application/javascript image/svg+xml; gzip_min_length 256; - # Static assets: cache for a week (filenames are stable for this app). + # Assets use stable filenames and we redeploy in place, so revalidate on + # every load (cheap — tiny files, 304s) instead of caching for a week. + # A week-long cache here means redeploys leave returning players on stale + # CSS/JS until expiry; no-cache avoids that footgun. location ~* \.(css|js|svg|png|ico)$ { add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Cache-Control "public, max-age=604800"; + add_header Cache-Control "no-cache" always; } # HTML entry point: always revalidate so redeploys show up immediately. diff --git a/script.js b/script.js index fc31e6e..ff6a8ad 100644 --- a/script.js +++ b/script.js @@ -442,12 +442,17 @@ function fitActiveScreen() { const card = screen.querySelector('.card'); if (!card) return; // offsetWidth/Height ignore transforms, so we always read the natural size. + // offsetWidth/Height are layout sizes (they ignore transforms), so we + // always measure the card's natural size even after a prior scale. const w = card.offsetWidth; const h = card.offsetHeight; if (!w || !h) return; const margin = 16; // small breathing room around the card const scale = Math.min(1, (window.innerWidth - margin) / w, (window.innerHeight - margin) / h); - card.style.setProperty('--fit', String(scale)); + // Fold the fit scale into the centering translate. The card is position:fixed + // at top/left 50%, so translate(-50%,-50%) centers it on the viewport and the + // scale shrinks it to fit — together, no scrolling at any size. + card.style.transform = `translate(-50%, -50%) scale(${scale})`; } window.addEventListener('resize', scheduleFit); window.addEventListener('orientationchange', scheduleFit); diff --git a/style.css b/style.css index 24f04c5..06cf8d3 100644 --- a/style.css +++ b/style.css @@ -113,7 +113,7 @@ /* ---------- Base layout (full-screen, never scroll) ---------- The viewport is locked to the screen; the active card is scaled by JS (--fit) to always fit. It's tic-tac-toe — everything stays in view. */ -html { height: 100%; } +html { height: 100%; overflow: hidden; } body { font-family: var(--body-font); background: var(--bg); @@ -219,12 +219,14 @@ body { } /* ---------- Cards & screens ---------- */ -.screen { display: none; animation: fadeIn 0.5s ease; } +/* Opacity-only fade: a transform on .screen would become the containing block + for the fixed-positioned card and break its viewport centering mid-animation. */ +.screen { display: none; animation: fadeIn 0.4s ease; } .screen.active { display: block; } @keyframes fadeIn { - from { opacity: 0; transform: scale(0.96) translateY(10px); } - to { opacity: 1; transform: scale(1) translateY(0); } + from { opacity: 0; } + to { opacity: 1; } } .card { @@ -234,15 +236,26 @@ body { backdrop-filter: blur(10px); border: 3px solid var(--surface-border); transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease; - /* Scaled to fit the viewport by fitActiveScreen() in script.js */ - transform: scale(var(--fit, 1)); + /* Fixed + viewport-centered so a card taller than the screen still centers + (grid centering pins an over-tall item to the top). fitActiveScreen() in + script.js folds the fit scale into the transform: + translate(-50%, -50%) scale(fit) + so the whole card always fits with no scrolling, at any window size. */ + position: fixed; + top: 50%; + left: 50%; + width: min(var(--maxw), calc(100vw - 16px)); + transform: translate(-50%, -50%); transform-origin: center center; will-change: transform; + z-index: 2; } .name-card { padding: 30px 30px 34px; } .game-card { padding: 36px; } -.winner-card { padding: 56px 40px; text-align: center; position: relative; overflow: hidden; } +/* No position override here — inherit .card's position:fixed so the card stays + viewport-centered. fixed is still a positioning context for .celebration. */ +.winner-card { padding: 56px 40px; text-align: center; overflow: hidden; } /* ---------- Headings ---------- */ .title, .game-title, .winner-title {