Index: test/c2s-title.test.js
===================================================================
--- test/c2s-title.test.js	(revision cb1bd827117ad2b6229aa4186e7b5cc3e66adb7f)
+++ test/c2s-title.test.js	(revision 78b8e58cb0313999935f4f36dcaaf2d5d90342f1)
@@ -51,11 +51,19 @@
 });
 
-test('zonder name blijft alles zoals het was: lege titel, geen kopregel', async () => {
-  const r = await maak({ content: '<p>gewoon een note</p>' });
-  assert.equal(r.status, 201);
-  const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(r.id);
+test('geen echte titel blijft een lege titel: zonder name, en met alleen witruimte', async () => {
+  // Een regel met twee kanten (samengevoegd 25-8): wat er niet is, wordt niet
+  // verzonnen -- of het veld nu ontbreekt of alleen witruimte draagt.
+  const zonder = await maak({ content: '<p>gewoon een note</p>' });
+  assert.equal(zonder.status, 201);
+  const post = db.prepare('SELECT * FROM posts WHERE id = ?').get(zonder.id);
   assert.equal(post.title, '', 'geen name is een lege titel, geen undefined of null');
   const note = AP.buildNote('https://test.example', site, post);
-  assert.ok(!/<strong>/.test(note.content.split('</p>')[0] + '</p>') || !post.title, 'geen verzonnen kopregel');
+  assert.ok(!note.content.startsWith('<p><strong>'), 'geen verzonnen kopregel');
+
+  const wit = await maak({ name: '   ', content: '<p>z</p>' });
+  const witPost = db.prepare('SELECT title FROM posts WHERE id = ?').get(wit.id);
+  assert.equal(witPost.title, '', 'witruimte trimt weg tot leeg');
+  const witNote = AP.buildNote('https://test.example', site, db.prepare('SELECT * FROM posts WHERE id = ?').get(wit.id));
+  assert.ok(!witNote.content.startsWith('<p><strong>'), 'en er komt geen lege kopregel');
 });
 
@@ -79,9 +87,2 @@
 });
 
-test('een titel met alleen witruimte is geen titel', async () => {
-  const r = await maak({ name: '   ', content: '<p>z</p>' });
-  const post = db.prepare('SELECT title FROM posts WHERE id = ?').get(r.id);
-  assert.equal(post.title, '', 'witruimte trimt weg tot leeg');
-  const note = AP.buildNote('https://test.example', site, post);
-  assert.ok(!note.content.startsWith('<p><strong>'), 'en er komt geen lege kopregel');
-});
Index: test/migrate-page.test.js
===================================================================
--- test/migrate-page.test.js	(revision cb1bd827117ad2b6229aa4186e7b5cc3e66adb7f)
+++ test/migrate-page.test.js	(revision 78b8e58cb0313999935f4f36dcaaf2d5d90342f1)
@@ -39,5 +39,11 @@
 
 test('de volgorde op de pagina volgt de verhuizing: claimen eerst, aankondigen laatst', () => {
+  // Volgorde is hier INHOUD, geen layout (25-8): wie deze stappen in de
+  // verkeerde volgorde doet, breekt zijn verhuizing -- de pagina IS het
+  // stappenplan. Daarom mag deze positietoets blijven waar andere sneuvelden.
+  // De losse toets dat het stappenplan "bovenaan, voor de knoppen" staat is
+  // hierin opgegaan: dat het er IS hoort erbij, waar het staat niet.
   const ejs = fs.readFileSync('src/views/pages/admin-migrate.ejs', 'utf8');
+  assert.ok(ejs.indexOf('mig-steps') > -1, 'er hoort een stappenplan te staan');
   const alias = ejs.indexOf('name="ap_aliases"');
   const halen = ejs.indexOf('/admin/migrate/pull');
@@ -67,12 +73,4 @@
 
 // ── Het stappenplan ───────────────────────────────────────────────
-
-test('het stappenplan zet de volgorde vast die niemand raadt', () => {
-  const ejs = fs.readFileSync('src/views/pages/admin-migrate.ejs', 'utf8');
-  const plan = ejs.indexOf('mig-steps');
-  const alias = ejs.indexOf('name="ap_aliases"');
-  assert.ok(plan > -1, 'er hoort een stappenplan te staan');
-  assert.ok(plan < alias, 'en bovenaan, voor de knoppen die het beschrijft');
-});
 
 test('"hier" draait om als je vanaf de vertrekkende kant kijkt', async () => {
Index: test/mixtape-ui.test.js
===================================================================
--- test/mixtape-ui.test.js	(revision cb1bd827117ad2b6229aa4186e7b5cc3e66adb7f)
+++ test/mixtape-ui.test.js	(revision 78b8e58cb0313999935f4f36dcaaf2d5d90342f1)
@@ -88,13 +88,10 @@
   // Het teken zat eerst in de tekst; nu staat er een echte cassette getekend en
   // is het woord genoeg. De eis blijft dezelfde: hij noemt zichzelf goed.
-  assert.match(html, /class="tape-kind">Mixtape</, 'een mixtape hoort zichzelf zo te noemen');
+  // Zonder de klassenaam erbij (25-8): WAAR het woord staat is layout, DAT het
+  // er staat is het besluit. De bron-regex-toets op de labelmap is weg om
+  // dezelfde reden: die toetste deze regel nog een keer, maar dan op de
+  // spelling van de code.
+  assert.match(html, />Mixtape</, 'een mixtape hoort zichzelf zo te noemen');
   assert.doesNotMatch(html, /💿 Album/, 'en niet het jasje van een album te dragen');
-});
-
-test('de renderer kiest zijn label niet meer met een tweewegkeuze', () => {
-  const bron = fs.readFileSync('src/services/AudioEmbedService.js', 'utf8');
-  assert.doesNotMatch(bron, /kind === 'playlist' \? '📃 Playlist' : '💿 Album'/,
-    'die vorm gaf een mixtape het jasje van een album');
-  assert.match(bron, /mixtape: '📼 Mixtape'/);
 });
 
@@ -110,7 +107,9 @@
   const html = AudioEmbedService.embedPlaylistShortcodes('<p>[[playlist:tape]]</p>', (id) => (id === 'tape' ? pl : null));
 
+  // Alleen de haken waar tape.js zich aan vastpakt (.post-tape, data-tape-go):
+  // dat is het contract tussen markup en module. De spoelen zelf zijn decor en
+  // worden hier sinds 25-8 niet meer geteld -- hoe de cassette eruitziet is
+  // layout, dat hij bedienbaar is niet.
   assert.match(html, /class="post-tape"/);
-  assert.match(html, /tape-reel--left/, 'twee spoelen, anders is het geen cassette');
-  assert.match(html, /tape-reel--right/);
   assert.match(html, /data-tape-go="back"/, 'terugspoelen');
   assert.match(html, /data-tape-go="fwd"/, 'vooruitspoelen');
