diff --git a/frontend/assets.json b/frontend/assets.json
index e845dec..253339a 100644
--- a/frontend/assets.json
+++ b/frontend/assets.json
@@ -8,6 +8,7 @@
"/styles.css",
"/service-ui.css",
"/refresh.css",
+ "/layout-base.css",
"/recommendations.css",
"/flag-ui.js",
"/async-guard.js",
diff --git a/frontend/index.html b/frontend/index.html
index 377fb1b..5ffd4f6 100755
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -16,7 +16,7 @@
content="default-src 'self'; img-src 'self' https: data: asset: http://asset.localhost blob:; media-src 'self' https: blob: asset: http://asset.localhost; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; script-src 'self' 'wasm-unsafe-eval' https://esm.sh; connect-src 'self' https: ipc: http://ipc.localhost blob:; worker-src 'self';"
/>
YT Player
-
+
@@ -26,6 +26,7 @@
+
diff --git a/frontend/layout-base.css b/frontend/layout-base.css
new file mode 100644
index 0000000..2a0a1b1
--- /dev/null
+++ b/frontend/layout-base.css
@@ -0,0 +1,11 @@
+/* Shared markup defaults stay eager when individual layouts load on demand. */
+.stg-overlay, .skip10, .deck-chips, .vol-pct, .mode-name { display: none; }
+.np-btn .ti, .np-btn .tl { pointer-events: none; }
+.np-btn .ti { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
+.np-btn .i-done { display: none; }
+.np-btn.done .i-save { display: none; }
+.np-btn.done .i-done { display: inline; }
+.stg-bot, .ch-badge, .upnext-clear, .list-sub, .p2p-strip { display: none; }
+.sleep-status .sl-ic, .sleep-status .sl-sub { display: none; }
+.ab-group { display: contents; }
+.mc-tune { display: none; }
diff --git a/frontend/shell-consistency.test.js b/frontend/shell-consistency.test.js
index f42dc57..d44f109 100644
--- a/frontend/shell-consistency.test.js
+++ b/frontend/shell-consistency.test.js
@@ -79,3 +79,11 @@ test('only core scripts and styles remain eager; native fallback and active layo
assert.deepEqual(JSON.parse(html.match(/id="ytp-assets">([^<]+)<\/script>/)[1]).groups,groups);
for(const name of ['playlist','settings','service-mode','lyrics-editor'])assert.ok(groups['feature:'+name].eager);
});
+
+test('layout-independent visibility and transport defaults stay in eager CSS',()=>{
+ const {groups}=JSON.parse(readFileSync(join(__dirname,'assets.json'),'utf8'));
+ assert.ok(groups.core.files.includes('/layout-base.css'));
+ const shared=readFileSync(join(__dirname,'layout-base.css'),'utf8');
+ for(const selector of ['.stg-overlay','.deck-chips','.np-btn.done .i-save','.sleep-status .sl-ic','.ab-group','.mc-tune'])assert.ok(shared.includes(selector));
+ assert.ok(html.includes('href="layout-base.css"'));
+});
diff --git a/frontend/sw.js b/frontend/sw.js
index d41a5c3..efca9b4 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -60,6 +60,7 @@ const SHELL = [
'/styles.css',
'/service-ui.css',
'/refresh.css',
+ '/layout-base.css',
'/theme-glass.css',
'/theme-bento-hub.css',
'/theme-f7.css',
diff --git a/frontend/theme-glass.css b/frontend/theme-glass.css
index c2f289f..090285a 100644
--- a/frontend/theme-glass.css
+++ b/frontend/theme-glass.css
@@ -14,12 +14,6 @@
* ========================================================================== */
/* Elements that only exist for this theme stay hidden elsewhere. */
-.stg-overlay, .skip10, .deck-chips, .vol-pct, .mode-name { display: none; }
-.np-btn .ti, .np-btn .tl { pointer-events: none; }
-.np-btn .ti { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
-.np-btn .i-done { display: none; }
-.np-btn.done .i-save { display: none; }
-.np-btn.done .i-done { display: inline; }
html[data-layout="glass-stage"] {
--bg: #121317;
@@ -202,8 +196,6 @@ html[data-layout="glass-stage"] .stg-overlay { justify-content: flex-start; }
html[data-layout="glass-stage"] .stg-mid { margin: auto 0; }
/* =========================== fidelity pass (matches the approved mockup) =========================== */
-.stg-bot, .ch-badge, .upnext-clear, .list-sub, .p2p-strip { display: none; }
-.sleep-status .sl-ic, .sleep-status .sl-sub { display: none; }
/* stage HUD */
html[data-layout="glass-stage"] .stg-bot { display: flex; position: absolute; left: 12px; right: 12px; bottom: 12px; justify-content: space-between; pointer-events: none; }
@@ -292,7 +284,6 @@ html[data-layout="glass-stage"] .ps-txt small { font: 400 10px var(--mono); colo
html[data-layout="glass-stage"] .ps-dl { width: 34px; height: 34px; border-radius: 12px; border: 0; background: rgba(255, 255, 255, 0.07); color: var(--accent-bright); font-size: 16px; cursor: pointer; }
/* ===== quick row (clean rebuild): [A | B | ↺] [Fullscreen] [More] ===== */
-.ab-group { display: contents; }
html[data-layout="glass-stage"] #controls .dk-quick { display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: 8px; }
html[data-layout="glass-stage"] #controls .ab-group { display: inline-flex; align-items: stretch; gap: 2px; padding: 3px; border-radius: 14px; background: var(--tile); border: 1px solid var(--glass-line); flex: 0 1 auto; min-width: 0; }
html[data-layout="glass-stage"] #controls .ab-group .ctrl {
@@ -339,7 +330,6 @@ html[data-layout="glass-stage"] #controls .ab-group .ctrl { padding: 0 7px !impo
html[data-layout="glass-stage"] #controls .ab-group #abClearBtn { padding: 0 7px !important; font-size: 13px !important; }
/* ===== A-B · Fullscreen · More — styled like the mockup ===== */
-.mc-tune { display: none; }
html[data-layout="glass-stage"] #controls .dk-quick { flex-wrap: wrap; justify-content: space-between; gap: 8px; }
html[data-layout="glass-stage"] #controls .ab-group { padding: 2px; border-radius: 12px; gap: 0; background: rgba(255, 255, 255, 0.04); border: 1px solid rgba(255, 255, 255, 0.05); }
html[data-layout="glass-stage"] #controls .ab-group .ctrl { height: 26px !important; padding: 0 10px !important; border-radius: 8px !important; font: 500 10px var(--mono) !important; color: var(--text-dim); }