diff --git a/src/renderer/src/components/terminal-pane/TerminalPane.tsx b/src/renderer/src/components/terminal-pane/TerminalPane.tsx
index 3c556118d..a7543e8df 100644
--- a/src/renderer/src/components/terminal-pane/TerminalPane.tsx
+++ b/src/renderer/src/components/terminal-pane/TerminalPane.tsx
@@ -2896,16 +2896,23 @@ export default function TerminalPane({
onRestartDaemon={() => daemonActions.setPending('restart')}
/>
)}
- {showSshReconnectOverlay && sshReconnectTargetId && sshReconnectStatus ? (
-
- ) : null}
+ {/* Why: portal into the pane so the banner stacks above the xterm canvas (sibling mount painted under WebGL). */}
+ {showSshReconnectOverlay && sshReconnectTargetId && sshReconnectStatus
+ ? managedPanes.map((pane) =>
+ createPortal(
+ ,
+ pane.container,
+ `ssh-reconnect-${pane.id}`
+ )
+ )
+ : null}
{isActive && (
{
expect(screen.getByText(/This terminal is waiting for devbox/)).toBeInTheDocument()
expect(screen.getByRole('status')).toBeInTheDocument()
const banner = container.querySelector('[data-terminal-ssh-reconnect-banner="disconnected"]')
- expect(banner).toHaveClass('inset-x-3', 'bottom-3')
+ expect(banner).toHaveClass('inset-x-3', 'bottom-3', 'z-40')
expect(banner).not.toHaveClass('inset-0', 'bg-background/75')
await user.click(screen.getByRole('button', { name: 'Connect' }))
diff --git a/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx b/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx
index a90e16c2a..b0506a28a 100644
--- a/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx
+++ b/src/renderer/src/components/terminal-pane/TerminalSshReconnectOverlay.tsx
@@ -136,13 +136,14 @@ export function TerminalSshReconnectOverlay({
}
}, [isConnecting, mountedRef, setSshConnectionState, sshOwnerEnvironmentId, targetId])
+ // Why: z-40 clears pane-local chrome (focus rim z-30); bg-card is fully opaque so terminal text cannot paint through.
return (