Index: src/routes/circle.js
===================================================================
--- src/routes/circle.js	(revision e091add199b4b576c0f965ef2bbe7a85b50fea81)
+++ src/routes/circle.js	(revision 86fa2d2795c9ba37e01a9f0c0315a1ca031b645d)
@@ -19,7 +19,13 @@
   try { return s ? JSON.parse(s) : []; } catch { return []; }
 }
-function mediaImage(media_json) {
+// The cover image + (separately) a cover video from a remote note's media. NEVER use a video/audio
+// item as the cover image — that produced a broken <img> for an animated cover that federated as an
+// MP4 (the video becomes a <video> instead).
+function coverMedia(media_json) {
   const media = safeJson(media_json).map((m) => ({ ...m, url: safeUrl(m.url) })).filter((m) => m.url);
-  return media.find((m) => /image/i.test(m.type || '')) || media[0] || null;
+  const video = media.find((m) => /video/i.test(m.type || '')) || null;
+  const image = media.find((m) => /image/i.test(m.type || ''))
+    || (media[0] && !/(video|audio)/i.test(media[0].type || '') ? media[0] : null);
+  return { image, video };
 }
 function htmlToText(html) {
@@ -37,5 +43,5 @@
     const titleM = (r.content || '').match(/^\s*<p>\s*<strong>([\s\S]*?)<\/strong>/i);
     const realTitle = titleM ? htmlToText(titleM[1]).trim() : '';
-    const image = mediaImage(r.media_json);
+    const cover = coverMedia(r.media_json);
     const name = r.author_name || r.author_handle || 'Onbekend';
     return {
@@ -46,5 +52,6 @@
         : (text ? (text.length > 90 ? text.slice(0, 90) + '…' : text) : name),
       excerpt: '',
-      cover_image_url: image ? image.url : null,
+      cover_image_url: cover.image ? cover.image.url : null,
+      cover_video_url: cover.video ? cover.video.url : null,
       published_at: r.published,
       created_at: r.published,
