diff --git a/index.html b/index.html
new file mode 100644
index 0000000..0bbcf8d
--- /dev/null
+++ b/index.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en" class="landing-root">
+<head>
+	<meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<title>PyGitWeb</title>
+	<meta name="description" content="Hassle-free git repo management. A Python re-imagining of GitWeb with FastAPI, thin dependencies, and metadata stored in your repo.">
+	<link rel="preconnect" href="https://fonts.googleapis.com">
+	<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+	<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,400&family=Figtree:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap" rel="stylesheet">
+	<link rel="stylesheet" href="main.css">
+	<link rel="stylesheet" href="landing.css">
+	<link rel="icon" href="logo.svg" type="image/svg+xml">
+</head>
+<body class="landing">
+	<a class="skip-link" href="#main">Skip to content</a>
+
+	<header class="hero" aria-labelledby="site-title">
+		<div class="hero__mesh" aria-hidden="true"></div>
+		<div class="hero__inner">
+			<div class="hero__mark reveal" style="--reveal-delay: 0ms">
+				<img src="logo.svg" alt="" width="120" height="120" class="hero__logo">
+			</div>
+			<p class="hero__eyebrow reveal" style="--reveal-delay: 80ms">Project hosting, re-imagined</p>
+			<h1 id="site-title" class="hero__title reveal" style="--reveal-delay: 120ms">PyGitWeb</h1>
+			<p class="hero__tagline reveal" style="--reveal-delay: 180ms">Hassle-free git repo management platform</p>
+			<p class="hero__lede reveal" style="--reveal-delay: 240ms">
+				A ground-up rewrite of <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb">GitWeb</a> in Python with FastAPI:
+				Mostly API compatible with GitWeb with tons of additional features for modern workflows.
+				<a href="https://app.pygitweb.com">See it in action!</a>
+			</p>
+		</div>
+		<div class="hero__scroll-hint" aria-hidden="true">
+			<span class="hero__scroll-line"></span>
+		</div>
+	</header>
+
+	<main id="main" class="main">
+		<section id="what" class="section section--split reveal-section" aria-labelledby="what-heading">
+			<div class="section__container">
+				<h2 id="what-heading" class="section__heading reveal">What is PyGitWeb?</h2>
+				<div class="section__prose reveal">
+					<p>
+						The rewrite keeps dependencies thin: everything you need to run the stack, nothing you do not.
+						Aside from git-daemon, the usual suspects are PyGit2 for fast repo work, FastAPI and Uvicorn for the ASGI surface,
+						Jinja2 for templates, orjson for JSON, pydantic-settings for configuration, and python-ripgrep for search.
+					</p>
+					<p>
+						PyGit2 is the only dependency required by the internal tooling package, so you can script workflows without
+						bringing up the web server. There is also a plugin model if you want to extend the stack with your own tools.
+					</p>
+				</div>
+			</div>
+		</section>
+
+		<section class="section section--deps" aria-labelledby="deps-heading">
+			<div class="section__container">
+				<h2 id="deps-heading" class="section__heading reveal">Core dependencies</h2>
+				<ul class="dep-grid" role="list">
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">PyGit2</h3>
+						<p class="dep-card__desc">LibGit2 wrapper for fast manipulation and queries. Shared with standalone tooling.</p>
+					</li>
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">FastAPI + Uvicorn</h3>
+						<p class="dep-card__desc">ASGI server for rendered pages and internal workflow requests.</p>
+					</li>
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">Jinja2</h3>
+						<p class="dep-card__desc">Fast, safe templating.</p>
+					</li>
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">orjson</h3>
+						<p class="dep-card__desc">High-performance JSON serialization.</p>
+					</li>
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">pydantic-settings</h3>
+						<p class="dep-card__desc">Straightforward settings model.</p>
+					</li>
+					<li class="dep-card reveal">
+						<h3 class="dep-card__name">python-ripgrep</h3>
+						<p class="dep-card__desc">Fast file search across the tree.</p>
+					</li>
+				</ul>
+			</div>
+		</section>
+
+		<section id="apart" class="section section--philosophy reveal-section" aria-labelledby="apart-heading">
+			<div class="section__container">
+				<h2 id="apart-heading" class="section__heading reveal">What sets it apart?</h2>
+				<blockquote class="pullquote reveal">
+					<p>We believe all project metadata should live in-situ... discovering new merge requests and tasks should be a git fetch.</p>
+				</blockquote>
+				<div class="section__prose reveal">
+					<p>
+						Settings, merge requests, comments, Kanban tasks: it all lives in your repository object database.
+						No separate database service to provision. If you have a repo, you have what PyGitWeb needs.
+					</p>
+					<p class="section__cta">Setup is easy - give it a try.</p>
+				</div>
+			</div>
+		</section>
+
+		<section id="enterprise" class="section section--note reveal-section" aria-labelledby="enterprise-heading">
+			<div class="section__container">
+				<h2 id="enterprise-heading" class="section__heading reveal">Enterprise scale?</h2>
+				<div class="section__prose reveal">
+					<p>
+						Not yet - stay tuned for updates!
+					</p>
+				</div>
+			</div>
+		</section>
+	</main>
+
+	<footer class="site-footer">
+		<div class="site-footer__inner">
+			<p class="site-footer__text">PyGitWeb</p>
+			<p class="site-footer__meta text-muted"><code class="footer-code">(c) 2026 Will Bowers</code>.</p>
+		</div>
+	</footer>
+</body>
+</html>
diff --git a/landing.css b/landing.css
new file mode 100644
index 0000000..111e0cb
--- /dev/null
+++ b/landing.css
@@ -0,0 +1,430 @@
+/*
+ * PyGitWeb landing: extends ../pygitweb/static/main.css (--pgw-* tokens, links, .text-muted).
+ * Scroll-driven reveals use animation-timeline: view(); static fallbacks apply when unsupported.
+ */
+
+.landing-root {
+	scroll-behavior: smooth;
+}
+
+body.landing {
+	--landing-stone: #d1caa1;
+	--landing-slate: #66717e;
+	--landing-mist: #d4d6b9;
+	--landing-ink: #0f1218;
+	--landing-ink-soft: #1a1f2e;
+
+	margin: 0;
+	padding: 0;
+	border: none;
+	min-height: 100vh;
+	font-family: "Figtree", system-ui, sans-serif;
+	font-size: 1.0625rem;
+	line-height: 1.65;
+	background-color: var(--pgw-bg);
+	color: var(--pgw-text);
+}
+
+.landing h1,
+.landing h2,
+.landing h3 {
+	font-family: "Fraunces", Georgia, serif;
+	font-weight: 600;
+	font-variation-settings: "SOFT" 50, "WONK" 1;
+	color: var(--pgw-text);
+}
+
+.skip-link {
+	position: absolute;
+	left: -9999px;
+	z-index: 9999;
+	padding: 0.5rem 1rem;
+	background: var(--pgw-link);
+	color: var(--pgw-bg-raised);
+	font-weight: 600;
+}
+
+.skip-link:focus {
+	left: 1rem;
+	top: 1rem;
+}
+
+/* Hero */
+.hero {
+	position: relative;
+	min-height: min(92vh, 52rem);
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	padding: clamp(2rem, 6vw, 5rem) 1.5rem 4rem;
+	overflow: clip;
+	background:
+		radial-gradient(ellipse 120% 80% at 50% -20%, color-mix(in srgb, var(--landing-stone) 35%, transparent), transparent 55%),
+		radial-gradient(ellipse 80% 50% at 100% 60%, color-mix(in srgb, var(--landing-slate) 22%, transparent), transparent 50%),
+		radial-gradient(ellipse 70% 45% at 0% 70%, color-mix(in srgb, var(--landing-mist) 18%, transparent), transparent 45%),
+		var(--pgw-bg);
+}
+
+.hero__mesh {
+	position: absolute;
+	inset: -40% -20% -20%;
+	background:
+		repeating-linear-gradient(
+			-18deg,
+			transparent,
+			transparent 3px,
+			color-mix(in srgb, var(--pgw-text) 4%, transparent) 3px,
+			color-mix(in srgb, var(--pgw-text) 4%, transparent) 4px
+		);
+	opacity: 0.35;
+	animation: mesh-drift 28s linear infinite;
+	pointer-events: none;
+	mask-image: radial-gradient(ellipse 65% 55% at 50% 40%, black 20%, transparent 75%);
+}
+
+@keyframes mesh-drift {
+	0% {
+		transform: translate(0, 0) rotate(0deg);
+	}
+	100% {
+		transform: translate(-2%, 1.5%) rotate(0.5deg);
+	}
+}
+
+.hero__inner {
+	position: relative;
+	z-index: 1;
+	max-width: 38rem;
+	text-align: center;
+}
+
+.hero__mark {
+	margin-bottom: 1.25rem;
+	filter: drop-shadow(0 12px 28px color-mix(in srgb, var(--landing-slate) 45%, transparent));
+}
+
+.hero__logo {
+	display: block;
+	margin: 0 auto;
+	width: clamp(5rem, 18vw, 7.5rem);
+	height: auto;
+}
+
+@keyframes logo-float {
+	0%,
+	100% {
+		transform: translateY(0);
+	}
+	50% {
+		transform: translateY(-6px);
+	}
+}
+
+.hero__eyebrow {
+	margin: 0 0 0.35rem;
+	font-size: 0.8125rem;
+	font-weight: 600;
+	letter-spacing: 0.22em;
+	text-transform: uppercase;
+	color: var(--pgw-text-muted);
+}
+
+.hero__title {
+	margin: 0 0 0.5rem;
+	font-size: clamp(2.75rem, 8vw, 4.25rem);
+	font-weight: 700;
+	line-height: 1.05;
+	letter-spacing: -0.03em;
+	background: linear-gradient(
+		135deg,
+		var(--pgw-text) 0%,
+		color-mix(in srgb, var(--landing-slate) 75%, var(--pgw-text)) 100%
+	);
+	-webkit-background-clip: text;
+	background-clip: text;
+	color: transparent;
+}
+
+@media (prefers-reduced-motion: reduce) {
+	.hero__mesh,
+	.hero__logo {
+		animation: none;
+	}
+}
+
+.hero__tagline {
+	margin: 0 0 1.25rem;
+	font-size: clamp(1.15rem, 2.8vw, 1.45rem);
+	font-weight: 500;
+	color: var(--pgw-text-muted);
+}
+
+.hero__lede {
+	margin: 0;
+	color: var(--pgw-text);
+	text-wrap: pretty;
+}
+
+.hero__scroll-hint {
+	position: absolute;
+	bottom: 1.25rem;
+	left: 50%;
+	transform: translateX(-50%);
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	gap: 0.5rem;
+	opacity: 0.5;
+}
+
+.hero__scroll-line {
+	width: 1px;
+	height: 2.5rem;
+	background: linear-gradient(to bottom, var(--pgw-text-muted), transparent);
+	animation: scroll-pulse 2.2s ease-in-out infinite;
+}
+
+@keyframes scroll-pulse {
+	0%,
+	100% {
+		opacity: 0.35;
+		transform: scaleY(0.85);
+		transform-origin: top;
+	}
+	50% {
+		opacity: 1;
+		transform: scaleY(1);
+		transform-origin: top;
+	}
+}
+
+@media (prefers-reduced-motion: reduce) {
+	.hero__scroll-line {
+		animation: none;
+	}
+}
+
+/* Main sections */
+.main {
+	position: relative;
+}
+
+.section {
+	padding: clamp(3.5rem, 10vw, 6rem) 1.5rem;
+}
+
+.section__container {
+	max-width: 52rem;
+	margin: 0 auto;
+}
+
+.section__heading {
+	margin: 0 0 1.5rem;
+	font-size: clamp(1.75rem, 4vw, 2.25rem);
+	letter-spacing: -0.02em;
+}
+
+.section__prose p {
+	margin: 0 0 1rem;
+	text-wrap: pretty;
+}
+
+.section__prose p:last-child {
+	margin-bottom: 0;
+}
+
+.section--split {
+	background: var(--pgw-bg-muted);
+	border-top: 1px solid var(--pgw-border);
+	border-bottom: 1px solid var(--pgw-border);
+}
+
+.section--deps {
+	background: var(--pgw-bg);
+}
+
+.section--philosophy {
+	background: linear-gradient(
+		180deg,
+		var(--pgw-bg-raised) 0%,
+		color-mix(in srgb, var(--landing-mist) 12%, var(--pgw-bg-muted)) 100%
+	);
+	border-top: 1px solid var(--pgw-border);
+}
+
+.section--note {
+	background: var(--pgw-bg-muted);
+	border-top: 1px solid var(--pgw-border);
+}
+
+.section__cta {
+	font-weight: 600;
+	color: var(--pgw-text);
+}
+
+/* Dependency grid */
+.dep-grid {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	display: grid;
+	gap: 1rem;
+	grid-template-columns: 1fr;
+}
+
+@media (min-width: 40rem) {
+	.dep-grid {
+		grid-template-columns: repeat(2, 1fr);
+	}
+}
+
+@media (min-width: 56rem) {
+	.dep-grid {
+		grid-template-columns: repeat(3, 1fr);
+	}
+}
+
+.dep-card {
+	margin: 0;
+	padding: 1.25rem 1.35rem;
+	background: var(--pgw-bg-raised);
+	border: 1px solid var(--pgw-border);
+	border-radius: 2px;
+	box-shadow: 0 1px 0 color-mix(in srgb, var(--pgw-text) 6%, transparent);
+	transition:
+		border-color 0.25s ease,
+		box-shadow 0.25s ease,
+		transform 0.25s ease;
+}
+
+.dep-card:hover {
+	border-color: color-mix(in srgb, var(--landing-slate) 45%, var(--pgw-border));
+	box-shadow: 0 8px 24px color-mix(in srgb, var(--landing-slate) 12%, transparent);
+	transform: translateY(-2px);
+}
+
+@media (prefers-reduced-motion: reduce) {
+	.dep-card {
+		transition: border-color 0.2s ease;
+	}
+
+	.dep-card:hover {
+		transform: none;
+	}
+}
+
+.dep-card__name {
+	margin: 0 0 0.4rem;
+	font-size: 1.125rem;
+	font-weight: 600;
+}
+
+.dep-card__desc {
+	margin: 0;
+	font-size: 0.9375rem;
+	color: var(--pgw-text-muted);
+	line-height: 1.55;
+}
+
+/* Pull quote */
+.pullquote {
+	margin: 0 0 1.75rem;
+	padding: 1.5rem 1.25rem 1.5rem 1.75rem;
+	border-left: 4px solid var(--landing-slate);
+	background: color-mix(in srgb, var(--pgw-bg-raised) 92%, var(--landing-stone));
+	border-radius: 0 4px 4px 0;
+}
+
+.pullquote p {
+	margin: 0;
+	font-family: "Fraunces", Georgia, serif;
+	font-size: clamp(1.2rem, 2.5vw, 1.45rem);
+	font-style: italic;
+	font-weight: 400;
+	line-height: 1.45;
+	color: var(--pgw-text);
+}
+
+/* Footer */
+.site-footer {
+	padding: 2rem 1.5rem 2.5rem;
+	border-top: 1px solid var(--pgw-border);
+	background: var(--pgw-bg-raised);
+}
+
+.site-footer__inner {
+	max-width: 52rem;
+	margin: 0 auto;
+	text-align: center;
+}
+
+.site-footer__text {
+	margin: 0 0 0.35rem;
+	font-family: "Fraunces", Georgia, serif;
+	font-weight: 600;
+	font-size: 1.125rem;
+}
+
+.site-footer__meta {
+	margin: 0;
+	font-size: 0.875rem;
+}
+
+.footer-code {
+	font-size: 0.85em;
+	padding: 0.1em 0.35em;
+	background: var(--pgw-code-bg);
+	border: 1px solid var(--pgw-code-border);
+	border-radius: 3px;
+	color: var(--pgw-text);
+}
+
+/* Scroll-driven reveal (progressive enhancement) */
+.reveal {
+	opacity: 0.97;
+	transform: translateY(1.25rem);
+	animation: reveal-fallback 0.9s ease-out forwards;
+	animation-delay: var(--reveal-delay, 0ms);
+}
+
+@keyframes reveal-fallback {
+	to {
+		opacity: 1;
+		transform: translateY(0);
+	}
+}
+
+@keyframes reveal-in {
+	from {
+		opacity: 0;
+		transform: translateY(2rem);
+	}
+	to {
+		opacity: 1;
+		transform: translateY(0);
+	}
+}
+
+@supports (animation-timeline: view()) {
+	.reveal {
+		opacity: 0;
+		transform: translateY(2rem);
+		animation: reveal-in linear both;
+		animation-timeline: view();
+		animation-range: entry 0% cover 42%;
+		animation-delay: var(--reveal-delay, 0ms);
+	}
+
+	.hero .reveal {
+		animation-range: entry -5% cover 55%;
+	}
+}
+
+@media (prefers-reduced-motion: reduce) {
+	.reveal {
+		opacity: 1;
+		transform: none;
+		animation: none;
+	}
+}
diff --git a/logo.svg b/logo.svg
new file mode 100644
index 0000000..07fdaf8
--- /dev/null
+++ b/logo.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="200mm"
+   height="200mm"
+   viewBox="0 0 200 200"
+   version="1.1"
+   id="svg1"
+   xml:space="preserve"
+   inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
+   sodipodi:docname="logo.svg"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
+     id="namedview1"
+     pagecolor="#505050"
+     bordercolor="#eeeeee"
+     borderopacity="1"
+     inkscape:showpageshadow="0"
+     inkscape:pageopacity="0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#505050"
+     inkscape:document-units="mm"
+     inkscape:zoom="1"
+     inkscape:cx="323.5"
+     inkscape:cy="530.5"
+     inkscape:window-width="2560"
+     inkscape:window-height="1377"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg1" /><defs
+     id="defs1" /><path
+     style="display:inline;fill:#d1caa1;fill-opacity:1"
+     d="m 97.817306,196.01379 c -3.976529,-0.62308 -4.916609,-1.33611 -14.942115,-11.33339 l -8.54507,-8.52101 11.921972,-12.41383 11.921972,-12.41382 1.66968,-0.10808 c 2.896735,-0.18751 4.919985,-1.45651 6.076255,-3.81107 0.52443,-1.06793 0.64305,-1.62247 0.65685,-3.07052 l 0.0168,-1.76721 17.29996,-18.01769 17.29997,-18.01769 h 1.50968 c 2.89943,0 5.10893,-1.3424 6.33535,-3.84912 0.53364,-1.09072 0.63871,-1.59718 0.62889,-3.031258 -0.019,-2.77173 -1.30296,-4.820704 -3.83881,-6.126024 -0.96933,-0.49896 -1.51901,-0.61659 -2.96481,-0.63443 l -1.77487,-0.0219 -17.26451,-17.70036 -17.26451,-17.70035 0.0172,-1.89942 0.0172,-1.89942 11.91107,-12.423755 11.91107,-12.423751 30.00676,29.966026 c 16.50372,16.48131 30.51614,30.63699 31.1387,31.45706 3.69979,4.87355 4.15422,11.494262 1.18874,17.318962 -0.76292,1.4985 -3.62223,4.41455 -42.81597,43.66553 -24.2034,24.23874 -42.50117,42.38793 -43.18491,42.83423 -2.37122,1.54779 -6.17366,2.37459 -8.932604,1.94229 z"
+     id="path16" /><path
+     style="display:inline;fill:#66717e;fill-opacity:1"
+     d="M 38.70616,140.50922 C 5.3912695,107.1585 7.0026395,108.89534 5.5943025,104.81906 c -0.568758,-1.64622 -0.63461,-2.15391 -0.639707,-4.931888 -0.0065,-3.53901 0.260176,-4.698864 1.766823,-7.684574 0.912758,-1.80881 1.520044,-2.42915 42.3536585,-43.26423 L 90.502699,7.509257 92.452516,6.571881 c 2.673739,-1.2854018 4.465959,-1.7109258 7.22579,-1.7156068 3.580634,-0.0061 6.154394,0.761741 9.043644,2.6979328 0.93676,0.627756 4.39693,3.905702 9.13134,8.65045 l 7.62303,7.639679 -11.94859,12.444415 -11.94858,12.444417 h -1.57264 c -2.293034,0 -3.808064,0.63413 -5.228694,2.18852 -1.435912,1.57112 -1.942184,2.99117 -1.832086,5.13886 l 0.08026,1.56565 -17.089566,17.80306 -17.089564,17.80306 -1.50752,10e-4 c -1.958276,0.002 -3.376441,0.50219 -4.684148,1.65328 -1.619739,1.425754 -2.268187,2.878364 -2.283337,5.114992 -0.01608,2.37328 0.741107,4.06081 2.416148,5.38485 1.429386,1.12987 2.456775,1.47446 4.433037,1.48689 l 1.585644,0.01 17.109653,17.78547 17.109653,17.78546 v 2.03949 2.03949 l -9.153703,9.53698 c -5.034537,5.24534 -10.369101,10.78469 -11.854588,12.30966 l -2.700884,2.77268 z"
+     id="path17" /><path
+     style="display:inline;fill:#d4d6b9;fill-opacity:1"
+     d="m 81.019002,119.80676 -17.125558,-17.81509 0.01474,-1.94288 0.01474,-1.942868 17.142156,-17.849074 17.142155,-17.84907 1.63417,-0.0144 1.634164,-0.0144 17.29749,17.71122 17.2975,17.711234 0.018,1.93767 0.018,1.937668 -17.25738,17.96613 -17.25737,17.96612 -1.723634,0.006 -1.72363,0.006 z"
+     id="path18" /></svg>
diff --git a/main.css b/main.css
new file mode 100644
index 0000000..ef6cd69
--- /dev/null
+++ b/main.css
@@ -0,0 +1,886 @@
+/*
+ * Custom themes: load an extra stylesheet after this one that redefines
+ * the same --pgw-* (and optional --d2h-*) variables on :root.
+ * Example: <link rel="stylesheet" href="static/themes/solarized.css">
+ */
+
+:root {
+	color-scheme: light;
+	/* Surfaces */
+	--pgw-bg: #fafafa;
+	--pgw-bg-raised: #ffffff;
+	--pgw-bg-muted: #f4f4f5;
+	--pgw-border: #e4e4e7;
+	--pgw-border-muted: #d4d4d8;
+
+	--pgw-text: #18181b;
+	--pgw-text-muted: #52525b;
+	--pgw-text-dim: #71717a;
+
+	--pgw-link: #2563eb;
+	--pgw-link-visited: #7c3aed;
+	--pgw-link-hover: #dc2626;
+
+	--pgw-accent-bg: #e4e4e7;
+	--pgw-accent-text: #18181b;
+	--pgw-title-bg: #f4f4f5;
+	--pgw-title-hover-bg: #e4e4e7;
+
+	--pgw-code-bg: #f4f4f5;
+	--pgw-code-border: #e4e4e7;
+
+	--pgw-table-stripe: #fafafa;
+	--pgw-table-hover: #f4f4f5;
+
+	--pgw-add-fg: #166534;
+	--pgw-add-bg: #dcfce7;
+	--pgw-rem-fg: #991b1b;
+	--pgw-rem-bg: #fee2e2;
+	--pgw-info-fg: #52525b;
+
+	--pgw-blob-selection-bg: rgba(193, 173, 0, 0.35);
+
+	--pgw-navbar-bg: #e2e8f0;
+	--pgw-navbar-text: #1e293b;
+	--pgw-navbar-text-muted: #64748b;
+	--pgw-navbar-link-hover-bg: #cbd5e1;
+	--pgw-navbar-active-bg: #94a3b8;
+	--pgw-navbar-active-text: #ffffff;
+	--pgw-navbar-border: #cbd5e1;
+	--pgw-navbar-toggler-border: #94a3b8;
+	--pgw-navbar-toggler-icon: #475569;
+
+	/* diff2html (optional overrides) */
+	--d2h-info-bg-color: #f0f0f0;
+	--d2h-file-header-bg-color: rgb(220, 220, 255);
+	--d2h-empty-placeholder-bg-color: #f5f5f5;
+	--d2h-del-bg-color: rgb(255, 220, 220);
+	--d2h-ins-bg-color: rgb(220, 255, 220);
+	--d2h-dim-color: #666;
+	--d2h-bg-color: #fff;
+}
+
+@media (prefers-color-scheme: dark) {
+	:root {
+		color-scheme: dark;
+		--pgw-bg: #1a1a1e;
+		--pgw-bg-raised: #25252c;
+		--pgw-bg-muted: #2d2d36;
+		--pgw-border: #3d3d48;
+		--pgw-border-muted: #35353d;
+
+		--pgw-text: #e4e4e7;
+		--pgw-text-muted: #a1a1aa;
+		--pgw-text-dim: #71717a;
+
+		--pgw-link: #7dd3fc;
+		--pgw-link-visited: #c4b5fd;
+		--pgw-link-hover: #fda4af;
+
+		--pgw-accent-bg: #27272a;
+		--pgw-accent-text: #fafafa;
+		--pgw-title-bg: #2d2d36;
+		--pgw-title-hover-bg: #3d3d48;
+
+		--pgw-code-bg: #18181b;
+		--pgw-code-border: #27272a;
+
+		--pgw-table-stripe: #222226;
+		--pgw-table-hover: #2d2d36;
+
+		--pgw-add-fg: #86efac;
+		--pgw-add-bg: #14532d;
+		--pgw-rem-fg: #fca5a5;
+		--pgw-rem-bg: #7f1d1d;
+		--pgw-info-fg: #a1a1aa;
+
+		--pgw-blob-selection-bg: rgba(193, 173, 0, 0.4);
+
+		--pgw-navbar-bg: #2d2d4a;
+		--pgw-navbar-text: #e2e8f0;
+		--pgw-navbar-text-muted: #94a3b8;
+		--pgw-navbar-link-hover-bg: #3d3d5c;
+		--pgw-navbar-active-bg: #4f4f7a;
+		--pgw-navbar-active-text: #f1f5f9;
+		--pgw-navbar-border: #3d3d48;
+		--pgw-navbar-toggler-border: #64748b;
+		--pgw-navbar-toggler-icon: #94a3b8;
+
+		--d2h-info-bg-color: #222;
+		--d2h-file-header-bg-color: rgb(38, 38, 98);
+		--d2h-empty-placeholder-bg-color: #222;
+		--d2h-del-bg-color: rgb(98, 38, 38);
+		--d2h-ins-bg-color: rgb(38, 98, 38);
+		--d2h-dim-color: #b0b0b0;
+		--d2h-bg-color: #222;
+	}
+}
+
+html {
+	box-sizing: border-box;
+}
+
+*, *::before, *::after {
+	box-sizing: inherit;
+}
+
+body {
+	font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
+	font-size: 0.875rem;
+	line-height: 1.5;
+	margin: 0;
+	padding: 0.75rem 1rem;
+	background-color: var(--pgw-bg);
+	color: var(--pgw-text);
+	border: 1px solid var(--pgw-border);
+	height: auto;
+
+	&:has(.page) {
+		padding: 0;
+	}
+}
+
+/* Card: override Tabler so cards use color-scheme variables */
+.card {
+	background-color: var(--pgw-bg-raised);
+	border-color: var(--pgw-border);
+	color: var(--pgw-text);
+
+	.card-header {
+		background-color: var(--pgw-accent-bg);
+		border-bottom-color: var(--pgw-border);
+		color: var(--pgw-accent-text);
+	}
+
+	.card-title {
+		color: var(--pgw-accent-text);
+	}
+
+	.card-body {
+		background-color: var(--pgw-bg-raised);
+		color: var(--pgw-text);
+	}
+}
+
+/* Table: override Tabler .table / .card-table so tables use color-scheme variables */
+.table,
+.table.card-table {
+	background-color: var(--pgw-bg-raised);
+	border-color: var(--pgw-border);
+	color: var(--pgw-text);
+
+	thead th,
+	th {
+		background-color: var(--pgw-accent-bg);
+		border-bottom-color: var(--pgw-border-muted);
+		color: var(--pgw-accent-text);
+	}
+
+	td {
+		border-top-color: var(--pgw-border-muted);
+		border-bottom-color: var(--pgw-border-muted);
+		color: var(--pgw-text);
+	}
+
+	tbody tr {
+		&:nth-child(even) {
+			background-color: var(--pgw-table-stripe);
+		}
+
+		&:hover {
+			background-color: var(--pgw-table-hover);
+
+			td {
+				background-color: var(--pgw-table-hover);
+			}
+		}
+	}
+}
+
+/* Form: override Tabler form controls so they use color-scheme variables */
+.form-label,
+.form-check-label {
+	color: var(--pgw-text);
+}
+
+.form-label.required::after {
+	color: var(--pgw-text-muted);
+}
+
+.form-hint {
+	color: var(--pgw-text-muted);
+
+	code {
+		background-color: var(--pgw-code-bg);
+		border-color: var(--pgw-code-border);
+		color: var(--pgw-text);
+	}
+}
+
+.form-control,
+.form-select {
+	background-color: var(--pgw-bg-raised);
+	border-color: var(--pgw-border);
+	color: var(--pgw-text);
+
+	&::placeholder {
+		color: var(--pgw-text-muted);
+	}
+
+	&:focus {
+		background-color: var(--pgw-bg-raised);
+		border-color: var(--pgw-link);
+		color: var(--pgw-text);
+	}
+
+	&:disabled {
+		background-color: var(--pgw-bg-muted);
+		color: var(--pgw-text-muted);
+	}
+}
+
+.form-check-input {
+	background-color: var(--pgw-bg-raised);
+	border-color: var(--pgw-border);
+
+	&:checked {
+		background-color: var(--pgw-link);
+		border-color: var(--pgw-link);
+	}
+
+	&:focus {
+		border-color: var(--pgw-link);
+	}
+}
+
+.form-footer {
+	border-top-color: var(--pgw-border);
+}
+
+/* Buttons: primary and ghost variants */
+.btn-primary {
+	background-color: var(--pgw-link);
+	border-color: var(--pgw-link);
+	color: var(--pgw-accent-text);
+
+	&:hover {
+		background-color: var(--pgw-link-hover);
+		border-color: var(--pgw-link-hover);
+		color: var(--pgw-accent-text);
+	}
+}
+
+.btn-ghost-secondary {
+	background-color: transparent;
+	border-color: var(--pgw-border);
+	color: var(--pgw-text);
+
+	&:hover {
+		background-color: var(--pgw-bg-muted);
+		border-color: var(--pgw-border);
+		color: var(--pgw-text);
+	}
+}
+
+/* Dropzone (file upload area) */
+.dropzone {
+	background-color: var(--pgw-bg-muted);
+	border-color: var(--pgw-border);
+	color: var(--pgw-text);
+
+	.dropzone-placeholder,
+	.text-muted {
+		color: var(--pgw-text-muted);
+	}
+
+	&.border-primary {
+		border-color: var(--pgw-link);
+	}
+}
+
+radio-picker {
+	display: flex;
+
+	label {
+		box-shadow: inset 0 0 1.2px 0 #000;
+		padding: 0.5rem;
+		cursor: pointer;
+		background: #0002;
+
+		&:has(input:checked) {
+			box-shadow: inset 0 0 0.2rem 0 #888;
+		}
+
+		&:has(input:focus-visible) {
+			outline: 2px solid #000;
+		}
+
+		&:hover {
+			background: #0004;
+		}
+
+		&:active {
+			background: #0006;
+		}
+	}
+
+	input {
+		opacity: 0;
+		position: absolute;
+		pointer-events: none;
+	}
+}
+
+/* Fluid vertical layout: fixed sidebar + main content offset */
+.page {
+	.navbar-vertical.position-fixed {
+		top: 0;
+		bottom: 0;
+		left: 0;
+		z-index: 1030;
+		background-color: var(--pgw-navbar-bg);
+	}
+
+	.navbar-vertical.position-fixed ~ .page-wrapper {
+		margin-left: var(--tblr-navbar-vertical-width, 15rem);
+	}
+}
+
+a {
+	color: var(--pgw-link);
+	text-decoration: none;
+
+	&:hover {
+		color: var(--pgw-link-hover);
+	}
+
+	&:visited {
+		color: var(--pgw-link-visited);
+	}
+}
+
+h1, h2, h3 {
+	margin: 0 0 0.5em;
+	color: var(--pgw-accent-text);
+	font-weight: 600;
+}
+
+/* Tables */
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+	background-color: var(--pgw-bg-raised);
+	border: 1px solid var(--pgw-border);
+}
+
+th, td {
+	padding: 0.1rem 0.2rem;
+	text-align: left;
+	vertical-align: top;
+	border-bottom: 1px solid var(--pgw-border-muted);
+}
+
+p {
+	color: var(--pgw-text);
+}
+
+th {
+	background-color: var(--pgw-accent-bg);
+	color: var(--pgw-accent-text);
+	font-weight: 600;
+}
+
+#environment {
+	background-color: var(--pgw-bg-raised);
+	color: var(--pgw-text);
+}
+
+#environment tr:nth-child(2n+1) {
+	background-color: var(--pgw-table-stripe);
+}
+
+tbody tr {
+	&:nth-child(even) {
+		background-color: var(--pgw-table-stripe);
+	}
+
+	&:hover {
+		background-color: var(--pgw-table-hover);
+	}
+}
+
+/* Optional: page header / footer blocks for future use */
+.page_header,
+.page_footer {
+	padding: 0.5rem 0.75rem;
+	background-color: var(--pgw-accent-bg);
+	color: var(--pgw-accent-text);
+	border: 1px solid var(--pgw-border);
+}
+
+.page_footer_text {
+	color: var(--pgw-text-muted);
+	font-style: italic;
+}
+
+.page_body {
+	padding: 0.5rem;
+	font-family: ui-monospace, monospace;
+	background-color: var(--pgw-code-bg);
+	border: 1px solid var(--pgw-code-border);
+}
+
+.title, .title_text {
+	background-color: var(--pgw-title-bg);
+	padding: 0.375rem 0.5rem;
+	border-bottom: 1px solid var(--pgw-border);
+}
+
+.title:hover, a.title:hover {
+	background-color: var(--pgw-title-hover-bg);
+}
+
+/* Vertical navbar: variables and link states */
+.navbar-vertical {
+	.nav-link {
+		color: var(--pgw-navbar-text);
+
+		&:hover {
+			background-color: var(--pgw-navbar-link-hover-bg);
+			color: var(--pgw-navbar-text);
+
+			.nav-link-title,
+			.text-reset {
+				color: var(--pgw-navbar-text);
+			}
+		}
+
+		&.active {
+			background-color: var(--pgw-navbar-active-bg);
+			color: var(--pgw-navbar-active-text);
+
+			.nav-link-title {
+				color: var(--pgw-navbar-active-text);
+			}
+		}
+	}
+
+	.navbar-brand .text-reset,
+	.nav-link-title {
+		color: var(--pgw-navbar-text);
+	}
+
+	.navbar-toggler {
+		border-color: var(--pgw-navbar-toggler-border);
+	}
+
+	.navbar-toggler-icon {
+		background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23475569' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+	}
+
+	.nav-link-icon {
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		width: 1.5rem;
+		height: 1.5rem;
+
+		svg {
+			width: 1.25rem;
+			height: 1.25rem;
+		}
+	}
+}
+
+[data-bs-theme="dark"] {
+	.navbar-vertical .navbar-toggler-icon {
+		background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%2394a3b8' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+	}
+}
+
+.navbar-user {
+	border-top-color: var(--pgw-navbar-border) !important;
+
+	.navbar-user-name {
+		color: var(--pgw-navbar-text);
+	}
+
+	.navbar-user-meta,
+	.text-muted {
+		color: var(--pgw-navbar-text-muted);
+	}
+
+	.avatar svg {
+		width: 1.25rem;
+		height: 1.25rem;
+	}
+}
+
+/* Diff/status helpers (use var(--pgw-add-*) etc. in markup if needed) */
+.diff-add { color: var(--pgw-add-fg); background-color: var(--pgw-add-bg); }
+.diff-rem { color: var(--pgw-rem-fg); background-color: var(--pgw-rem-bg); }
+.text-muted { color: var(--pgw-text-muted); }
+
+/* Blob view: line numbers + code with syntax highlighting */
+.blob-view {
+	display: flex;
+	margin: 0.5rem 0;
+	max-width: 100%;
+	overflow: auto;
+	background-color: var(--pgw-code-bg);
+	border: 1px solid var(--pgw-code-border);
+
+	.blob-line-nums {
+		flex-shrink: 0;
+		user-select: none;
+		padding: 0.75rem 1rem 0.75rem 0.5rem;
+		text-align: right;
+		color: var(--pgw-text-muted);
+		white-space: pre;
+		font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
+		font-size: 0.9rem;
+		line-height: 1.5;
+		border-right: 1px solid var(--pgw-border);
+	}
+
+	.blob-content {
+		flex: 1;
+		margin: 0;
+		padding: 0.75rem 1rem;
+		overflow: auto;
+		font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
+		font-size: 0.9rem;
+		line-height: 1.5;
+		min-width: 0;
+
+		code {
+			background: none;
+			padding: 0;
+			font-size: inherit;
+			line-height: inherit;
+		}
+	}
+
+	.blob-line-selected,
+	.blob-selection {
+		background-color: var(--pgw-blob-selection-bg);
+	}
+}
+
+/* README markdown: base and all elements use color-scheme variables */
+.markdown-body {
+	color: var(--pgw-text);
+	background-color: transparent;
+
+	h1, h2, h3, h4, h5, h6 {
+		color: var(--pgw-accent-text);
+		border-bottom-color: var(--pgw-border-muted);
+	}
+
+	a {
+		color: var(--pgw-link);
+
+		&:hover {
+			color: var(--pgw-link-hover);
+		}
+
+		&:visited {
+			color: var(--pgw-link-visited);
+		}
+	}
+
+	code {
+		background-color: var(--pgw-code-bg);
+		color: var(--pgw-text);
+		border: 1px solid var(--pgw-code-border);
+	}
+
+	pre {
+		background-color: var(--pgw-code-bg);
+		border: 1px solid var(--pgw-code-border);
+		color: var(--pgw-text);
+
+		code {
+			background: none;
+			border: none;
+		}
+	}
+
+	blockquote {
+		border-left: 0.25rem solid var(--pgw-border);
+		color: var(--pgw-text-muted);
+		padding-left: 1rem;
+		margin-left: 0;
+	}
+
+	hr {
+		border-color: var(--pgw-border-muted);
+		background-color: var(--pgw-border-muted);
+	}
+
+	table {
+		background-color: var(--pgw-bg-raised);
+		border-color: var(--pgw-border);
+	}
+
+	th, td {
+		border-color: var(--pgw-border-muted);
+	}
+
+	th {
+		background-color: var(--pgw-accent-bg);
+		color: var(--pgw-accent-text);
+	}
+
+	tbody tr {
+		&:nth-child(even) {
+			background-color: var(--pgw-table-stripe);
+		}
+
+		&:hover {
+			background-color: var(--pgw-table-hover);
+		}
+	}
+
+	img {
+		background-color: var(--pgw-bg-muted);
+	}
+
+	.readme-heading-wrap {
+		position: relative;
+		display: block;
+	}
+
+	.readme-section-anchor {
+		position: absolute;
+		right: 100%;
+		padding-right: 0.35rem;
+		opacity: 0;
+		color: var(--pgw-text-muted);
+		font-size: 1.3rem;
+		text-decoration: none;
+		transition: opacity 0.15s ease;
+
+		&:focus {
+			opacity: 1;
+		}
+	}
+
+	.readme-heading-wrap:hover .readme-section-anchor,
+	.readme-section-anchor:hover {
+		opacity: 1;
+	}
+}
+
+.readme-toc-mount {
+	position: fixed;
+	left: var(--tblr-navbar-vertical-width, 15rem);
+	top: 0;
+	bottom: 0;
+	width: 12rem;
+	z-index: 1040;
+	pointer-events: none;
+	overflow: hidden;
+
+	.readme-toc {
+		pointer-events: auto;
+		padding: 1rem 0.5rem 1rem 0;
+		max-height: 100%;
+		overflow-y: auto;
+	}
+}
+
+.readme-toc-list {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	font-size: 0.8rem;
+}
+
+.readme-toc-item {
+	margin: 0.2rem 0;
+}
+
+.readme-toc-link {
+	display: block;
+	padding: 0.2rem 0.4rem;
+	color: var(--pgw-text-muted);
+	text-decoration: none;
+	border-radius: 0.25rem;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+
+	&:hover {
+		color: var(--pgw-link-hover);
+		background-color: var(--pgw-bg-raised);
+	}
+}
+
+.readme-toc-item.readme-toc-active .readme-toc-link {
+	color: var(--pgw-link);
+	font-weight: 600;
+	background-color: var(--pgw-accent-bg);
+}
+
+.readme-toc-level-1 { padding-left: 0; }
+.readme-toc-level-2 { padding-left: 0.5rem; }
+.readme-toc-level-3 { padding-left: 1rem; }
+.readme-toc-level-4 { padding-left: 1.5rem; }
+.readme-toc-level-5 { padding-left: 2rem; }
+.readme-toc-level-6 { padding-left: 2.5rem; }
+
+/* Board view: lift container max-width so columns can use full width; keep centered */
+.container-xl:has(.board-columns) {
+	max-width: none;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+/* Board view: center the column group when there's space; still scroll when many columns */
+.board-columns {
+	justify-content: center;
+}
+
+/* Board view columns: use theme variables so they respect light/dark */
+.board-column {
+	background-color: var(--pgw-bg-muted);
+	color: var(--pgw-text);
+	border-color: var(--pgw-border) !important;
+
+	.h6,
+	.text-muted {
+		color: var(--pgw-text-muted) !important;
+	}
+}
+
+/* Board: Sync status icons (mask so currentColor is respected when loaded) */
+.board-card-icon-wrap {
+	opacity: 0.7;
+	color: var(--pgw-text-muted);
+}
+
+.board-card-icon {
+	display: inline-block;
+	width: 1.25rem;
+	height: 1.25rem;
+	background-color: currentColor;
+	-webkit-mask-image: url(/static/icons/cloud.svg);
+	mask-image: url(/static/icons/cloud.svg);
+	-webkit-mask-size: contain;
+	mask-size: contain;
+	-webkit-mask-repeat: no-repeat;
+	mask-repeat: no-repeat;
+	-webkit-mask-position: center;
+	mask-position: center;
+}
+.board-card-icon[data-icon="cloud-up"] {
+	-webkit-mask-image: url(/static/icons/cloud-up.svg);
+	mask-image: url(/static/icons/cloud-up.svg);
+}
+.board-card-icon[data-icon="cloud-filled"] {
+	-webkit-mask-image: url(/static/icons/cloud-filled.svg);
+	mask-image: url(/static/icons/cloud-filled.svg);
+}
+.board-card-icon[data-icon="cloud-exclamation"] {
+	-webkit-mask-image: url(/static/icons/cloud-exclamation.svg);
+	mask-image: url(/static/icons/cloud-exclamation.svg);
+}
+.board-card .board-card-title {
+	padding-right: 1.75rem;
+}
+
+/* Board card popout: overlay and card on top of the board (above navbar and Bootstrap modals) */
+.board-card-popout-overlay {
+	position: fixed;
+	inset: 0;
+	z-index: 1060;
+	background-color: rgba(0, 0, 0, 0.35);
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	padding: 1.5rem;
+	box-sizing: border-box;
+
+	.board-card-popout {
+		position: relative;
+		z-index: 1061;
+		max-width: 40rem;
+		width: 100%;
+		pointer-events: auto;
+		box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2);
+	}
+}
+
+/* Popout card: editable fields (click to edit) */
+.board-card-popout {
+	.board-card-editable,
+	.board-card-title.board-card-editable {
+		cursor: pointer;
+		border-radius: 0.25rem;
+		min-height: 1.5em;
+
+		&:hover {
+			background-color: var(--pgw-bg-muted);
+		}
+	}
+
+	.board-card-title-edit,
+	.board-card-description-edit,
+	.board-card-due-date-edit,
+	.board-card-status-edit,
+	.board-card-priority-edit {
+		min-width: 10rem;
+		width: 100%;
+	}
+
+	.board-card-description-edit {
+		min-height: 4rem;
+		resize: vertical;
+	}
+}
+
+/* Search page: input, status, and result lines */
+.search-status {
+	font-style: italic;
+}
+
+.search-results {
+	display: flex;
+	flex-direction: column;
+	gap: 0.25rem;
+
+	&.is-loading {
+		opacity: 0.6;
+	}
+}
+
+.search-result {
+	background-color: var(--pgw-bg-raised);
+	border: 1px solid var(--pgw-border);
+	border-radius: 0.25rem;
+	padding: 0.25rem 0.5rem;
+
+	&:hover {
+		background-color: var(--pgw-table-hover);
+	}
+}
+
+.search-result-line {
+	margin: 0;
+	font-family: ui-monospace, "Fira Code", "Fira Mono", monospace;
+	font-size: 0.85rem;
+	line-height: 1.4;
+	color: var(--pgw-text);
+	white-space: pre-wrap;
+	word-break: break-word;
+}
+
+.subpage-frame {
+	display: block;
+	width: 100%;
+	height: 82vh;
+	min-height: 42rem;
+	border: 1px solid var(--pgw-border);
+	background-color: var(--pgw-bg-raised);
+}
