diff --git a/hmp3-ng.cabal b/hmp3-ng.cabal
--- a/hmp3-ng.cabal
+++ b/hmp3-ng.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: hmp3-ng
-version: 2.20.1
+version: 2.20.2
 synopsis: A TUI mp3 player written in Haskell
 description:
   An mp3 player with a curses frontend.  Playlists are populated by
@@ -40,6 +40,11 @@
   ghc-options:
     -Wall
     -Wprepositive-qualified-module
+
+  -- hiding unzip and (!?)
+  if impl(ghc <9.8)
+    ghc-options:
+      -Wno-dodgy-imports
 
 library
   import: opts
diff --git a/src/Base.hs b/src/Base.hs
--- a/src/Base.hs
+++ b/src/Base.hs
@@ -17,6 +17,7 @@
 import Data.Fixed as X
 import Data.Foldable as X
 import Data.Functor as X hiding (unzip)
+import Data.Function as X (fix)
 import Data.IORef as X
 import Data.List as X hiding ((!?))
 import Data.Maybe as X
diff --git a/src/Keymap.hs b/src/Keymap.hs
--- a/src/Keymap.hs
+++ b/src/Keymap.hs
@@ -19,7 +19,7 @@
 import State (getsHS, modifyHS_, KeysHelp, Modal(..), HState(..), SearchType(..), mpgRef, Mpg(..))
 import Style (plainSeg)
 import Text (SText, dropLastUTF8, fromBS, toBS)
-import UI qualified (getKey, resetui)
+import UI qualified (getKey, resetui, suspend)
 
 import Control.Monad.Trans.Maybe
 import Data.ByteString.Char8 qualified as P
@@ -178,6 +178,7 @@
     , ('[',              adjFolderCol (-1))
     , (']',              adjFolderCol 1)
     , ('\^C',            controlC)
+    , ('\^Z',            UI.suspend)
     ]
 
 -- Compiled dispatch table for normal-mode single-key commands.
diff --git a/src/UI.hs b/src/UI.hs
--- a/src/UI.hs
+++ b/src/UI.hs
@@ -9,7 +9,7 @@
 
 module UI (
     runDraw,
-    start, end, screenSize, refresh, refreshClock, resetui,
+    start, end, screenSize, refresh, refreshClock, resetui, suspend,
     getKey,
   ) where
 
@@ -28,7 +28,10 @@
 import Data.ByteString.Char8 qualified as P
 import Data.ByteString.Unsafe qualified as P
 import System.IO (stderr, hFlush)
-import System.Posix.Signals     (installHandler, Handler(..))
+import System.Posix.IO          (stdInput)
+import System.Posix.Process     (getProcessGroupID)
+import System.Posix.Signals     (installHandler, Handler(..), signalProcessGroup, sigSTOP)
+import System.Posix.Terminal    (getTerminalProcessGroupID)
 
 import Foreign.C.Error (Errno(..), getErrno)
 import Foreign.C.String
@@ -45,8 +48,6 @@
 runDraw :: Draw -> IO ()
 runDraw (Draw io) = withMVar drawLock $ const io
 
-------------------------------------------------------------------------
-
 -- | Initialize the UI
 start :: IO UIStyle
 start = do
@@ -79,6 +80,20 @@
     takeMVar drawLock        -- we keep so no one tries to draw
     setXtermTitle ["xterm"]  -- XXX I don't see this title after exit?
     Curses.endWin
+
+-- | Hand the terminal back to the shell and stop jobs.
+suspend :: IO ()
+suspend = do
+    withMVar drawLock \_ -> do
+        Curses.endWin
+        pg <- getProcessGroupID
+        fix \loop -> do
+            -- The RTS catches/re-raises SIGTSTP, which would leave races.
+            signalProcessGroup sigSTOP pg
+            fg <- (== pg) <$> getTerminalProcessGroupID stdInput
+            unless fg loop -- keep suspended if backgrounded
+        Curses.refresh
+    resetui
 
 -- | Find the current screen height and width.
 screenSize :: IO (Int, Int)
