diff --git a/public_html/README.md b/public_html/README.md
index 75fa9f6..945d273 100644
--- a/public_html/README.md
+++ b/public_html/README.md
@@ -60,6 +60,13 @@ except installation and the service worker.
it paused — everything in there resets the clocks anyway — while the score
sheet picks the turn back up when you close it, so glancing at the score
doesn't cost you a tap on play.
+- **Settings are only reachable before a match starts.** Every control in that
+ sheet resets both clocks, so the gear disappears the moment the first tap
+ starts the clock and comes back after a reset. Its space in the bar is kept
+ empty, so nothing else shifts. That means a mistake in the time or delay can
+ only be fixed by resetting and starting the match again — deliberately, since
+ the alternative is wiping two live clocks by accident. Correcting a clock
+ mid-match is what the score sheet is for.
Backgammon clocks pause between games, and after a cocked die the delay is
normally restarted — use pause and reset for those.
diff --git a/public_html/index.html b/public_html/index.html
index b231531..0244774 100644
--- a/public_html/index.html
+++ b/public_html/index.html
@@ -165,6 +165,10 @@
.btn svg{width:clamp(23px,6.4vw,32px);height:auto;display:block}
.btn svg[hidden]{display:none}
.btn[hidden]{display:none} /* .btn's own display would otherwise win */
+ /* not [hidden]: that would collapse the slot and shift the whole bar. This
+ keeps the space, and still drops the button out of the tab order, the
+ accessibility tree and the path of a stray tap. */
+ .btn--vacant{visibility:hidden}
.btn:focus-visible{outline:2px solid var(--icon-hot);outline-offset:-6px;border-radius:8px}
/* reset armed for a second tap — the ring unwinds over the same 2s as the timeout */
@@ -591,6 +595,7 @@
var btnSound = document.getElementById("sound");
var btnReset = document.getElementById("reset");
var btnScore = document.getElementById("score");
+ var btnSettings = document.getElementById("settings");
var sheet = document.getElementById("sheet");
var scoreSheet = document.getElementById("scoresheet");
var scrim = document.getElementById("scrim");
@@ -681,6 +686,18 @@
return Math.max(0, st.reserve[i] - consumed(i));
}
+ /* Every timer control in the settings resets both clocks, so the sheet is only
+ safe to reach in exactly the state resetAll() leaves behind. Checking the
+ phase alone would not do: a score change runs newGame(), which returns to
+ IDLE waiting for the next game's first tap, with the match still very much
+ on. The score and reserve checks are what cover that. */
+ function matchPristine(){
+ return st.phase === IDLE && st.active === -1 &&
+ st.moves[0] === 0 && st.moves[1] === 0 &&
+ st.score[0] === 0 && st.score[1] === 0 &&
+ st.reserve[0] === matchTime() && st.reserve[1] === matchTime();
+ }
+
/* ---- write only on change ----
render() runs 60 times a second but the digits turn over once a second, and
.time is a ~190px glyph run: rewriting its text node every frame forces a
@@ -735,6 +752,10 @@
(st.phase === FLAG && i === st.active) ? "flagged"
: (i === st.active && st.phase !== IDLE) ? "active" : "idle");
}
+
+ // here rather than in the mutators, so it can never go stale; the
+ // two-argument toggle leaves the DOM alone when the state already matches
+ btnSettings.classList.toggle("btn--vacant", !matchPristine());
}
function loop(){ render(); requestAnimationFrame(loop); }
@@ -1026,7 +1047,10 @@
}
function closeSheet(){ openSheet(null); }
- document.getElementById("settings").addEventListener("click", function(){
+ btnSettings.addEventListener("click", function(){
+ // the CSS only hides the button; the rule itself lives here, so no route in
+ // can reach the timer controls once the match is under way
+ if(!matchPristine()) return;
pauseNow(); // nobody's clock runs while a sheet is open
disarm();
paintSettings();
diff --git a/public_html/sw.js b/public_html/sw.js
index 61214cc..ddf8fef 100644
--- a/public_html/sw.js
+++ b/public_html/sw.js
@@ -1,5 +1,5 @@
/* Cache-first: once installed the clock never touches the network again. */
-const CACHE = "bgclock-v7";
+const CACHE = "bgclock-v8";
const FILES = [
"./",
"./index.html",