Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 08ab8adfb2c704f234c751e044723451d5a65e53)
+++ src/services/ActivityPubService.js	(revision 6100ce970f3f5922cd8b14cc66c96c878e803218)
@@ -3099,5 +3099,10 @@
     // player for PeerTube. Without one the card stays a thumbnail.
     const player = playback ? playerUrlFor(e.url) : null;
-    return player ? { ...e, 'shaer:playerUrl': player } : e;
+    if (player) return { ...e, 'shaer:playerUrl': player };
+    // The gate is shut and there IS something behind it. Saying so costs
+    // nothing (the card already shows a video thumbnail) and saves the child
+    // from tapping a card that will never answer: the app can explain instead
+    // of doing nothing. It stays a statement of fact, never a way in.
+    return playerUrlFor(e.url) ? { ...e, 'shaer:playable': true } : e;
   } catch { return undefined; }
 }
Index: test/gated-settings.test.js
===================================================================
--- test/gated-settings.test.js	(revision 08ab8adfb2c704f234c751e044723451d5a65e53)
+++ test/gated-settings.test.js	(revision 6100ce970f3f5922cd8b14cc66c96c878e803218)
@@ -162,4 +162,8 @@
   assert.ok(shut && shut.url, 'the card itself still travels');
   assert.equal(shut['shaer:playerUrl'], undefined, 'no player without the gate');
+  // But the card says there IS something behind the gate, so the app can
+  // explain the silence instead of ignoring a tap. Robin tapped a video that
+  // could not answer and nothing happened, which reads as broken, not as shut.
+  assert.equal(shut['shaer:playable'], true, 'a shut gate still admits that a player exists');
 
   const open = AP2.timelineEmbed(yt, { playback: true });
@@ -173,4 +177,7 @@
   const page = JSON.stringify({ url: 'https://yougubrands.com/about', title: 'About', media: [] });
   assert.equal(AP2.timelineEmbed(page, { playback: true })['shaer:playerUrl'], undefined);
+  // And no promise of one either: a news article is not a shut gate, it is
+  // simply not a video, and the app must not offer to ask for it.
+  assert.equal(AP2.timelineEmbed(page)['shaer:playable'], undefined);
   assert.equal(AP2.playerUrlFor('https://nos.nl/artikel/1'), null);
   // PeerTube is decentralised, so it is matched by shape, not by a host list.
