diff --git a/claude_code_log/html/templates/components/minimal/layout.css b/claude_code_log/html/templates/components/minimal/layout.css index 5ce44e50..55595f77 100644 --- a/claude_code_log/html/templates/components/minimal/layout.css +++ b/claude_code_log/html/templates/components/minimal/layout.css @@ -916,8 +916,13 @@ color: inherit; } +/* The shared sheet's `code { line-height: 1.5 }` reaches the that + Pygments wraps a line-numbered block's code column in, while the number + column is a bare
 at this theme's 1.55: the numbers then outrun
+   the code by a line every thirty lines (#364). */
 .theme-minimal #transcript pre code {
     font-size: inherit;
+    line-height: inherit;
     padding: 0;
     background: none;
 }
diff --git a/test/__snapshots__/test_snapshot_html.ambr b/test/__snapshots__/test_snapshot_html.ambr
index 212bd99d..c7fc8ab8 100644
--- a/test/__snapshots__/test_snapshot_html.ambr
+++ b/test/__snapshots__/test_snapshot_html.ambr
@@ -29199,8 +29199,13 @@
       color: inherit;
   }
   
+  /* The shared sheet's `code { line-height: 1.5 }` reaches the  that
+     Pygments wraps a line-numbered block's code column in, while the number
+     column is a bare 
 at this theme's 1.55: the numbers then outrun
+     the code by a line every thirty lines (#364). */
   .theme-minimal #transcript pre code {
       font-size: inherit;
+      line-height: inherit;
       padding: 0;
       background: none;
   }
@@ -44992,8 +44997,13 @@
       color: inherit;
   }
   
+  /* The shared sheet's `code { line-height: 1.5 }` reaches the  that
+     Pygments wraps a line-numbered block's code column in, while the number
+     column is a bare 
 at this theme's 1.55: the numbers then outrun
+     the code by a line every thirty lines (#364). */
   .theme-minimal #transcript pre code {
       font-size: inherit;
+      line-height: inherit;
       padding: 0;
       background: none;
   }
@@ -69426,8 +69436,13 @@
       color: inherit;
   }
   
+  /* The shared sheet's `code { line-height: 1.5 }` reaches the  that
+     Pygments wraps a line-numbered block's code column in, while the number
+     column is a bare 
 at this theme's 1.55: the numbers then outrun
+     the code by a line every thirty lines (#364). */
   .theme-minimal #transcript pre code {
       font-size: inherit;
+      line-height: inherit;
       padding: 0;
       background: none;
   }
diff --git a/test/test_minimal_components_browser.py b/test/test_minimal_components_browser.py
index aa0e90e7..ef0cd333 100644
--- a/test/test_minimal_components_browser.py
+++ b/test/test_minimal_components_browser.py
@@ -559,3 +559,51 @@ def test_a_markdown_image_stays_in_its_message(page: Page, tmp_path: Path) -> No
     )
     assert widths[2] == 3000, "the image did not load"
     assert widths[0] <= widths[1] + 0.5, widths
+
+
+READ_TOOL = TEST_DATA / "read_tool_pygments.jsonl"
+
+# A line-numbered Pygments block: `td.linenos > div > pre` holds the numbers,
+# `td.code > div > pre > code` the code. Both columns' line boxes must agree
+# or the numbers drift away from their lines down a long file (#364).
+LINE_NUMBERED_BLOCK = """
+() => {
+    const table = [...document.querySelectorAll('.highlighttable')]
+        .find(t => !t.closest('.preview-content'));
+    const numbers = table.querySelector('td.linenos pre');
+    const code = table.querySelector('td.code pre code');
+    const content = (el) => {
+        const s = getComputedStyle(el);
+        return el.getBoundingClientRect().height
+            - parseFloat(s.paddingTop) - parseFloat(s.paddingBottom);
+    };
+    const top = (el) => {
+        const s = getComputedStyle(el);
+        return el.getBoundingClientRect().top + parseFloat(s.paddingTop);
+    };
+    return {
+        lines: numbers.textContent.trim().split('\\n').length,
+        numbersHeight: content(numbers),
+        codeHeight: content(code),
+        numbersTop: top(numbers),
+        codeTop: top(code),
+    };
+}
+"""
+
+
+@pytest.mark.browser
+class TestLineNumberedCode:
+    @pytest.mark.parametrize("theme", ["classic", "minimal"])
+    def test_line_numbers_keep_step_with_the_code(
+        self, page: Page, tmp_path: Path, theme: str
+    ) -> None:
+        page.goto(_render(tmp_path, READ_TOOL, theme).as_uri())
+        page.evaluate(
+            "document.querySelectorAll('details').forEach(d => d.open = true)"
+        )
+        box = page.evaluate(LINE_NUMBERED_BLOCK)
+        assert box["lines"] >= 20, box
+        pitch = (box["numbersHeight"] / box["lines"], box["codeHeight"] / box["lines"])
+        assert abs(box["numbersHeight"] - box["codeHeight"]) < 1, (box, pitch)
+        assert abs(box["numbersTop"] - box["codeTop"]) < 1, box