diff --git a/l4d2web/tests/e2e/test_files_overlay.py b/l4d2web/tests/e2e/test_files_overlay.py
index ad80b01..f7a42b7 100644
--- a/l4d2web/tests/e2e/test_files_overlay.py
+++ b/l4d2web/tests/e2e/test_files_overlay.py
@@ -175,3 +175,60 @@ def test_create_new_file_409_askConflict_keep_both(page: Page, files_overlay_ser
expect(
page.locator('.file-tree-row-file[data-target-path="cfg (1)"]')
).to_be_visible(timeout=5000)
+
+
+def test_open_binary_file_renders_replace_ui(page: Page, files_overlay_server) -> None:
+ """Open icon.png and assert the routed editor renders the binary
+ branch of overlay_file_editor.html — replace-zone present, save
+ button labelled "Replace" and disabled until a file is queued or
+ the filename is edited.
+
+ The same `/files/edit?path=...` route serves both text and binary
+ modes; the server picks the template branch based on is_editable +
+ a magic-byte check. This test pins the binary contract: the user
+ cannot save by accident on a fresh open, and the queue UI is wired
+ correctly.
+
+ Pins:
+ * .files-editor-binary[data-rel-path="icon.png"] exists (the
+ data-rel-path stable hook for save logic).
+ * Save button has visible text "Replace" + the `disabled`
+ attribute is set on open.
+ * .files-editor-replace-zone + .files-editor-replace-browse +
+ the hidden file exist.
+ * Download link points back at /files/download?path=icon.png.
+ """
+ base = files_overlay_server["base_url"]
+ overlay_id = files_overlay_server["overlay_id"]
+ _open_overlay(page, base, overlay_id)
+
+ page.click('button.file-tree-name-button[data-target-path="icon.png"]')
+
+ fragment = page.locator("#files-editor-fragment")
+ expect(fragment).to_be_visible(timeout=5000)
+
+ binary_panel = page.locator('.files-editor-binary[data-rel-path="icon.png"]')
+ expect(binary_panel).to_be_visible(timeout=5000)
+
+ save_btn = page.locator(".files-editor-save")
+ expect(save_btn).to_have_text("Replace")
+ expect(save_btn).to_be_disabled()
+
+ expect(page.locator(".files-editor-replace-zone")).to_be_visible()
+ expect(page.locator(".files-editor-replace-browse")).to_be_visible()
+ # The file input is type=file hidden — Playwright's "visible"
+ # assertions treat hidden inputs as not visible, so check
+ # attached + the type instead.
+ expect(page.locator(".files-editor-replace-input")).to_have_attribute(
+ "type", "file"
+ )
+ # Download link points back at the overlay's /files/download.
+ download = page.locator("a.files-editor-download")
+ expect(download).to_have_attribute(
+ "href", f"/overlays/{overlay_id}/files/download?path=icon.png"
+ )
+ # In binary mode there's no