Index: src/services/ThumbnailService.js
===================================================================
--- src/services/ThumbnailService.js	(revision 48027f29830b79c87770fe10fc1f9970d91fcc63)
+++ src/services/ThumbnailService.js	(revision 8308c9cab86782954a4c0c4939a85d635e879073)
@@ -24,7 +24,8 @@
 
 // Allowed widths (whitelist → no arbitrary-size abuse). 96 = small feed/comment avatars
-// (~44px); 128 = nav/profile avatars; 256 ≈ 2× a list cover; 480 ≈ 2× a grid tile. Keep
-// these ~2× the display size so line-art stays crisp (the browser barely downscales).
-export const THUMB_SIZES = new Set([96, 128, 256, 320, 480, 640]);
+// (~44px); 128 = nav/profile avatars; 256 ≈ 2× a list cover; 480 ≈ 2× a grid tile; 1280 =
+// full-width timeline media (crisp on mobile retina, ~430px × 3 DPR). Keep these ~2× the
+// display size so the browser barely scales (avoids both jaggies and upscaling blur).
+export const THUMB_SIZES = new Set([96, 128, 256, 320, 480, 640, 1280]);
 
 let _seq = 0;
Index: src/views/pages/news.ejs
===================================================================
--- src/views/pages/news.ejs	(revision 48027f29830b79c87770fe10fc1f9970d91fcc63)
+++ src/views/pages/news.ejs	(revision 8308c9cab86782954a4c0c4939a85d635e879073)
@@ -118,5 +118,5 @@
             <div class="tl-media">
               <% imgs.forEach(function(m){ %>
-                <a class="tl-media-img" href="<%= m.url %>" target="_blank" rel="noopener"><img src="<%= thumb(m.url, 640) %>" alt="" loading="lazy" decoding="async"></a>
+                <a class="tl-media-img" href="<%= m.url %>" target="_blank" rel="noopener"><img src="<%= thumb(m.url, 1280) %>" alt="" loading="lazy" decoding="async"></a>
               <% }); %>
             </div>
