diff --git a/Data/Attoparsec/Text/Internal.hs b/Data/Attoparsec/Text/Internal.hs
--- a/Data/Attoparsec/Text/Internal.hs
+++ b/Data/Attoparsec/Text/Internal.hs
@@ -75,7 +75,7 @@
 import Data.Attoparsec.Internal.Types hiding (Parser, Failure, Success)
 import qualified Data.Attoparsec.Text.Buffer as Buf
 import Data.Attoparsec.Text.Buffer (Buffer, buffer)
-import Data.Char (chr, ord)
+import Data.Char (isAsciiUpper, isAsciiLower, toUpper, toLower)
 import Data.List (intercalate)
 import Data.String (IsString(..))
 import Data.Text.Internal (Text(..))
@@ -225,14 +225,15 @@
 
 -- | Satisfy a literal string, ignoring case for characters in the ASCII range.
 asciiCI :: Text -> Parser Text
-asciiCI s = string_ (stringSuspended asciiToLower) asciiToLower s
-  where
-    asciiToLower = T.map f
-      where
-        offset = ord 'a' - ord 'A'
-        f c | 'A' <= c && c <= 'Z' = chr (ord c + offset)
-            | otherwise            = c
+asciiCI s = fmap fst $ match $ T.foldr ((*>) . asciiCharCI) (pure ()) s
 {-# INLINE asciiCI #-}
+
+asciiCharCI :: Char -> Parser Char
+asciiCharCI c
+  | isAsciiUpper c = char c <|> char (toLower c)
+  | isAsciiLower c = char c <|> char (toUpper c)
+  | otherwise = char c
+{-# INLINE asciiCharCI #-}
 
 -- | Skip past input for as long as the predicate returns 'True'.
 skipWhile :: (Char -> Bool) -> Parser ()
diff --git a/attoparsec.cabal b/attoparsec.cabal
--- a/attoparsec.cabal
+++ b/attoparsec.cabal
@@ -1,5 +1,5 @@
 name:            attoparsec
-version:         0.13.2.0
+version:         0.13.2.1
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Parsing
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+0.13.2.0
+
+* `pure` is now strict in `Position`
+
 0.13.1.0
 
 * `runScanner` now correctly returns the final state
