Index: src/views/shell.ejs
===================================================================
--- src/views/shell.ejs	(revision 7ca3ffcf846109ed0ca6ebd1a0832365fc85be1f)
+++ src/views/shell.ejs	(revision a036251bd8eca14e2618a9480a696594cf90f548)
@@ -198,5 +198,30 @@
 
 <!-- v9 stylesheet (full palette system) -->
-<link rel="stylesheet" href="/assets/css/style.css?v=53">
+<link rel="stylesheet" href="/assets/css/style.css?v=54">
+<script>
+/* iOS safe-area, built by hand: env(safe-area-inset-top) resolves to 0 in some iOS
+   states (Safari + standalone) even with viewport-fit=cover. Measure it directly, and
+   if it comes back empty on a notch/Dynamic-Island iPhone, fall back to a fixed inset.
+   The masthead reads --ios-safe-top. */
+(function(){
+  var ua = navigator.userAgent || '';
+  var isIOS = /iP(hone|od|ad)/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
+  if (!isIOS) return;
+  function setInset(){
+    if (!document.body) return;
+    var p = document.createElement('div');
+    p.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;padding-top:env(safe-area-inset-top,0px);visibility:hidden;pointer-events:none';
+    document.body.appendChild(p);
+    var i = parseFloat(getComputedStyle(p).paddingTop) || 0;
+    p.remove();
+    // env() gave nothing but this looks like a notched iPhone (logical height >= 812)
+    // -> use a fixed status-bar inset so the masthead clears the camera anyway.
+    if (i < 20 && Math.max(screen.width, screen.height) >= 812) i = 50;
+    document.documentElement.style.setProperty('--ios-safe-top', i + 'px');
+  }
+  if (document.body) setInset(); else document.addEventListener('DOMContentLoaded', setInset);
+  window.addEventListener('orientationchange', function(){ setTimeout(setInset, 250); });
+})();
+</script>
 
 <!-- Audio player styles: loaded on every page so the mini-player works
