diff --git a/src/web/public/mobile-handlers.js b/src/web/public/mobile-handlers.js index 393180b3..fddf5926 100644 --- a/src/web/public/mobile-handlers.js +++ b/src/web/public/mobile-handlers.js @@ -148,6 +148,13 @@ const MobileDetection = { if (typeof KeyboardHandler !== 'undefined' && KeyboardHandler.keyboardVisible) return; const vh = window.visualViewport?.height || window.innerHeight; document.documentElement.style.setProperty('--app-height', `${vh}px`); + // How far the layout viewport (which anchors position: fixed) extends below + // the visual viewport, i.e. behind the browser's bottom bar. 0 on iPhone + // Safari, where fixed elements already stop above the bar; the overlap + // where they do not. mobile.css lifts the toolbar by this rather than by + // (100vh - --app-height), which on iPhone measures the collapsible chrome + // instead and left an empty band between the toolbar and the bar. + document.documentElement.style.setProperty('--chrome-overlap', `${Math.max(0, window.innerHeight - vh)}px`); }, /** Initialize mobile detection and set up resize listener */ diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index b313f097..64036a18 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -471,11 +471,11 @@ html.mobile-init .file-browser-panel { padding-bottom: calc(40px + var(--safe-area-bottom)); } - /* iOS Safari: toolbar is pushed up by (100vh - --app-height) to clear the - browser's bottom bar. Match that offset in main's padding so the terminal - doesn't extend behind the toolbar. */ + /* iOS Safari: the toolbar is lifted by --chrome-overlap where the browser's + bottom bar would otherwise cover it. Match that offset in main's padding so + the terminal doesn't extend behind the toolbar. */ .ios-device.safari-browser .main { - padding-bottom: calc(40px + var(--safe-area-bottom) + (100vh - var(--app-height, 100vh))); + padding-bottom: calc(40px + var(--safe-area-bottom) + var(--chrome-overlap, 0px)); } .header-right { @@ -780,11 +780,14 @@ html.mobile-init .file-browser-panel { will-change: transform; } - /* iOS Safari with tab bar: position: fixed uses the layout viewport which - extends behind the browser chrome. Offset the toolbar upward by the delta - between 100vh (layout) and --app-height (visual). */ + /* iOS Safari: where position: fixed anchors to a layout viewport that + extends behind the browser's bottom bar, lift the toolbar by that overlap. + --chrome-overlap is innerHeight minus the visual viewport height, set in + mobile-handlers.js. On iPhone Safari it is 0 because fixed elements already + stop above the bar; the previous (100vh - --app-height) lift measured the + collapsible chrome instead and left an empty band above the bar. */ .ios-device.safari-browser .toolbar { - bottom: calc(var(--safe-area-bottom) + (100vh - var(--app-height, 100vh))); + bottom: calc(var(--safe-area-bottom) + var(--chrome-overlap, 0px)); } /* When keyboard is visible the JS translateY already accounts for the full