diff --git a/fktry/src/ui/buildbar.ts b/fktry/src/ui/buildbar.ts index dd151ec..361d6f7 100644 --- a/fktry/src/ui/buildbar.ts +++ b/fktry/src/ui/buildbar.ts @@ -115,6 +115,9 @@ export function createBuildBar(data: GameData, sel: BuildSelection): BuildBar { function render() { buildButtons(pages[active]); for (let i = 0; i < tabs.length; i++) cls(tabs[i], 'is-active', i === active); + // The strip scrolls, so paging with [ ] must bring its tab into view or the player + // pages blind. `nearest` avoids yanking the strip when the tab is already visible. + tabs[active]?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' }); sync(); } diff --git a/fktry/src/ui/style.ts b/fktry/src/ui/style.ts index 796902f..9bbd690 100644 --- a/fktry/src/ui/style.ts +++ b/fktry/src/ui/style.ts @@ -125,8 +125,30 @@ const CSS = ` justify-content: center; } -/* Kind tabs. A page holds at most nine so 1-9 always reaches every machine. */ -.fk-tabs { display: flex; gap: 3px; margin-bottom: 5px; flex-wrap: wrap; justify-content: center; } +/* Kind tabs. A page holds at most nine so 1-9 always reaches every machine. + v5: a single scrolling strip rather than a wrapping row — the catalog keeps growing + (RESEARCH joined in v4 and threw REMOVE onto a line of its own), and tabs jumping to a + second row moves every other tab under the player's cursor. */ +.fk-tabs { + display: flex; gap: 3px; margin-bottom: 5px; + flex-wrap: nowrap; + overflow-x: auto; + overflow-y: hidden; + max-width: 100%; + scrollbar-width: thin; + scrollbar-color: var(--fk-line) transparent; + /* The scrollbar gets its OWN lane below the buttons. With too little padding it is + painted over the lower half of every tab and swallows clicks there — the exact + "clicking POWER does nothing" bug from review, which is why keyboard paging still + worked. Must stay >= the scrollbar height below. */ + padding-bottom: 7px; +} +.fk-tabs::-webkit-scrollbar { height: 4px; } +.fk-tabs::-webkit-scrollbar-track { background: transparent; } +.fk-tabs::-webkit-scrollbar-thumb { background: var(--fk-line); } +.fk-tabs::-webkit-scrollbar-thumb:hover { background: var(--fk-dim); } +/* Tabs must not shrink into unreadable slivers when the strip overflows. */ +.fk-tab { flex: 0 0 auto; } .fk-tab { background: var(--fk-bg-solid); border: 1px solid var(--fk-line); @@ -138,9 +160,8 @@ const CSS = ` .fk-tab:hover { color: var(--fk-fg); border-color: var(--fk-cool); } .fk-tab.is-active { color: var(--fk-cool); border-color: var(--fk-cool); background: #0c1a1a; } -/* REMOVE is a mode, not a machine — it sits with the tabs and reads as a hazard. - No auto margin: with the RESEARCH tab added the row wraps, and an auto margin threw - REMOVE onto a line of its own. */ +/* REMOVE is a mode, not a machine — it sits at the end of the strip and reads as a + hazard. It scrolls with the tabs; the hotkey (X) is always available regardless. */ .fk-tab-remove { margin-left: 10px; color: var(--fk-dim); } .fk-tab-remove:hover { color: var(--fk-red); border-color: var(--fk-red); } .fk-tab-remove.is-armed { @@ -227,6 +248,11 @@ const CSS = ` .fk-ins-heat-state { font-size: 10px; color: var(--fk-amber); margin-top: 3px; } .fk-ins-heat-state.is-scram { color: var(--fk-red); font-weight: bold; } +/* MOSQUITO EXPOSURE — wildlife is a hazard, not a fault: amber, quiet, matter-of-fact. */ +.fk-ins-exposure { display: none; margin-top: 6px; } +.fk-ins-exposure.is-on { display: block; } +.fk-ins-exposure-pct { color: var(--fk-amber); font-size: 10px; } + /* Process select: a native