Index: src/assets/js/audio-player.js
===================================================================
--- src/assets/js/audio-player.js	(revision c0e463b1f870f3f742bb65ca7ee345c081ff782b)
+++ src/assets/js/audio-player.js	(revision 5e724138a7411a573c73ec9c9d90db23ba7c650e)
@@ -885,8 +885,61 @@
   }
 
+  /**
+   * Waar op de BAND ligt deze verhouding? Geeft het nummer en de positie erin.
+   *
+   * Dit is de hele truc van hele-band-seek. De balk toont al de hele band (de
+   * teller rekent met de trackduren), maar aanklikken werkte binnen het lopende
+   * nummer -- en de keten houdt maar een nummer vooruit, dus een balk die de
+   * hele band belooft reikte in werkelijkheid tot nummer twee. Dat is erger dan
+   * geen balk, want het ziet eruit alsof het werkt.
+   *
+   * Omrekenen kan alleen met de echte duren, en die hebben we sinds de teller
+   * uit bandDuur()/bandOffset() komt. Dezelfde omrekening werkt op BEIDE
+   * motoren: de blob-motor heeft helemaal geen doorlopende tijdlijn, dus daar
+   * is dit niet alleen de beste maar de enige manier.
+   */
+  function bandPositie(ratio) {
+    const totaal = bandDuur();
+    if (!totaal) return null;
+    const doel = Math.max(0, Math.min(ratio, 1)) * totaal;
+    for (let i = 0; i < queue.length; i++) {
+      const start = bandOffset(i);
+      const eind = start + (Number(queue[i].duration) || 0);
+      if (doel < eind || i === queue.length - 1) {
+        return { index: i, binnen: Math.max(0, Math.min(doel - start, (Number(queue[i].duration) || 0) - 0.25)) };
+      }
+    }
+    return null;
+  }
+
   function attachSeek(seekEl) {
     seekEl.addEventListener('click', (e) => {
       const rect = seekEl.getBoundingClientRect();
       const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
+
+      // BANDMODUS: de balk is de hele band, dus de klik ook.
+      const plek = tapeMode ? bandPositie(ratio) : null;
+      if (plek) {
+        if (plek.index === currentIndex) {
+          // Binnen het lopende nummer: gewoon verzetten, geen herlaadsprong.
+          if (useMse() && chain.length) {
+            const seg = currentSegment();
+            if (seg) { try { audio.currentTime = seg.start + plek.binnen; } catch (er) {} }
+          } else {
+            try { audio.currentTime = plek.binnen; } catch (er) {}
+          }
+          updatePositionState();
+          return;
+        }
+        // Een ander nummer: laden en er meteen in springen. pendingSeek is de
+        // bestaande weg daarvoor -- het sessieherstel doet precies dit -- en
+        // wordt toegepast zodra de metadata er is. Er valt een hoorbaar gat bij
+        // de sprong; dat hoort bij spoelen naar een plek die nog niet in de
+        // buffer zit, en is eerlijker dan een balk die daar niet komt.
+        pendingSeek = plek.binnen;
+        loadTrack(plek.index, !audio.paused);
+        return;
+      }
+
       if (useMse() && chain.length) {
         // Seek within the CURRENT track's segment of the chain timeline.
Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision c0e463b1f870f3f742bb65ca7ee345c081ff782b)
+++ src/views/shell.ejs	(revision 5e724138a7411a573c73ec9c9d90db23ba7c650e)
@@ -406,5 +406,5 @@
      ?v=N — cache-buster: bump bij elke audio-player.js wijziging zodat
      Cloudflare (max-age=1y) niet de oude versie blijft serveren. -->
-<script src="/assets/js/audio-player.js?v=38"></script>
+<script src="/assets/js/audio-player.js?v=39"></script>
 <!-- Eigen custom media-embeds (YouTube/SoundCloud/Spotify) via de echte
      player-API's + gedeelde mutual-exclusion registry met de site-speler. -->
Index: test/mixtape-ui.test.js
===================================================================
--- test/mixtape-ui.test.js	(revision c0e463b1f870f3f742bb65ca7ee345c081ff782b)
+++ test/mixtape-ui.test.js	(revision 5e724138a7411a573c73ec9c9d90db23ba7c650e)
@@ -245,2 +245,24 @@
     'zonder dit las de teller de lengte van wat toevallig gebufferd was');
 });
+
+test('de balk is in bandmodus de hele band (shaer-tmyn, stap 4)', () => {
+  const speler = fs.readFileSync('src/assets/js/audio-player.js', 'utf8');
+
+  // De omrekening ratio -> nummer + positie erin. Zonder dit seekt de balk
+  // binnen het lopende nummer, terwijl hij de hele band TOONT: een balk die
+  // belooft wat hij niet doet is erger dan geen balk.
+  assert.match(speler, /function bandPositie\(ratio\)/);
+  assert.match(speler, /const plek = tapeMode \? bandPositie\(ratio\) : null;/);
+
+  // Zit het doel in het lopende nummer, dan geen herlaadsprong -- anders hoor
+  // je een gat bij het verzetten van een paar seconden.
+  assert.match(speler, /if \(plek\.index === currentIndex\)/);
+
+  // En anders langs pendingSeek, dezelfde weg die het sessieherstel gebruikt.
+  assert.match(speler, /pendingSeek = plek\.binnen;\s*\n\s*loadTrack\(plek\.index, !audio\.paused\);/,
+    'de speelstand hoort mee te gaan: springen mag niet stiekem starten of stoppen');
+
+  // De niet-bandmodus mag hier niets van merken.
+  assert.match(speler, /if \(useMse\(\) && chain\.length\) \{\s*\n\s*\/\/ Seek within the CURRENT track/,
+    'het oude gedrag hoort te blijven staan voor een gewone wachtrij');
+});
