diff --git a/docs/VUE-MIGRATION.md b/docs/VUE-MIGRATION.md
index 9db7fcbfc..f88ade519 100644
--- a/docs/VUE-MIGRATION.md
+++ b/docs/VUE-MIGRATION.md
@@ -42,6 +42,16 @@ one-panel rule too. Share links continue to record which panels are open.
this repository and styled by its existing stylesheet and custom properties.
They contain no application state.
+Controls share one skin, so every panel reads the same. Every scrolling
+surface carries the kit's `.ui-scroll` (ui-vue's name), which draws the thin
+themed scrollbar from `--scroll-thumb` and `--scroll-track`; the Cyber theme
+sets those two tokens, and Cockpit's HUD windows keep their accent tint. A
+surface without it would draw the platform's light scrollbar, because the
+page declares no dark `color-scheme`. Selects and number fields are
+`.gev-field`, which draws its own chevron in place of the platform's arrow
+and keeps native spinners dark; ranges are `.gev-quantitative-slider`, with
+a feature supplying only its accent.
+
## Phases
1. **Foundation.** Vue, Pinia and the Vite Vue plugin join the browser build
diff --git a/src/contextTabKeyboard.test.mjs b/src/contextTabKeyboard.test.mjs
index 05e013e96..adc176196 100644
--- a/src/contextTabKeyboard.test.mjs
+++ b/src/contextTabKeyboard.test.mjs
@@ -95,9 +95,9 @@ test('Context async action buttons remain focused while busy', () => {
});
test('Context header holds only its title and collapse control', () => {
- const panel = html.match(/
([\s\S]*?)<\/div>\s*
([\s\S]*?)<\/div>\s*
`${tag}.${cls}`);
assert.deepEqual(tags, ['span.panel-title', 'span.panel-divider', 'button.panel-collapse-btn']);
diff --git a/src/data/bhoteKoshiEmbeddedMedia.js b/src/data/bhoteKoshiEmbeddedMedia.js
index 1ea0ccec9..53e21f8f4 100644
--- a/src/data/bhoteKoshiEmbeddedMedia.js
+++ b/src/data/bhoteKoshiEmbeddedMedia.js
@@ -1054,7 +1054,7 @@ export function createBhoteKoshiEmbeddedMedia({
leader.innerHTML =
'';
const player = documentRef.createElement('div');
- player.className = 'bhote-embedded-callout-player';
+ player.className = 'bhote-embedded-callout-player ui-scroll';
const footer = documentRef.createElement('footer');
const title = documentRef.createElement('strong');
title.textContent = bhoteEventText(
diff --git a/src/data/bhoteKoshiEmbeddedMedia.test.mjs b/src/data/bhoteKoshiEmbeddedMedia.test.mjs
index 6829adca5..af6afc1e8 100644
--- a/src/data/bhoteKoshiEmbeddedMedia.test.mjs
+++ b/src/data/bhoteKoshiEmbeddedMedia.test.mjs
@@ -348,7 +348,7 @@ test('camera updates retain a preload until the visible player loads', () => {
assert.equal(frame.parent, root, 'keep the preload while the visible player starts');
const card = root.children.find(node => node.tag === 'section');
const body = card.children.find(node => node.className === 'bhote-embedded-callout-body');
- const player = body.children.find(node => node.className === 'bhote-embedded-callout-player');
+ const player = body.children.find(node => node.className.split(' ').includes('bhote-embedded-callout-player'));
player.children.find(node => node.tag === 'iframe').dispatchEvent(new Event('load'));
assert.equal(frame.parent, null, 'visible player readiness releases the preload');
media.destroy();
diff --git a/src/streetLevelMarkup.test.mjs b/src/streetLevelMarkup.test.mjs
index bc1716f14..18a61de42 100644
--- a/src/streetLevelMarkup.test.mjs
+++ b/src/streetLevelMarkup.test.mjs
@@ -32,7 +32,7 @@ test('Street Level is a collapsible right-rail panel that starts collapsed', ()
assert.doesNotMatch(html, /sl-mark|mly-mark/);
assert.match(
html,
- /