Index: src/config/database.js
===================================================================
--- src/config/database.js	(revision 2e247e4c529b09d55619100d5bd4e50de5a95e54)
+++ src/config/database.js	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -87,4 +87,5 @@
   ensureColumn('posts', 'view_count', 'INTEGER DEFAULT 0');         // weergaven per post
   ensureColumn('audio_tracks', 'play_count', 'INTEGER DEFAULT 0');  // plays per track
+  ensureColumn('audio_tracks', 'downloadable', 'INTEGER DEFAULT 0'); // download-voor-email (premium #2)
 
   // Playlists (v9 feature) — first-class entity. CREATE IF NOT EXISTS is
Index: src/routes/admin-audio.js
===================================================================
--- src/routes/admin-audio.js	(revision 2e247e4c529b09d55619100d5bd4e50de5a95e54)
+++ src/routes/admin-audio.js	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -78,5 +78,5 @@
   const rows = db.prepare(`
     SELECT t.id, t.title, t.artist, t.album, t.duration, t.cover_url,
-           t.position, t.created_at, m.filename, m.size, m.mime_type
+           t.position, t.created_at, t.downloadable, m.filename, m.size, m.mime_type
     FROM audio_tracks t
     LEFT JOIN media m ON m.id = t.media_id
@@ -241,4 +241,17 @@
     });
   });
+});
+
+// Download-voor-email per track aan/uit (premium #2). Zonder-JS toggle vanaf de
+// audio-beheerlijst → flip + terug.
+router.post('/:id/downloadable', requireGod, (req, res) => {
+  const site = res.locals.site;
+  if (!site) return res.status(404).send('Site required');
+  const row = db.prepare('SELECT downloadable FROM audio_tracks WHERE id = ? AND site_id = ?').get(req.params.id, site.id);
+  if (row) {
+    db.prepare('UPDATE audio_tracks SET downloadable = ? WHERE id = ? AND site_id = ?')
+      .run(row.downloadable ? 0 : 1, req.params.id, site.id);
+  }
+  res.redirect('/admin/audio');
 });
 
@@ -413,4 +426,8 @@
   }
 
+  if (Object.prototype.hasOwnProperty.call(body, 'downloadable')) {
+    fields.push('downloadable = ?'); values.push(body.downloadable ? 1 : 0);
+  }
+
   if (fields.length === 0) {
     return res.status(400).json({ error: 'Niks om te updaten' });
Index: src/routes/download.js
===================================================================
--- src/routes/download.js	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
+++ src/routes/download.js	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -0,0 +1,126 @@
+/**
+ * Download-voor-email (premium feature #2).
+ *
+ *   GET  /downloads                 -> lijst van downloadbare tracks (premium; anders 404)
+ *   GET  /download/:id              -> e-mail-capture-pagina voor één track
+ *   POST /download/:id              -> e-mail opslaan (-> mailinglijst) + download vrijgeven
+ *   GET  /download/:id/bestand      -> serveert het bestand (sessie-gated na capture)
+ *
+ * De fan laat z'n e-mail achter en krijgt het bestand; het adres komt in de
+ * subscribers-lijst (source 'download', single opt-in — geen confirm-drempel vóór de
+ * download). Hub: via /user/:slug/... (resolveSite + siteUrlBase).
+ */
+
+import express from 'express';
+import path from 'path';
+import fs from 'fs';
+import { fileURLToPath } from 'url';
+import db from '../config/database.js';
+import { renderPage } from '../middleware/render.js';
+import { premiumUnlocked } from '../services/PatreonService.js';
+import { addSubscriber } from '../services/SubscriberService.js';
+
+const router = express.Router();
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const AUDIO_DIR = path.resolve(process.env.AUDIO_PATH || path.join(__dirname, '..', '..', 'storage', 'audio'));
+
+const MIME = { '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.flac': 'audio/flac', '.m4a': 'audio/mp4', '.ogg': 'audio/ogg' };
+const GRACE_MS = 15 * 60 * 1000; // download-venster na capture
+
+function dlTrack(siteId, id) {
+  return db.prepare(
+    `SELECT t.id, t.title, t.artist, t.cover_url, m.storage_path
+       FROM audio_tracks t JOIN media m ON m.id = t.media_id
+      WHERE t.id = ? AND t.site_id = ? AND t.downloadable = 1`
+  ).get(id, siteId);
+}
+function safeName(title, storagePath) {
+  const ext = path.extname(storagePath || '').toLowerCase() || '.mp3';
+  const base = String(title || 'track').replace(/[^a-zA-Z0-9 _.-]/g, '').trim().slice(0, 80) || 'track';
+  return base + ext;
+}
+
+// Lijst van downloadbare tracks.
+router.get('/downloads', (req, res, next) => {
+  if (!premiumUnlocked()) return next();
+  const site = res.locals.site;
+  if (!site) return next();
+  const tracks = db.prepare(
+    `SELECT id, title, artist, cover_url FROM audio_tracks
+      WHERE site_id = ? AND downloadable = 1 ORDER BY position ASC, created_at ASC`
+  ).all(site.id);
+  renderPage(req, res, 'pages/downloads', {
+    pageTitle: 'Downloads — ' + (site.title || ''),
+    bodyClass: 'on-downloads',
+    dlTracks: tracks,
+  });
+});
+
+// Capture-pagina voor één track.
+router.get('/download/:id', (req, res, next) => {
+  if (!premiumUnlocked()) return next();
+  const site = res.locals.site;
+  if (!site) return next();
+  const track = dlTrack(site.id, req.params.id);
+  if (!track) return next();
+  const fan = req.session && req.session.user;
+  renderPage(req, res, 'pages/download', {
+    pageTitle: track.title + ' — download',
+    bodyClass: 'on-download',
+    dlState: 'form',
+    dlTrack: track,
+    dlPrefill: (fan && fan.email && fan.email.includes('@')) ? fan.email : '',
+  });
+});
+
+// E-mail opslaan + download vrijgeven.
+router.post('/download/:id', (req, res, next) => {
+  if (!premiumUnlocked()) return next();
+  const site = res.locals.site;
+  if (!site) return next();
+  const track = dlTrack(site.id, req.params.id);
+  if (!track) return next();
+  const email = (req.body.email || '').trim();
+  const r = addSubscriber(site.id, email, 'download', { doubleOptin: false });
+  if (!r.ok) {
+    return renderPage(req, res, 'pages/download', {
+      pageTitle: track.title + ' — download', bodyClass: 'on-download',
+      dlState: 'form', dlTrack: track, dlPrefill: email,
+      dlError: r.error === 'invalid_email' ? 'Controleer je e-mailadres.' : 'Er ging iets mis.',
+    });
+  }
+  // Download vrijgeven in de sessie (kort venster).
+  if (!req.session.dl) req.session.dl = {};
+  req.session.dl[track.id] = Date.now();
+  renderPage(req, res, 'pages/download', {
+    pageTitle: track.title + ' — download', bodyClass: 'on-download',
+    dlState: 'ready', dlTrack: track,
+  });
+});
+
+// Het bestand serveren — alleen als er net een e-mail is achtergelaten (sessie).
+router.get('/download/:id/bestand', (req, res, next) => {
+  if (!premiumUnlocked()) return next();
+  const site = res.locals.site;
+  if (!site) return next();
+  const track = dlTrack(site.id, req.params.id);
+  if (!track) return next();
+  const ts = req.session && req.session.dl && req.session.dl[track.id];
+  if (!ts || (Date.now() - ts) > GRACE_MS) {
+    return res.status(403).send('Laat eerst je e-mailadres achter om te downloaden.');
+  }
+  const sp = track.storage_path;
+  if (!sp || sp.includes('/') || sp.includes('\\') || sp.includes('..')) return res.status(400).send('Bad path');
+  const filePath = path.join(AUDIO_DIR, sp);
+  if (!filePath.startsWith(AUDIO_DIR + path.sep)) return res.status(400).send('Bad path');
+  let stat;
+  try { stat = fs.statSync(filePath); } catch { return res.status(404).send('Bestand niet gevonden'); }
+  if (!stat.isFile()) return res.status(404).send('Bestand niet gevonden');
+  const ext = path.extname(sp).toLowerCase();
+  res.setHeader('Content-Type', MIME[ext] || 'application/octet-stream');
+  res.setHeader('Content-Length', stat.size);
+  res.setHeader('Content-Disposition', 'attachment; filename="' + safeName(track.title, sp) + '"');
+  fs.createReadStream(filePath).pipe(res);
+});
+
+export default router;
Index: src/server.js
===================================================================
--- src/server.js	(revision 2e247e4c529b09d55619100d5bd4e50de5a95e54)
+++ src/server.js	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -55,4 +55,5 @@
 import newsletterRoutes from './routes/newsletter.js';
 import adminNewsletterRoutes from './routes/admin-newsletter.js';
+import downloadRoutes from './routes/download.js';
 
 if (!process.env.SESSION_SECRET) {
@@ -271,4 +272,5 @@
 app.use('/', epkRoutes); // /pers perskit (premium; niet-premium: next() -> 404)
 app.use('/', newsletterRoutes); // /nieuwsbrief in/uitschrijven (premium; niet-premium: next())
+app.use('/', downloadRoutes); // /downloads + /download/:id download-voor-email (premium)
 app.use('/', postsRoutes);
 
Index: src/views/pages/admin-audio.ejs
===================================================================
--- src/views/pages/admin-audio.ejs	(revision 2e247e4c529b09d55619100d5bd4e50de5a95e54)
+++ src/views/pages/admin-audio.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -101,4 +101,9 @@
               <% } %>
               <button type="button" class="ax-icon-btn" data-track-edit data-id="<%= t.id %>" aria-label="Bewerken" title="Bewerken">✎</button>
+              <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
+                <form action="/admin/audio/<%= t.id %>/downloadable" method="post" class="ax-track-delete" style="display:inline">
+                  <button type="submit" class="ax-icon-btn" aria-label="Download-voor-email" title="<%= t.downloadable ? 'Download-voor-email staat AAN — klik om uit te zetten' : 'Download-voor-email staat uit — klik om aan te zetten' %>" style="<%= t.downloadable ? 'color:var(--accent,#6b8f71)' : 'opacity:.5' %>">⬇</button>
+                </form>
+              <% } %>
               <form action="/admin/audio/<%= t.id %>/delete" method="post" onsubmit="return confirm('Track verwijderen?')" class="ax-track-delete">
                 <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="Verwijderen" title="Verwijderen">🗑</button>
Index: src/views/pages/admin.ejs
===================================================================
--- src/views/pages/admin.ejs	(revision 2e247e4c529b09d55619100d5bd4e50de5a95e54)
+++ src/views/pages/admin.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -35,4 +35,5 @@
       <a href="/admin/newsletter" class="btn">✉️ Nieuwsbrief</a>
       <% if (tenancy !== 'hub' && primarySite) { %><a href="/pers" class="btn" target="_blank">📰 Perskit</a><% } %>
+      <% if (tenancy !== 'hub' && primarySite) { %><a href="/downloads" class="btn" target="_blank">⬇ Downloads</a><% } %>
     <% } %>
     <a href="/admin/updates" class="btn">🔄 Updates</a>
Index: src/views/pages/download.ejs
===================================================================
--- src/views/pages/download.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
+++ src/views/pages/download.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -0,0 +1,50 @@
+<%
+  var t = (typeof dlTrack !== 'undefined') ? dlTrack : {};
+  var st = (typeof dlState !== 'undefined') ? dlState : 'form';
+  var fileUrl = siteUrlBase + '/download/' + t.id + '/bestand';
+%>
+<section class="dl">
+  <div class="dl-card">
+    <div class="dl-head">
+      <% if (t.cover_url) { %><span class="dl-cover" style="background-image:url('<%= t.cover_url %>')"></span><% } else { %><span class="dl-cover dl-cover-empty">♪</span><% } %>
+      <div>
+        <div class="dl-title"><%= t.title %></div>
+        <% if (t.artist) { %><div class="dl-artist"><%= t.artist %></div><% } %>
+      </div>
+    </div>
+
+    <% if (st === 'ready') { %>
+      <h1 class="dl-h1">Bedankt! ⬇</h1>
+      <p class="dl-sub">Je download zou nu moeten starten. Gebeurt er niets?</p>
+      <p><a class="dl-go" href="<%= fileUrl %>">Download handmatig starten</a></p>
+      <script>
+        // Auto-start de download (zelfde-origin attachment-link).
+        setTimeout(function(){ try { window.location.href = <%- JSON.stringify(fileUrl) %>; } catch(e){} }, 600);
+      </script>
+    <% } else { %>
+      <h1 class="dl-h1">Download <%= t.title %></h1>
+      <p class="dl-sub">Laat je e-mailadres achter en je krijgt het bestand. Je komt dan ook op de nieuwsbrieflijst — uitschrijven kan altijd.</p>
+      <% if (typeof dlError !== 'undefined' && dlError) { %><p class="dl-err"><%= dlError %></p><% } %>
+      <form method="POST" action="<%= siteUrlBase %>/download/<%= t.id %>" class="dl-form">
+        <input type="email" name="email" required placeholder="jouw@email.nl" value="<%= (typeof dlPrefill!=='undefined')?dlPrefill:'' %>" autocomplete="email">
+        <button type="submit" class="dl-go">⬇ Download</button>
+      </form>
+    <% } %>
+  </div>
+</section>
+
+<style>
+  .dl { max-width: 520px; margin: 0 auto; padding: 48px 18px; }
+  .dl-card { border: 1px solid rgba(128,128,128,.2); border-radius: 18px; padding: 28px; }
+  .dl-head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
+  .dl-cover { width: 56px; height: 56px; border-radius: 10px; background-size: cover; background-position: center; display: grid; place-items: center; flex: 0 0 auto; }
+  .dl-cover-empty { background: var(--accent,#6b8f71); color: #fff; opacity: .85; }
+  .dl-title { font-weight: 700; font-size: 17px; }
+  .dl-artist { opacity: .65; font-size: 13px; }
+  .dl-h1 { font-size: clamp(22px,4.5vw,30px); margin: 0 0 8px; }
+  .dl-sub { opacity: .85; line-height: 1.55; margin: 0 0 18px; }
+  .dl-err { color: #c43c3c; margin: 0 0 12px; }
+  .dl-form { display: flex; gap: 10px; flex-wrap: wrap; }
+  .dl-form input { flex: 1 1 200px; padding: 12px 14px; border-radius: 10px; border: 1px solid rgba(128,128,128,.4); background: transparent; color: inherit; font-size: 15px; }
+  .dl-go { padding: 12px 20px; border-radius: 10px; border: none; background: var(--accent,#6b8f71); color: #fff; font-weight: 600; font-size: 15px; cursor: pointer; text-decoration: none; display: inline-block; }
+</style>
Index: src/views/pages/downloads.ejs
===================================================================
--- src/views/pages/downloads.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
+++ src/views/pages/downloads.ejs	(revision 91094a44a78eaba7694534a039a5d0843f451e10)
@@ -0,0 +1,38 @@
+<%
+  var ts = (typeof dlTracks !== 'undefined') ? dlTracks : [];
+%>
+<section class="dls">
+  <h1 class="dls-h1">Downloads</h1>
+  <p class="dls-sub">Gratis te downloaden — laat je e-mailadres achter en je krijgt het bestand.</p>
+
+  <% if (!ts.length) { %>
+    <p class="dls-empty">Er staan momenteel geen downloads klaar.</p>
+  <% } else { %>
+    <ul class="dls-list">
+      <% ts.forEach(function(t){ %>
+        <li class="dls-item">
+          <% if (t.cover_url) { %><span class="dls-cover" style="background-image:url('<%= t.cover_url %>')"></span><% } else { %><span class="dls-cover dls-cover-empty">♪</span><% } %>
+          <span class="dls-meta">
+            <span class="dls-title"><%= t.title %></span>
+            <% if (t.artist) { %><span class="dls-artist"><%= t.artist %></span><% } %>
+          </span>
+          <a class="dls-btn" href="<%= siteUrlBase %>/download/<%= t.id %>">⬇ Download</a>
+        </li>
+      <% }); %>
+    </ul>
+  <% } %>
+</section>
+
+<style>
+  .dls { max-width: 680px; margin: 0 auto; padding: 32px 18px 64px; }
+  .dls-h1 { font-size: clamp(26px,5vw,38px); margin: 0 0 8px; }
+  .dls-sub { opacity: .8; margin: 0 0 24px; }
+  .dls-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
+  .dls-item { display: flex; align-items: center; gap: 14px; padding: 10px 12px; border: 1px solid rgba(128,128,128,.2); border-radius: 12px; }
+  .dls-cover { flex: 0 0 auto; width: 48px; height: 48px; border-radius: 8px; background-size: cover; background-position: center; display: grid; place-items: center; }
+  .dls-cover-empty { background: var(--accent,#6b8f71); color: #fff; opacity: .85; }
+  .dls-meta { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
+  .dls-title { font-weight: 600; }
+  .dls-artist { font-size: 12.5px; opacity: .65; }
+  .dls-btn { flex: 0 0 auto; padding: 9px 15px; border-radius: 999px; background: var(--accent,#6b8f71); color: #fff; text-decoration: none; font-weight: 600; font-size: 13.5px; }
+</style>
