diff --git a/frontend/index.html b/frontend/index.html
index c012096..b612a88 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -683,14 +683,13 @@
RAM operations perform atomic GlobalPlatform commands over SCP80. Card keys are taken from the saved preset.
-
-
-
-
-
+
+
+
+
-
@@ -1747,7 +1746,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
-const SIMPLE_VERSION = '3.6.45';
+const SIMPLE_VERSION = '3.6.46';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
diff --git a/frontend/sw.js b/frontend/sw.js
index c0f774f..b274058 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v318';
+const CACHE = 'simple-v319';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/html.test.js b/frontend/tests/html.test.js
index 7bb947b..5d1ee68 100644
--- a/frontend/tests/html.test.js
+++ b/frontend/tests/html.test.js
@@ -448,3 +448,41 @@ test('every data-l10n attribute resolves in LANG_RU after HTML decoding', () =>
const missing = [...new Set([...attrs, ...titles].filter(k => !(k in dict)))];
assert.deepStrictEqual(missing, [], 'data-l10n keys with no LANG_RU entry:\n' + missing.join('\n'));
});
+
+test('every tab-content lives inside the max-w-7xl page container', () => {
+ // SCP81 and Cards used to sit outside the container (its closing
+ // came before them), so they stretched to the whole window while the
+ // other tabs were capped - v3.6.46.
+ const markup = htmlOnly(html);
+ const re = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*?)>/g;
+ let m;
+ let depth = 0;
+ let containerDepth = null;
+ const seen = new Set();
+ while ((m = re.exec(markup))) {
+ const closing = m[1] === '/';
+ const tag = m[2].toLowerCase();
+ const attrs = m[3] || '';
+ const selfClosing = attrs.trim().endsWith('/');
+ if (VOID_TAGS.has(tag) || selfClosing) continue;
+ if (closing) {
+ if (containerDepth !== null && depth === containerDepth) containerDepth = null;
+ depth--;
+ continue;
+ }
+ if (tag === 'div' && /class="[^"]*max-w-7xl/.test(attrs)) containerDepth = depth + 1;
+ if (tag === 'div' && /class="[^"]*tab-content/.test(attrs)) {
+ const id = (/id="([^"]+)"/.exec(attrs) || [])[1];
+ assert.notStrictEqual(containerDepth, null,
+ id + ' must be inside the max-w-7xl page container');
+ seen.add(id);
+ }
+ depth++;
+ }
+ assert.strictEqual(seen.size, 7, 'expected all seven tabs, saw ' + seen.size);
+});
+
+test('the RAM form shows Card preset and Operation in one row', () => {
+ const row = /
\s*
\s*