diff --git a/distgit/hooks.py b/distgit/hooks.py
index 6a2fbee..108af4c 100644
--- a/distgit/hooks.py
+++ b/distgit/hooks.py
@@ -38,7 +38,8 @@ Use this hook to:
 - Edit the message file in place (e.g. strip template comments)
 - Insert a standard prefix/suffix (e.g. branch name, ticket ID)
 - Add Signed-off-by from a template
-Takes 1–3 parameters: message file path, source (message|template|merge|squash|commit), and optionally commit hash for amend.
+Takes 1–3 parameters: message file path, source (message|template|merge|squash|commit),
+and optionally commit hash for amend.
 """
 
 
@@ -94,7 +95,8 @@ class PostCommit(Hook):
 
 """
 Pre-Merge-Commit Hook (Client-side)
-Runs after a merge has been carried out successfully and before the merge commit message is finalized; can be bypassed with --no-verify.
+Runs after a merge has been carried out successfully and before the merge commit
+message is finalized; can be bypassed with --no-verify.
 Use this hook to:
 - Validate the merged tree (e.g. run tests on the result)
 - Inspect or adjust the merge commit message
@@ -117,7 +119,8 @@ Called by git rebase; can be used to prevent a branch from being rebased.
 Use this hook to:
 - Block rebasing certain branches (e.g. main)
 - Run checks before rewriting history
-Takes one or two parameters: upstream ref, and optionally the branch being rebased (absent when rebasing the current branch).
+Takes one or two parameters: upstream ref, and optionally the branch being rebased
+(absent when rebasing the current branch).
 """
 
 
diff --git a/distgit/tasks.py b/distgit/tasks.py
index e6026c5..6260d3c 100644
--- a/distgit/tasks.py
+++ b/distgit/tasks.py
@@ -48,7 +48,10 @@ def _build_tag_raw(
 	message: str,
 	object_type: str = "commit",
 ) -> bytes:
-	"""Build tag object body (content only) for odb.write(4, data). libgit2 prepends 'tag <len>\\0'. object_type must match the target (e.g. 'commit' or 'tree')."""
+	"""Build tag object body (content only) for odb.write(4, data).
+
+	libgit2 prepends 'tag <len>\\0'. object_type must match the target (e.g. 'commit' or 'tree').
+	"""
 	content = f"object {str(target)}\ntype {object_type}\ntag {name}\ntagger {_tagger_str(tagger)}\n\n{message}"
 	return content.encode("utf-8")
 
diff --git a/pygitweb/actions.py b/pygitweb/actions.py
index 141f0c6..2aa913d 100644
--- a/pygitweb/actions.py
+++ b/pygitweb/actions.py
@@ -180,7 +180,10 @@ def _render_readme_card(
 	readme_content: str,
 	blob_dir: str = "",
 ) -> str:
-	"""Return HTML for the README card (and script for markdown). blob_dir is the current tree path for relative links."""
+	"""Return HTML for the README card (and script for markdown).
+
+	blob_dir is the current tree path for relative links.
+	"""
 	is_markdown = readme_filename.lower().endswith(".md")
 	blob_base = f"/project/{project}?a=blob&h={quote(ref_oid, safe='')}&f="
 	blob_base_attr = blob_base.replace("&", "&amp;").replace('"', "&quot;")
@@ -263,7 +266,9 @@ def git_object(project: str, h: str | None) -> Response:
 			site_name=config.SITE_NAME,
 		)
 		return HTMLResponse(
-			f"{pre}<h1>Blob {esc_html(oid_short)}</h1><p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>{blob_html}{POSTAMBLE}"
+			f"{pre}<h1>Blob {esc_html(oid_short)}</h1>"
+			f"<p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>"
+			f"{blob_html}{POSTAMBLE}"
 		)
 	raise HTTPException(status_code=404, detail="Unknown object type")
 
@@ -485,7 +490,9 @@ def git_history(
 	)
 	pagination_html = _render_pagination(request, page, pagecount, page > 1, has_next)
 	return HTMLResponse(
-		f"{pre}<h1>{title}</h1><p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>{table}{pagination_html}{POSTAMBLE}"
+		f"{pre}<h1>{title}</h1>"
+		f"<p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>"
+		f"{table}{pagination_html}{POSTAMBLE}"
 	)
 
 
@@ -568,7 +575,9 @@ def git_tags(project: str, request: Request, page: int, pagecount: int) -> HTMLR
 		request, page, pagecount, page > 1, page < total_pages, total_pages=total_pages
 	)
 	return HTMLResponse(
-		f"{pre}<h1>{title}</h1><p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>{table}{pagination_html}{POSTAMBLE}"
+		f"{pre}<h1>{title}</h1>"
+		f"<p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>"
+		f"{table}{pagination_html}{POSTAMBLE}"
 	)
 
 
@@ -623,7 +632,9 @@ def git_tag(project: str, h: str | None) -> HTMLResponse:
 	title = f"Tag {esc_html(tag_name)} - {esc_html(project)}"
 	pre = PREAMBLE.render(title=title, site_name=config.SITE_NAME)
 	return HTMLResponse(
-		f"{pre}<h1>Tag {esc_html(tag_name)}</h1><p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>{table}{POSTAMBLE}"
+		f"{pre}<h1>Tag {esc_html(tag_name)}</h1>"
+		f"<p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>"
+		f"{table}{POSTAMBLE}"
 	)
 
 
@@ -672,7 +683,9 @@ def git_commit(project: str, h: str | None) -> HTMLResponse:
 	title = f"Commit {esc_html(oid_short)} - {esc_html(project)}"
 	pre = PREAMBLE.render(title=title, site_name=config.SITE_NAME)
 	return HTMLResponse(
-		f"{pre}<h1>Commit {esc_html(oid_short)}</h1><p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>{table}{POSTAMBLE}"
+		f"{pre}<h1>Commit {esc_html(oid_short)}</h1>"
+		f"<p>Project: <a href='/project/{project}'>{esc_html(project)}</a></p>"
+		f"{table}{POSTAMBLE}"
 	)
 
 
diff --git a/pygitweb/config.py b/pygitweb/config.py
index f7a8257..f66282f 100644
--- a/pygitweb/config.py
+++ b/pygitweb/config.py
@@ -102,9 +102,9 @@ GIT = (GIT_BINDIR + "/git") if GIT_BINDIR else "git"
 MAXLOAD: float | None = None  # 300 in perl; None = disabled
 
 """
-The auth provider to use. "None" disables authentication entirely, bypassing distgit. This is useful for testing, development, or as a local admin panel - but USE WITH CAUTION.
-RootProvider: Mostly a stub but provides admin login with an admin user/password. All other providers inherit from this.
-SSHProvider: TODO - Will authenticate against a local SSH server using existing keys. Easiest coming from raw git-daemon.
+The auth provider to use. "None" disables authentication entirely, bypassing distgit. USE WITH CAUTION.
+RootProvider: Stub which provides admin login with a user/password. Parent class for other providers.
+SSHProvider: TODO - Will authenticate using existing SSH keys. Easiest coming from raw git-daemon.
 OAuth2Provider: TODO - Will authenticate against an OAuth2 server.
 OIDCProvider: TODO - Will authenticate against an OIDC server.
 MatrixProvider: TODO - Will authenticate against the Matrix network.
@@ -303,7 +303,10 @@ def filter_snapshot_fmts(fmts: list[str]) -> list[str]:
 
 
 def filter_and_validate_refs(refs: list[str], is_valid_ref_format: Any) -> list[str]:
-	"""Validate ref names and unique sort; 'heads' omitted (added in get_branch_refs). Port of filter_and_validate_refs."""
+	"""Validate ref names and unique sort; 'heads' omitted (added in get_branch_refs).
+
+	Port of filter_and_validate_refs.
+	"""
 	seen: set[str] = set()
 	for ref in refs:
 		if not is_valid_ref_format(ref):
diff --git a/pygitweb/settings.py b/pygitweb/settings.py
index 497c537..0ee4c08 100644
--- a/pygitweb/settings.py
+++ b/pygitweb/settings.py
@@ -95,15 +95,36 @@ def _render_form_field(name: str, value: Any, value_type: type, docstring: str, 
 	field_name = f"{name_prefix}{name}" if name_prefix else name
 	safe_name = field_name.replace(".", "_")
 	hint = f'<small class="form-hint text-muted">{esc_html(docstring)}</small>'
+	tpl = env.get_template("settings_form_field.html")
 	if value_type is bool:
-		checked = " checked" if value else ""
-		return f'<div class="mb-3"><div class="form-check"><input type="checkbox" class="form-check-input" id="{safe_name}" name="{field_name}" value="on"{checked}><label class="form-check-label" for="{safe_name}">{name}</label></div>{hint}</div>'
+		return tpl.render(
+			kind="bool",
+			safe_name=safe_name,
+			field_name=field_name,
+			name=name,
+			checked_attr=" checked" if value else "",
+			hint=hint,
+		)
 	if value_type in (int, float):
-		str_val = "" if value is None else str(value)
-		return f'<div class="mb-3"><label class="form-label" for="{safe_name}">{name}</label><input type="number" step="any" class="form-control" id="{safe_name}" name="{field_name}" value="{esc_html(str_val)}">{hint}</div>'
-	# str or None
-	str_val = "" if value is None else str(value)
-	return f'<div class="mb-3"><label class="form-label" for="{safe_name}">{name}</label><input type="text" class="form-control" id="{safe_name}" name="{field_name}" value="{esc_html(str_val)}">{hint}</div>'
+		raw = "" if value is None else str(value)
+		return tpl.render(
+			kind="number",
+			safe_name=safe_name,
+			field_name=field_name,
+			name=name,
+			str_val=esc_html(raw) or "",
+			hint=hint,
+			number_step="any",
+		)
+	raw = "" if value is None else str(value)
+	return tpl.render(
+		kind="text",
+		safe_name=safe_name,
+		field_name=field_name,
+		name=name,
+		str_val=esc_html(raw) or "",
+		hint=hint,
+	)
 
 
 # ---------- PyGit2 Settings schema ----------
@@ -219,21 +240,50 @@ def _render_pygit2_field(name: str, value: Any, kind: str, docstring: str, name_
 	field_name = f"{name_prefix}{name}"
 	safe_name = field_name.replace(".", "_")
 	hint = f'<small class="form-hint text-muted">{esc_html(docstring)}</small>'
+	tpl = env.get_template("settings_form_field.html")
 	if kind == "readonly":
-		str_val = str(value) if value != "" else "(not set)"
-		return f'<div class="mb-3"><label class="form-label" for="{safe_name}">{name}</label><input type="text" class="form-control bg-secondary" id="{safe_name}" name="{field_name}" value="{esc_html(str_val)}" readonly>{hint}</div>'
+		raw = str(value) if value != "" else "(not set)"
+		return tpl.render(
+			kind="readonly",
+			safe_name=safe_name,
+			field_name=field_name,
+			name=name,
+			str_val=esc_html(raw) or "",
+			hint=hint,
+		)
 	if kind == "bool":
-		checked = (
+		checked_attr = (
 			" checked"
 			if (value is True or (isinstance(value, str) and value.lower() in ("true", "1", "on", "yes")))
 			else ""
 		)
-		return f'<div class="mb-3"><div class="form-check"><input type="checkbox" class="form-check-input" id="{safe_name}" name="{field_name}" value="on"{checked}><label class="form-check-label" for="{safe_name}">{name}</label></div>{hint}</div>'
+		return tpl.render(
+			kind="bool",
+			safe_name=safe_name,
+			field_name=field_name,
+			name=name,
+			checked_attr=checked_attr,
+			hint=hint,
+		)
 	if kind == "int":
-		str_val = str(value) if value != "" and value is not None else ""
-		return f'<div class="mb-3"><label class="form-label" for="{safe_name}">{name}</label><input type="number" class="form-control" id="{safe_name}" name="{field_name}" value="{esc_html(str_val)}">{hint}</div>'
-	str_val = str(value) if value is not None else ""
-	return f'<div class="mb-3"><label class="form-label" for="{safe_name}">{name}</label><input type="text" class="form-control" id="{safe_name}" name="{field_name}" value="{esc_html(str_val)}">{hint}</div>'
+		raw = str(value) if value != "" and value is not None else ""
+		return tpl.render(
+			kind="number",
+			safe_name=safe_name,
+			field_name=field_name,
+			name=name,
+			str_val=esc_html(raw) or "",
+			hint=hint,
+		)
+	raw = str(value) if value is not None else ""
+	return tpl.render(
+		kind="text",
+		safe_name=safe_name,
+		field_name=field_name,
+		name=name,
+		str_val=esc_html(raw) or "",
+		hint=hint,
+	)
 
 
 # ---------- Project config (local/worktree only) ----------
@@ -257,8 +307,16 @@ def _project_config_table_row(key: str, value: str, index: int) -> list[str]:
 	"""One table row as [key_cell_html, value_cell_html] for project config form."""
 	key_name = f"config_key_{index}"
 	val_name = f"config_value_{index}"
-	key_cell = f'<input type="text" class="form-control form-control-sm" id="{key_name}" name="{key_name}" value="{esc_html(key)}" placeholder="e.g. user.name">'
-	val_cell = f'<input type="text" class="form-control form-control-sm" id="{val_name}" name="{val_name}" value="{esc_html(value)}" placeholder="value">'
+	key_cell = (
+		'<input type="text" class="form-control form-control-sm" '
+		f'id="{key_name}" name="{key_name}" '
+		f'value="{esc_html(key)}" placeholder="e.g. user.name">'
+	)
+	val_cell = (
+		'<input type="text" class="form-control form-control-sm" '
+		f'id="{val_name}" name="{val_name}" '
+		f'value="{esc_html(value)}" placeholder="value">'
+	)
 	return [key_cell, val_cell]
 
 
@@ -271,7 +329,7 @@ def settings_pygitweb_page(request: Request):
 		fields_html.append(_render_form_field(name, val, value_type, doc))
 	form_body = "\n".join(fields_html)
 	pre = PREAMBLE.render(title=f"{config.SITE_NAME} - PyGitWeb settings", site_name=config.SITE_NAME)
-	body = f'<h1 class="page-title">PyGitWeb settings</h1><div class="card"><div class="card-body"><form action="/settings/pygitweb/submit" method="post" class="needs-validation" novalidate>{form_body}<div class="form-footer"><button type="submit" class="btn btn-primary">Save</button><a href="/" class="btn btn-ghost-secondary">Cancel</a></div></form></div></div>'
+	body = env.get_template("pygitweb_settings.html").render(form_body=form_body)
 	return HTMLResponse(f"{pre}{body}{POSTAMBLE}")
 
 
@@ -310,7 +368,7 @@ def settings_pygit2_page(request: Request):
 		fields_html.append(_render_pygit2_field(name, val, kind, doc))
 	form_body = "\n".join(fields_html)
 	pre = PREAMBLE.render(title=f"{config.SITE_NAME} - PyGit2 settings", site_name=config.SITE_NAME)
-	body = f'<h1 class="page-title">PyGit2 settings</h1><div class="card"><div class="card-body"><form action="/settings/pygit2/submit" method="post" class="needs-validation" novalidate>{form_body}<div class="form-footer"><button type="submit" class="btn btn-primary">Save</button><a href="/" class="btn btn-ghost-secondary">Cancel</a></div></form></div></div>'
+	body = env.get_template("pygit2_settings.html").render(form_body=form_body)
 	return HTMLResponse(f"{pre}{body}{POSTAMBLE}")
 
 
@@ -384,7 +442,12 @@ def settings_project_page(request: Request, name: str):
 	)
 	submit_url = f"/settings/project/{_quote_path(project)}/submit"
 	cancel_url = f"/project/{_quote_path(project)}"
-	body = f'<h1 class="page-title">Project settings: {esc_html(project)}</h1><p class="text-muted">Repository config (local and worktree only).</p><div class="card"><div class="card-body"><form action="{esc_html(submit_url)}" method="post" class="needs-validation" novalidate>{table_html}<div class="form-footer mt-3"><button type="submit" class="btn btn-primary">Save</button><a href="{esc_html(cancel_url)}" class="btn btn-ghost-secondary">Cancel</a></div></form></div></div>'
+	body = env.get_template("project_settings.html").render(
+		project_title=esc_html(project),
+		submit_url=esc_html(submit_url),
+		cancel_url=esc_html(cancel_url),
+		table_html=table_html,
+	)
 	return HTMLResponse(f"{pre}{body}{POSTAMBLE}")
 
 
diff --git a/pygitweb/templates/project_settings.html b/pygitweb/templates/project_settings.html
new file mode 100644
index 0000000..ce932ce
--- /dev/null
+++ b/pygitweb/templates/project_settings.html
@@ -0,0 +1,13 @@
+<h1 class="page-title">Project settings: {{ project_title | safe }}</h1>
+<p class="text-muted">Repository config (local and worktree only).</p>
+<div class="card">
+  <div class="card-body">
+    <form action="{{ submit_url | safe }}" method="post" class="needs-validation" novalidate>
+      {{ table_html | safe }}
+      <div class="form-footer mt-3">
+        <button type="submit" class="btn btn-primary">Save</button>
+        <a href="{{ cancel_url | safe }}" class="btn btn-ghost-secondary">Cancel</a>
+      </div>
+    </form>
+  </div>
+</div>
diff --git a/pygitweb/templates/pygit2_settings.html b/pygitweb/templates/pygit2_settings.html
new file mode 100644
index 0000000..0b7eb8d
--- /dev/null
+++ b/pygitweb/templates/pygit2_settings.html
@@ -0,0 +1,12 @@
+<h1 class="page-title">PyGit2 settings</h1>
+<div class="card">
+  <div class="card-body">
+    <form action="/settings/pygit2/submit" method="post" class="needs-validation" novalidate>
+      {{ form_body | safe }}
+      <div class="form-footer">
+        <button type="submit" class="btn btn-primary">Save</button>
+        <a href="/" class="btn btn-ghost-secondary">Cancel</a>
+      </div>
+    </form>
+  </div>
+</div>
diff --git a/pygitweb/templates/pygitweb_settings.html b/pygitweb/templates/pygitweb_settings.html
new file mode 100644
index 0000000..625a807
--- /dev/null
+++ b/pygitweb/templates/pygitweb_settings.html
@@ -0,0 +1,12 @@
+<h1 class="page-title">PyGitWeb settings</h1>
+<div class="card">
+  <div class="card-body">
+    <form action="/settings/pygitweb/submit" method="post" class="needs-validation" novalidate>
+      {{ form_body | safe }}
+      <div class="form-footer">
+        <button type="submit" class="btn btn-primary">Save</button>
+        <a href="/" class="btn btn-ghost-secondary">Cancel</a>
+      </div>
+    </form>
+  </div>
+</div>
diff --git a/pygitweb/templates/settings_form_field.html b/pygitweb/templates/settings_form_field.html
new file mode 100644
index 0000000..01a6228
--- /dev/null
+++ b/pygitweb/templates/settings_form_field.html
@@ -0,0 +1,27 @@
+{% if kind == "bool" %}
+<div class="mb-3">
+  <div class="form-check">
+    <input type="checkbox" class="form-check-input" id="{{ safe_name }}" name="{{ field_name }}" value="on"{{ checked_attr | safe }}>
+    <label class="form-check-label" for="{{ safe_name }}">{{ name }}</label>
+  </div>
+  {{ hint | safe }}
+</div>
+{% elif kind == "number" %}
+<div class="mb-3">
+  <label class="form-label" for="{{ safe_name }}">{{ name }}</label>
+  <input type="number" {% if number_step %}step="{{ number_step }}" {% endif %}class="form-control" id="{{ safe_name }}" name="{{ field_name }}" value="{{ str_val | safe }}">
+  {{ hint | safe }}
+</div>
+{% elif kind == "readonly" %}
+<div class="mb-3">
+  <label class="form-label" for="{{ safe_name }}">{{ name }}</label>
+  <input type="text" class="form-control bg-secondary" id="{{ safe_name }}" name="{{ field_name }}" value="{{ str_val | safe }}" readonly>
+  {{ hint | safe }}
+</div>
+{% else %}
+<div class="mb-3">
+  <label class="form-label" for="{{ safe_name }}">{{ name }}</label>
+  <input type="text" class="form-control" id="{{ safe_name }}" name="{{ field_name }}" value="{{ str_val | safe }}">
+  {{ hint | safe }}
+</div>
+{% endif %}
