diff --git a/src/Roux/Html.hs b/src/Roux/Html.hs
index aa3f3cb..1660e5f 100644
--- a/src/Roux/Html.hs
+++ b/src/Roux/Html.hs
@@ -1136,7 +1136,20 @@ dedupFirst (i : rest) acc =
renderMethodSections :: [Section] -> Html
renderMethodSections sections = do
let allSteps = concatMap sectionMethodSteps (filter isMethodSection sections)
- mapM_ (uncurry renderMethodStep) (zip [1 ..] allSteps)
+ -- Exclude empty/whitespace-only steps (from leading newlines in
+ -- Cooklang files) so step numbering always starts at 1.
+ meaningful = filter (not . isEmptyStep) allSteps
+ mapM_ (uncurry renderMethodStep) (zip [1 ..] meaningful)
+
+-- | Is this a SectionBodyItem that contains no meaningful step content?
+isEmptyStep :: (Maybe Text, SectionBodyItem) -> Bool
+isEmptyStep (_, SecStep step) = null (unStep step) || all isBlankText (unStep step)
+isEmptyStep _ = False
+
+-- | Is a StepItem just whitespace?
+isBlankText :: StepItem -> Bool
+isBlankText (StepText t) = T.all (== ' ') t
+isBlankText _ = False
-- | Is this section a method section (not an ingredient listing)?
isMethodSection :: Section -> Bool