diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for scripths
 
+
+## 0.4.0.1 -- 2026-05-30
+* Parse pandoc markdown code fences as haskel code fences.
+
 ## 0.4.0.0 -- 2026-05-29
 
 * **`OverloadedStrings` on by default** in every scripths repl, so string literals
diff --git a/scripths.cabal b/scripths.cabal
--- a/scripths.cabal
+++ b/scripths.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               scripths
-version:            0.4.0.0
+version:            0.4.0.1
 synopsis:           GHCi scripts for standalone execution and Markdown documentation.
 description:        GHCi scripts for standalone execution (with dependency resolution) and Markdown documentation (produces inline output).
 homepage:           https://www.datahaskell.org/
diff --git a/src/ScriptHs/Notebook.hs b/src/ScriptHs/Notebook.hs
--- a/src/ScriptHs/Notebook.hs
+++ b/src/ScriptHs/Notebook.hs
@@ -99,4 +99,23 @@
 mkMarker n = "---SCRIPTHS_BLOCK_" <> T.pack (show n) <> "_END---"
 
 isHaskell :: Text -> Bool
-isHaskell lang = T.toLower (T.strip lang) `elem` ["haskell", "hs"]
+isHaskell lang = fenceLanguage lang `elem` ["haskell", "hs"]
+
+isPython :: Text -> Bool
+isPython lang = fenceLanguage lang `elem` ["python", "py"]
+
+{- | The base language name of a code-fence info string, lower-cased and
+trimmed. Handles both bare tags (@haskell@, @hs@) and Pandoc-style attribute
+tags: @{haskell}@, @{.haskell}@, @{.haskell:hs}@, @{.haskell:ghci}@.
+-}
+fenceLanguage :: Text -> Text
+fenceLanguage lang =
+    let stripped = T.strip lang
+        inner = case T.stripPrefix "{" stripped of
+            Just s -> T.takeWhile (/= '}') s
+            Nothing -> stripped
+        token = case T.words inner of
+            (t : _) -> t
+            [] -> ""
+        base = T.takeWhile (/= ':') (T.dropWhile (== '.') token)
+     in T.toLower base
diff --git a/test/Test/Notebook.hs b/test/Test/Notebook.hs
--- a/test/Test/Notebook.hs
+++ b/test/Test/Notebook.hs
@@ -10,6 +10,7 @@
     addOutputToSegments,
     generatedMarkedScript,
     isHaskell,
+    isPython,
     mkIndexedCodeSegments,
     mkMarker,
     parseBlocks,
@@ -37,6 +38,28 @@
                 isHaskell "python" @?= False
                 isHaskell "" @?= False
                 isHaskell "hask" @?= False
+            , testCase "accepts pandoc attribute fences" $ do
+                isHaskell "{.haskell:hs}" @?= True
+                isHaskell "{haskell}" @?= True
+                isHaskell "{.haskell}" @?= True
+                isHaskell "{.haskell:ghci}" @?= True
+            , testCase "pandoc python fences are not haskell" $ do
+                isHaskell "{.python}" @?= False
+                isHaskell "{python}" @?= False
+                isHaskell "{.python:py}" @?= False
+            ]
+        , testGroup
+            "isPython"
+            [ testCase "accepts python and py" $ do
+                isPython "python" @?= True
+                isPython "py" @?= True
+            , testCase "accepts pandoc attribute fences" $ do
+                isPython "{.python:py}" @?= True
+                isPython "{python}" @?= True
+                isPython "{.python}" @?= True
+            , testCase "rejects haskell" $ do
+                isPython "haskell" @?= False
+                isPython "{.haskell:hs}" @?= False
             ]
         , testGroup
             "mkMarker"
