Index: CHANGELOG.md
===================================================================
--- CHANGELOG.md	(revision 535f955817ec83f38e0bd69aafbc344a7f7c716a)
+++ CHANGELOG.md	(revision 247988ed245044e3ec30c09f8653d602090a219c)
@@ -15,4 +15,9 @@
 
 ### Toegevoegd
+- Beheerder kan z'n **Google-account koppelen** (Account → Inloggen met Google) en
+  daarna óók met Google inloggen. Veilig: koppelen kan alleen terwijl je met
+  wachtwoord bent ingelogd, en Google-login geeft alleen beheer als de exacte
+  gekoppelde Google-account (`google_sub`) matcht. Ontkoppelen kan zolang er een
+  wachtwoord is.
 - Posts liken / favorieten: ingelogde gebruikers kunnen een post liken met een ♥-knop
   (toont het aantal likes). Je gelikete posts staan op de nieuwe **Favorieten**-pagina
Index: src/routes/account.js
===================================================================
--- src/routes/account.js	(revision 535f955817ec83f38e0bd69aafbc344a7f7c716a)
+++ src/routes/account.js	(revision 247988ed245044e3ec30c09f8653d602090a219c)
@@ -24,4 +24,5 @@
 import { renderPage } from '../middleware/render.js';
 import { requireAuth } from '../middleware/auth.js';
+import { googleConfigured } from '../config/google.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -58,9 +59,10 @@
 router.get('/', requireAuth, (req, res) => {
   const account = db.prepare(`
-    SELECT id, username, email, role, bio, avatar_url, created_at, password_hash
+    SELECT id, username, email, role, bio, avatar_url, created_at, password_hash, google_sub
     FROM users WHERE id = ?
   `).get(req.session.user.id);
   const hasPassword = !!(account && account.password_hash && account.password_hash !== '!google-oauth');
-  if (account) delete account.password_hash; // niet naar de view lekken
+  const googleLinked = !!(account && account.google_sub);
+  if (account) { delete account.password_hash; delete account.google_sub; } // niet naar de view lekken
 
   renderPage(req, res, 'pages/account', {
@@ -69,4 +71,6 @@
     account,
     hasPassword,
+    googleLinked,
+    googleAvailable: googleConfigured(),
     editableSite: ownedSite(req.session.user),
     success: req.query.success || null,
@@ -144,4 +148,18 @@
 });
 
+// Google-account ontkoppelen. Alleen toegestaan als er nog een wachtwoord is,
+// anders zou je jezelf buitensluiten (geen login-methode meer over).
+router.post('/google/unlink', requireAuth, (req, res) => {
+  const row = db.prepare('SELECT password_hash, google_sub FROM users WHERE id = ?').get(req.session.user.id);
+  if (!row || !row.google_sub) {
+    return res.redirect('/account?error=' + encodeURIComponent('Er is geen Google-account gekoppeld'));
+  }
+  if (!row.password_hash || row.password_hash === '!google-oauth') {
+    return res.redirect('/account?error=' + encodeURIComponent('Stel eerst een wachtwoord in — anders kun je niet meer inloggen.'));
+  }
+  db.prepare('UPDATE users SET google_sub = NULL, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(req.session.user.id);
+  res.redirect('/account?success=' + encodeURIComponent('Google-account ontkoppeld'));
+});
+
 // ==================== UPLOAD AVATAR ====================
 router.post('/avatar', requireAuth, (req, res) => {
Index: src/routes/auth.js
===================================================================
--- src/routes/auth.js	(revision 535f955817ec83f38e0bd69aafbc344a7f7c716a)
+++ src/routes/auth.js	(revision 247988ed245044e3ec30c09f8653d602090a219c)
@@ -6,5 +6,5 @@
 import { renderPage } from '../middleware/render.js';
 import { loginLimiter, registerLimiter } from '../middleware/rate-limit.js';
-import { safeNext } from '../middleware/auth.js';
+import { safeNext, requireAuth } from '../middleware/auth.js';
 import { googleConfigured, authorizeUrl, exchangeCode, fetchUserinfo } from '../config/google.js';
 import { premiumUnlocked } from '../services/PatreonService.js';
@@ -268,4 +268,19 @@
   req.session.oauthState = state;
   req.session.oauthNext = safeNext(req.query.next) || '';
+  delete req.session.oauthLink;
+  res.redirect(authorizeUrl(state));
+});
+
+// Google KOPPELEN aan het huidige (ingelogde) account — bv. een beheerder die
+// voortaan óók met Google wil inloggen. Vereist dat je al ingelogd bent (met
+// wachtwoord); de koppeling slaat de google_sub op het eigen account op.
+// Alleen googleConfigured() nodig (geen premium-gate — dit is geen fan-login).
+router.get('/google/link', requireAuth, (req, res) => {
+  if (!googleConfigured()) {
+    return res.redirect('/account?error=' + encodeURIComponent('Google-login is op deze site niet ingesteld.'));
+  }
+  const state = crypto.randomBytes(16).toString('hex');
+  req.session.oauthState = state;
+  req.session.oauthLink = true; // koppel-modus i.p.v. login-modus
   res.redirect(authorizeUrl(state));
 });
@@ -283,32 +298,54 @@
 
 router.get('/google/callback', async (req, res) => {
-  const fail = (code) => res.redirect('/auth/login?gerr=' + code);
-  if (!fanLoginReady()) return fail('unavailable');
+  const linking = !!req.session.oauthLink;
+  const failLogin = (code) => res.redirect('/auth/login?gerr=' + code);
+  const failLink = (msg) => res.redirect('/account?error=' + encodeURIComponent(msg));
+
   try {
     const { code, state } = req.query;
-    if (!code || !state || state !== req.session.oauthState) return fail('session');
-    const next = safeNext(req.session.oauthNext) || '';
-    delete req.session.oauthState;
-    delete req.session.oauthNext;
+    if (!code || !state || state !== req.session.oauthState) {
+      delete req.session.oauthState; delete req.session.oauthLink; delete req.session.oauthNext;
+      return linking ? failLink('Google-koppeling afgebroken of sessie verlopen. Probeer opnieuw.') : failLogin('session');
+    }
 
     const tok = await exchangeCode(String(code));
     const info = await fetchUserinfo(tok.access_token);
     const email = (info.email || '').trim().toLowerCase();
-    if (!email || info.email_verified === false) return fail('email');
+
+    // ── KOPPEL-MODUS: Google aan het huidige (ingelogde) account hangen ──
+    if (linking) {
+      delete req.session.oauthState; delete req.session.oauthLink;
+      if (!req.session.user) return failLogin('session');
+      if (!info.sub) return failLink('Google gaf geen account-id terug. Probeer opnieuw.');
+      if (info.email && info.email_verified === false) return failLink('Je Google-adres is niet geverifieerd.');
+      // Dit Google-account mag niet al aan een ANDER account hangen.
+      const other = db.prepare('SELECT id FROM users WHERE google_sub = ? AND id != ?').get(info.sub, req.session.user.id);
+      if (other) return failLink('Dit Google-account is al aan een andere gebruiker gekoppeld.');
+      db.prepare(`
+        UPDATE users SET google_sub = ?, avatar_url = COALESCE(avatar_url, ?),
+          updated_at = CURRENT_TIMESTAMP WHERE id = ?
+      `).run(info.sub, info.picture || null, req.session.user.id);
+      return res.redirect('/account?success=' + encodeURIComponent('Google-account gekoppeld — je kunt nu ook met Google inloggen.'));
+    }
+
+    // ── LOGIN-MODUS (luisteraars/fans + gekoppelde beheerder) ──
+    if (!fanLoginReady()) return failLogin('unavailable');
+    const next = safeNext(req.session.oauthNext) || '';
+    delete req.session.oauthState; delete req.session.oauthNext;
+    if (!email || info.email_verified === false) return failLogin('email');
 
     let user = db.prepare('SELECT * FROM users WHERE LOWER(email) = ?').get(email);
 
-    if (user) {
-      // Strikte scheiding: Google geeft nooit beheer. Hoort dit adres bij een
-      // beheerder, dan moet diegene met wachtwoord inloggen (geen Google-bypass).
-      if (user.role === 'god' || user.role === 'admin') {
-        // Eigen, duidelijke uitleg-pagina (zie auth-login.ejs) i.p.v. een kale melding.
-        return fail('admin');
+    if (user && (user.role === 'god' || user.role === 'admin')) {
+      // Beheerder mag ALLEEN met Google in als 'ie z'n Google expliciet gekoppeld
+      // heeft (matchende google_sub). Anders blijft gelden: Google = nooit beheer.
+      if (!(user.google_sub && info.sub && user.google_sub === info.sub)) {
+        return failLogin('admin');
       }
+      // gekoppeld + match → doorgaan met de eigen (beheer)rol.
+    } else if (user) {
       // Bestaande luisteraar: koppel google_sub/avatar als die ontbreken; weiger
       // als al aan een ander Google-account gekoppeld.
-      if (user.google_sub && info.sub && user.google_sub !== info.sub) {
-        return fail('linked');
-      }
+      if (user.google_sub && info.sub && user.google_sub !== info.sub) return failLogin('linked');
       db.prepare(`
         UPDATE users SET google_sub = COALESCE(google_sub, ?), avatar_url = COALESCE(avatar_url, ?),
@@ -334,5 +371,8 @@
   } catch (e) {
     console.error('[auth/google/callback]', e.message);
-    fail('failed');
+    delete req.session.oauthState; delete req.session.oauthLink; delete req.session.oauthNext;
+    return linking
+      ? res.redirect('/account?error=' + encodeURIComponent('Google koppelen mislukt — probeer opnieuw.'))
+      : failLogin('failed');
   }
 });
Index: src/views/pages/account.ejs
===================================================================
--- src/views/pages/account.ejs	(revision 535f955817ec83f38e0bd69aafbc344a7f7c716a)
+++ src/views/pages/account.ejs	(revision 247988ed245044e3ec30c09f8653d602090a219c)
@@ -132,4 +132,19 @@
     </form>
   </section>
+
+  <% if (typeof googleAvailable !== 'undefined' && googleAvailable) { %>
+  <section class="ax-card">
+    <div class="ax-card-title">Inloggen met Google</div>
+    <% if (typeof googleLinked !== 'undefined' && googleLinked) { %>
+      <p class="ax-tagline" style="margin:0 0 1rem">✓ Je Google-account is gekoppeld — je kunt ook met Google inloggen.</p>
+      <form action="/account/google/unlink" method="post">
+        <button type="submit" class="ax-btn">Google ontkoppelen</button>
+      </form>
+    <% } else { %>
+      <p class="ax-tagline" style="margin:0 0 1rem">Koppel je Google-account zodat je voortaan ook met één klik via Google kunt inloggen (naast je wachtwoord).</p>
+      <a href="/auth/google/link" class="ax-btn ax-btn-primary" data-full-load>Koppel Google-account</a>
+    <% } %>
+  </section>
+  <% } %>
   <% } else { %>
   <section class="ax-card">
Index: src/views/pages/auth-login.ejs
===================================================================
--- src/views/pages/auth-login.ejs	(revision 535f955817ec83f38e0bd69aafbc344a7f7c716a)
+++ src/views/pages/auth-login.ejs	(revision 247988ed245044e3ec30c09f8653d602090a219c)
@@ -13,5 +13,5 @@
       admin: { icon: '🔐', title: 'Dit is een beheerders-account',
         body: 'Je logde in met Google, maar dit e-mailadres hoort bij de beheerder van deze site. Inloggen met Google is alleen voor luisteraars/fans — beheerders gebruiken altijd hun wachtwoord (zo kan een Google-login nooit per ongeluk beheerrechten geven).',
-        list: ['<strong>Ben je de beheerder?</strong> <a href="/auth/admin">Log in met je gebruikersnaam + wachtwoord</a>.', '<strong>Wil je als fan inloggen?</strong> Gebruik een <strong>ander Google-account</strong> (niet je beheerdersadres).'] },
+        list: ['<strong>Ben je de beheerder?</strong> <a href="/auth/admin">Log in met je gebruikersnaam + wachtwoord</a>.', '<strong>Wil je voortaan met Google inloggen?</strong> Log eerst in met wachtwoord en <strong>koppel je Google-account</strong> onder Account → daarna werkt Google-login ook voor jou.', '<strong>Wil je als fan inloggen?</strong> Gebruik een <strong>ander Google-account</strong> (niet je beheerdersadres).'] },
       session: { icon: '⏱️', title: 'Login afgebroken', body: 'De Google-login is afgebroken of de sessie was verlopen. Probeer het gewoon opnieuw.' },
       email: { icon: '✉️', title: 'Geen geverifieerd e-mailadres', body: 'Je Google-account heeft geen geverifieerd e-mailadres. Verifieer je adres bij Google.' },
