vextoly/gitman

Fork of patx/gitman.

Redesign UI with flat black design system and drop CDN assets

Commit ac1dc2e · niklas · 2026-11-03T19:52:16+01:00

Changeset
ac1dc2ec3ebe42a73d82e40753decad61c836726
Parents
5a613ad7390953e0795167e67a60aebbb881313b

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/app.py b/app.py
index 0531e79..2c35a7c 100644
--- a/app.py
+++ b/app.py
@@ -241,8 +241,8 @@ CSP_HEADER = (
     "form-action 'self'; "
     "object-src 'none'; "
     "img-src 'self' data: http: https:; "
-    "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; "
-    "script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com"
+    "style-src 'self' 'unsafe-inline'; "
+    "script-src 'self' 'unsafe-inline'"
 )
 REPO_INDEX_THREADS = set()
 REPO_INDEX_LOCK = threading.Lock()
diff --git a/static/git.svg b/static/git.svg
index 0979cdb..d7bf780 100644
--- a/static/git.svg
+++ b/static/git.svg
@@ -1,3 +1,6 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16">
-  <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
-</svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
+  <circle cx="32" cy="32" r="29" fill="#000000" stroke="#ffffff" stroke-width="6"/>
+  <g transform="translate(14.4 14.4) scale(2.2)" fill="#ffffff">
+    <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
+  </g>
+</svg>
diff --git a/static/styles.css b/static/styles.css
index 7c679c7..72812f8 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -7,213 +7,344 @@ html {
 }
 
 :root {
-  color-scheme: light;
-  --page-bg: #fff;
-  --text: #111;
-  --link: #0645ad;
-  --surface: #fff;
-  --soft-surface: #fafafa;
-  --button-surface: #eee;
-  --border: #ddd;
-  --soft-border: #eee;
-  --strong-border: #999;
-  --control-border: #aaa;
-  --menu-border: #bbb;
-  --muted: #666;
-  --detail: #333;
-  --danger: #900;
-  --notice: #060;
-  --ref-hover: #eef4fb;
-  --shadow: rgba(0, 0, 0, .12);
-  --app-font: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
-}
-
-@media (prefers-color-scheme: dark) {
-  :root {
-    color-scheme: dark;
-    --page-bg: #101214;
-    --text: #e8e8e3;
-    --link: #7fb4ff;
-    --surface: #15191d;
-    --soft-surface: #1b2025;
-    --button-surface: #242a30;
-    --border: #343a40;
-    --soft-border: #2a3036;
-    --strong-border: #6b727a;
-    --control-border: #6b727a;
-    --menu-border: #464d55;
-    --muted: #a3a9b0;
-    --detail: #d6d8d5;
-    --danger: #ff8b8b;
-    --notice: #7fd18b;
-    --ref-hover: #233246;
-    --shadow: rgba(0, 0, 0, .45);
-  }
+  color-scheme: dark;
+  --page-bg: #000000;
+  --ink: #ffffff;
+  --text: #b3b3b3;
+  --muted: #999999;
+  --chip: #333333;
+  --press: #e2e2e2;
+  --surface: #161616;
+  --surface-border: #262626;
+  --code-bg: #1f1f1f;
+  --app-font: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
+  --code-font: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
 }
 
 body {
-  max-width: 960px;
-  margin: 2rem auto;
-  padding: 0 1rem 3rem;
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  min-height: 100svh;
+  margin: 0;
   color: var(--text);
   background: var(--page-bg);
-  font: 16px/1.6 var(--app-font);
+  font: 1rem/1.5 var(--app-font);
+}
+
+main.page {
+  flex: 1 1 auto;
+  width: 100%;
+  max-width: 60rem;
+  margin-inline: auto;
+  padding: clamp(3rem, 9vh, 5.5rem) 24px clamp(3rem, 8vh, 5rem);
+}
+
+h1, h2, h3, h4 {
+  margin: 0 0 .75rem;
+  color: var(--ink);
+  font-weight: 700;
+}
+h1 { font-size: clamp(2rem, 6vw, 2.5rem); line-height: 1.2; }
+h2 { font-size: 1.25rem; line-height: 1.4; }
+h3 { font-size: 1.05rem; line-height: 1.4; }
+
+p, ul, ol, dl { margin: 0 0 .75rem; }
+small { font-size: .875rem; }
+
+a { color: inherit; text-decoration: none; }
+a:hover { color: var(--ink); text-decoration: underline; }
+
+:is(a, button, input, select, textarea):focus-visible {
+  outline: 2px solid var(--ink);
+  outline-offset: 2px;
 }
 
-a { color: var(--link); text-decoration: underline; }
-h1, h2, p, pre, ul, table, form { margin-top: 0; }
-pre, code, input, textarea, select, button { font: inherit; }
-pre, .clone-box code { overflow-x: auto; padding: .6rem; border: 1px solid var(--border); background: var(--soft-surface); font: 14px/1.55 var(--app-font); }
-pre code.hljs { padding: 0; background: transparent; }
+form { display: grid; gap: .9rem; }
+label { display: grid; gap: .35rem; align-content: start; }
+
+input, textarea, select {
+  width: 100%;
+  padding: 12px 14px;
+  color: var(--ink);
+  background: var(--surface);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
+  font: inherit;
+}
+input::placeholder { color: var(--muted); }
+textarea { resize: vertical; }
+
+button, .button {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+  min-height: 48px;
+  padding: 12px 18px;
+  border: 0;
+  border-radius: 999px;
+  background: var(--chip);
+  color: var(--ink);
+  font: inherit;
+  font-weight: 500;
+  text-decoration: none;
+  cursor: pointer;
+}
+button:hover, .button:hover {
+  background: var(--ink);
+  color: var(--page-bg);
+  text-decoration: none;
+}
+button:active, .button:active {
+  background: var(--press);
+  color: var(--page-bg);
+}
+.button.small { min-height: 44px; padding: 8px 14px; }
+.button.danger { background: var(--ink); color: var(--page-bg); }
+.button.danger:hover { background: var(--press); }
+.button.danger:active { background: var(--chip); color: var(--ink); }
+
+form button[type="submit"] { justify-self: start; }
+
+@media (prefers-reduced-motion: no-preference) {
+  button, .button, .nav-icons, .repo-search-link, .ref-picker-option, .ref-picker-toggle, .mention-option {
+    transition: background-color 150ms ease, color 150ms ease;
+  }
+}
+
+code { font-family: var(--code-font); font-size: .9em; background: var(--code-bg); border-radius: 3px; padding: .12em .35em; }
+pre {
+  margin: 0 0 .75rem;
+  overflow-x: auto;
+  padding: .75rem 1rem;
+  color: var(--text);
+  background: var(--surface);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
+  font: .875rem/1.55 var(--code-font);
+}
+pre code { padding: 0; background: transparent; border-radius: 0; font-size: inherit; }
+.clone-box code {
+  display: block;
+  overflow-x: auto;
+  padding: .75rem 1rem;
+  background: var(--surface);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
+  font: .875rem/1.55 var(--code-font);
+}
+.diff { max-height: 70vh; overflow: auto; }
+
 .copyable-code {
   position: relative;
   display: flex;
   align-items: stretch;
-  border: 1px solid var(--border);
-  background: var(--soft-surface);
+  background: var(--surface);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
 }
 .copyable-code pre {
   margin: 0;
   border: 0;
   background: transparent;
+  border-radius: 0;
 }
 .copyable-code pre.code {
   flex: 1 1 auto;
   min-width: 0;
-  padding: 2.1rem .6rem .6rem;
+  padding: 2.4rem 1rem .75rem;
 }
 .copyable-code pre.line-numbers {
   flex: 0 0 auto;
   overflow: hidden;
-  padding: 2.1rem .55rem .6rem .6rem;
+  padding: 2.4rem .75rem .75rem 1rem;
   color: var(--muted);
   text-align: right;
   user-select: none;
-  border-right: 1px solid var(--border);
+  border-right: 1px solid var(--surface-border);
 }
 .copy-button {
   position: absolute;
-  top: .45rem;
-  right: .45rem;
+  top: .55rem;
+  right: .55rem;
   display: inline-flex;
   align-items: center;
   justify-content: center;
-  width: 1.9rem;
-  height: 1.9rem;
+  width: 2rem;
+  height: 2rem;
+  min-height: 0;
   padding: 0;
-  border-radius: .35rem;
-  background: var(--surface);
-  box-shadow: 0 1px 2px var(--shadow);
-}
-.copy-button svg {
-  width: 1rem;
-  height: 1rem;
-  fill: currentColor;
-}
-.copy-button.is-copied {
-  color: var(--notice);
-  border-color: var(--notice);
-}
-.copy-button.copy-error {
-  color: var(--danger);
-  border-color: var(--danger);
-}
-input, textarea, select { width: 100%; padding: .4rem; color: var(--text); border: 1px solid var(--control-border); background: var(--surface); }
+  border-radius: 999px;
+  background: var(--chip);
+  color: var(--ink);
+}
+.copy-button:hover { background: var(--ink); color: var(--page-bg); }
+.copy-button svg { width: 1rem; height: 1rem; fill: currentColor; }
+.copy-button.is-copied { background: var(--ink); color: var(--page-bg); }
+.copy-button.copy-error { outline: 2px solid var(--ink); outline-offset: 1px; }
+
 table { width: 100%; border-collapse: collapse; }
-th, td { padding: .35rem .5rem; border: 1px solid var(--border); text-align: left; }
+th, td { padding: .4rem .6rem; border: 1px solid var(--surface-border); text-align: left; }
+th { color: var(--ink); font-weight: 700; }
 
-button, .button {
-  display: inline-block;
-  padding: .3rem .55rem;
-  color: var(--text);
-  background: var(--button-surface);
-  border: 1px solid var(--strong-border);
-  text-decoration: none;
-  cursor: pointer;
-}
-.pagination {
+.site-header {
   display: flex;
-  flex-wrap: wrap;
-  gap: .6rem;
+  justify-content: space-between;
   align-items: center;
-  margin-top: 1rem;
-}
-.pagination-current, .pagination-disabled { color: var(--muted); }
-.pagination-disabled { padding: .3rem 0; }
-
-.sr-only {
-  position: absolute;
-  width: 1px;
-  height: 1px;
-  padding: 0;
-  margin: -1px;
-  overflow: hidden;
-  clip: rect(0, 0, 0, 0);
-  white-space: nowrap;
-  border: 0;
-}
-
-.site-header, .repo-header, .profile-header, .panel, .auth-card {
-  margin-bottom: 1.5rem;
-  padding-bottom: 1rem;
-  border-bottom: 1px solid var(--border);
-}
-
-.site-header, .nav, .repo-tabs, .filters, .tabs, .panel-heading, .hero-actions, .breadcrumb, .repo-actions, .ref-actions {
-  display: flex;
-  gap: .75rem;
-  align-items: baseline;
+  gap: 1rem;
   flex-wrap: wrap;
+  width: 100%;
+  max-width: 60rem;
+  margin-inline: auto;
+  padding: 24px;
 }
-
-.site-header, .panel-heading, .repo-header { justify-content: space-between; }
-.repo-title-row { display: flex; gap: .75rem; align-items: baseline; flex-wrap: wrap; }
-.repo-title-row h1 { margin-bottom: 0; }
 .brand {
   display: inline-flex;
   align-items: center;
   gap: 1rem;
-  color: var(--text);
-  font-weight: 700;
+  color: var(--ink);
   line-height: 1;
   text-decoration: none;
 }
+.brand:hover { color: var(--ink); text-decoration: none; }
 .brand svg {
   flex: 0 0 auto;
   transform: scale(2);
   transform-origin: center;
 }
-.site-header, .nav { align-items: center; }
+.nav, .repo-tabs, .filters, .tabs, .panel-heading, .hero-actions, .breadcrumb {
+  display: flex;
+  gap: .75rem;
+  align-items: baseline;
+  flex-wrap: wrap;
+}
+.nav { align-items: center; }
+.panel-heading, .repo-header { justify-content: space-between; }
+.panel-heading { margin-bottom: 1.25rem; }
+.panel-heading h2 { margin: 0; }
 .nav-icons {
   display: inline-flex;
   align-items: center;
   justify-content: center;
-  width: 24px;
-  height: 24px;
+  width: 40px;
+  height: 40px;
+  color: var(--text);
+}
+.nav-icons:hover { color: var(--ink); text-decoration: none; }
+.nav-icons svg { display: block; width: 20px; height: 20px; flex-shrink: 0; }
+.nav form, .repo-tabs form, .inline-form { display: inline; }
+
+.colophon {
+  display: flex;
+  justify-content: space-between;
+  gap: 1rem;
+  flex-wrap: wrap;
+  width: 100%;
+  max-width: 60rem;
+  margin-inline: auto;
+  padding: 1.5rem 24px 2.5rem;
+  color: var(--muted);
+  font-size: .875rem;
+}
+.colophon p { margin: 0; }
+.colophon a { color: var(--muted); }
+.colophon a:hover { color: var(--ink); text-decoration: underline; }
+
+.repo-header, .profile-header { margin-bottom: 2.5rem; }
+.repo-header { display: flex; align-items: flex-end; gap: 2rem; flex-wrap: wrap; }
+.panel { margin-bottom: 2.5rem; }
+.panel > :last-child, .repo-header > :last-child, .auth-card > :last-child { margin-bottom: 0; }
+.auth-card { max-width: 32rem; margin-bottom: 2.5rem; }
+.auth-card.wide { max-width: 36rem; }
+.repo-title-row { display: flex; gap: 1rem; align-items: baseline; flex-wrap: wrap; }
+.repo-title-row h1 { margin-bottom: 0; }
+
+.repo-tabs .repo-tab { padding: .25rem 0; }
+.repo-tab.active, .filters .active, .tabs .active {
+  color: var(--ink);
   font-weight: 700;
-  line-height: 1;
   text-decoration: none;
-  color: var(--muted);
 }
-.nav-icons svg { display: block; width: 24px; height: 24px; flex-shrink: 0; }
-.nav form, .repo-tabs form, .inline-form { display: inline; }
-.link-button { padding: 0; color: var(--link); background: none; border: 0; text-decoration: underline; }
-.repo-tabs .repo-tab { padding-bottom: .2rem; border-bottom: 2px solid transparent; }
-.repo-tabs .repo-tab.active { color: var(--text); font-weight: 700; text-decoration: none; }
-.repo-search {
-  position: relative;
-  width: 18rem;
-  max-width: 100%;
-  align-self: flex-start;
+
+.link-button, .button-link {
+  display: inline;
+  min-height: 0;
+  padding: 0;
+  border: 0;
+  border-radius: 0;
+  background: none;
+  color: var(--text);
+  font: inherit;
+  text-decoration: none;
+  cursor: pointer;
 }
-.repo-search-input {
-  width: 350px;
-  margin-bottom: 10px;
+.link-button:hover, .button-link:hover {
+  background: none;
+  color: var(--ink);
+  text-decoration: underline;
 }
-.repo-search-form {
-  display: block;
+
+.eyebrow, .muted, .empty, .issue-list span { color: var(--muted); }
+.eyebrow { font-size: .875rem; }
+
+.repo-list, .clean-list, .commit-list, .file-list, .issue-list, .comment-list, .activity-feed {
+  display: grid;
+  gap: .75rem;
+  padding-left: 0;
   margin: 0;
+  list-style: none;
+}
+.commit-list li, .file-list li { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; }
+.commit-list li > code, .file-list li > code { justify-self: start; }
+
+.meta-list { display: grid; grid-template-columns: 8rem 1fr; gap: .35rem 1rem; }
+.meta-list dt { color: var(--muted); }
+.meta-list dd { margin: 0; overflow-x: auto; }
+
+.activity-feed { gap: 1rem; }
+.activity-feed li { padding-bottom: 1rem; border-bottom: 1px solid var(--surface-border); }
+.activity-feed li:last-child { border-bottom: 0; }
+.activity-title, .activity-detail { margin-bottom: .25rem; }
+.activity-detail { color: var(--text); }
+
+.comment { padding-bottom: 1rem; border-bottom: 1px solid var(--surface-border); min-width: 0; }
+.comment:last-child { border-bottom: 0; padding-bottom: 0; }
+.comment-meta {
+  display: flex;
+  gap: .5rem;
+  align-items: baseline;
+  margin-bottom: .35rem;
+}
+.comment-body {
+  line-height: 1.6;
+  overflow-wrap: anywhere;
+}
+.comment-body > :last-child { margin-bottom: 0; }
+.comment p, .activity-title, .activity-detail {
+  font-size: 1rem;
+  line-height: 1.6;
+  overflow-wrap: anywhere;
+}
+.comment small, .activity-feed .muted { font-size: .875rem; }
+
+.markdown-body { overflow-x: auto; }
+.markdown-body img { max-width: 100%; }
+.markdown-body h1 { font-size: 1.5rem; margin: 1.5rem 0 .75rem; }
+.markdown-body h2 { font-size: 1.25rem; margin: 1.5rem 0 .75rem; }
+.markdown-body h3 { font-size: 1.05rem; margin: 1.25rem 0 .6rem; }
+.markdown-body h1:first-child, .markdown-body h2:first-child, .markdown-body h3:first-child { margin-top: 0; }
+.markdown-body blockquote {
+  margin: 0 0 .75rem;
+  padding: .25rem 0 .25rem 1rem;
+  border-left: 2px solid var(--chip);
+  color: var(--muted);
 }
+.markdown-body a { text-decoration: underline; }
+
+.repo-search { position: relative; width: 18rem; max-width: 100%; align-self: flex-start; }
+.repo-search-input { width: 350px; margin-bottom: 10px; }
+.repo-search-form { display: block; margin: 0; }
 .panel-heading > .repo-search { margin-left: auto; }
 .hero-actions .repo-search-menu { left: 0; right: auto; }
 .repo-search-menu {
@@ -229,25 +360,25 @@ button, .button {
   max-height: 20rem;
   overflow-y: auto;
   padding: .45rem;
-  border: 1px solid var(--menu-border);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
   background: var(--surface);
-  box-shadow: 0 .4rem 1.2rem var(--shadow);
-}
-.repo-search-result {
-  display: block;
 }
+.repo-search-result { display: block; }
 .repo-search-link {
   display: flex;
   justify-content: space-between;
   gap: .75rem;
   align-items: baseline;
   width: 100%;
-  padding: .4rem .5rem;
-  color: var(--text);
+  padding: .5rem .6rem;
+  border-radius: 6px;
+  color: var(--ink);
   text-decoration: none;
 }
 .repo-search-link:hover, .repo-search-link:focus {
-  background: var(--ref-hover);
+  background: var(--ink);
+  color: var(--page-bg);
   text-decoration: none;
 }
 .repo-search-link strong {
@@ -256,13 +387,27 @@ button, .button {
   text-overflow: ellipsis;
   white-space: nowrap;
 }
-.repo-search-link small {
-  flex: 0 0 auto;
-  white-space: nowrap;
-}
+.repo-search-link small { flex: 0 0 auto; white-space: nowrap; }
 .repo-search-link small, .repo-search-empty { color: var(--muted); }
+.repo-search-link:hover small, .repo-search-link:focus small { color: inherit; }
 .repo-search-empty { margin: 0; padding: .3rem .4rem; }
+
 .ref-picker { position: relative; display: inline-block; }
+.ref-picker-toggle {
+  display: inline-flex;
+  gap: .25rem;
+  align-items: center;
+  min-height: 0;
+  padding: .3rem .9rem;
+  border: 0;
+  border-radius: 999px;
+  background: var(--chip);
+  color: var(--ink);
+  font: inherit;
+  font-size: .9375rem;
+  cursor: pointer;
+}
+.ref-picker-toggle:hover { background: var(--ink); color: var(--page-bg); }
 .ref-picker-menu {
   position: absolute;
   left: 0;
@@ -271,26 +416,29 @@ button, .button {
   width: 20rem;
   max-width: calc(100vw - 2rem);
   padding: .5rem;
-  border: 1px solid var(--menu-border);
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
   background: var(--surface);
-  box-shadow: 0 .4rem 1.2rem var(--shadow);
 }
 .ref-picker-search { margin-bottom: .4rem; }
-.ref-picker-options { display: grid; gap: .1rem; max-height: 18rem; overflow-y: auto; }
+.ref-picker-options { display: grid; gap: .15rem; max-height: 18rem; overflow-y: auto; }
 .ref-picker-option {
   display: flex;
   gap: 1rem;
   justify-content: space-between;
-  padding: .35rem .45rem;
-  color: var(--text);
+  padding: .45rem .6rem;
+  border-radius: 6px;
+  color: var(--ink);
   text-decoration: none;
 }
-.ref-picker-option:hover, .ref-picker-option:focus, .ref-picker-option.active {
-  background: var(--ref-hover);
+.ref-picker-option:hover, .ref-picker-option:focus {
+  background: var(--ink);
+  color: var(--page-bg);
   text-decoration: none;
 }
 .ref-picker-option.active { font-weight: 700; }
 .ref-picker-current { color: var(--muted); font-weight: 400; }
+.ref-picker-option:hover .ref-picker-current, .ref-picker-option:focus .ref-picker-current { color: inherit; }
 .ref-picker-empty { padding: .35rem .45rem; color: var(--muted); }
 .ref-picker-footer {
   display: flex;
@@ -298,107 +446,47 @@ button, .button {
   flex-wrap: wrap;
   margin-top: .5rem;
   padding-top: .45rem;
-  border-top: 1px solid var(--border);
+  border-top: 1px solid var(--surface-border);
 }
-.ref-picker-link.active { color: var(--text); font-weight: 700; text-decoration: none; }
-.filters .active, .tabs .active { color: var(--text); font-weight: 700; text-decoration: none; }
+.ref-picker-link.active { color: var(--ink); font-weight: 700; text-decoration: none; }
+
 .list-search {
   display: inline-flex;
   gap: .4rem;
   align-items: center;
   flex-wrap: wrap;
 }
-.list-search input[type="search"] {
-  width: 18rem;
-  max-width: 100%;
-}
-.hero, .repo-tabs { margin-bottom: 1.5rem; }
-.eyebrow, .muted, .empty, .nav-user, .repo-card small, .issue-list span, .commit-list span, .file-list span, .clean-list span, .file-kind { color: var(--muted); }
-.repo-list, .stack, form, .clean-list, .commit-list, .file-list, .issue-list { display: grid; gap: .75rem; }
-.repo-card { display: block; padding: .75rem 0; border-bottom: 1px solid var(--soft-border); color: var(--text); }
-.repo-card strong, .repo-card span, .repo-card small, .clone-box code { display: block; }
-.markdown-body { overflow-x: auto; }
-.markdown-body img { max-width: 100%; }
-.markdown-body code {
-  padding: .12rem .28rem;
-  border-radius: .25rem;
-  background: var(--soft-surface);
-  font: .9em/1.45 var(--app-font);
-}
-.markdown-body pre code {
-  display: block;
-  padding: 0;
-  background: transparent;
-  font: inherit;
-}
-.grid.two { display: grid; grid-template-columns: 1fr 18rem; gap: 2rem; }
-.meta-list { display: grid; grid-template-columns: 8rem 1fr; gap: .35rem 1rem; }
-.meta-list dt { color: var(--muted); }
-.meta-list dd { margin: 0; overflow-x: auto; }
-.activity-feed { display: grid; gap: 1rem; padding-left: 0; list-style: none; }
-.activity-feed li { padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.activity-title, .activity-detail { margin-bottom: .25rem; }
-.activity-detail { color: var(--detail); }
-.comment-list { display: grid; gap: 1rem; }
-.comment { padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.comment, .activity-feed li { min-width: 0; }
-.comment-meta {
+.list-search input[type="search"] { width: 18rem; max-width: 100%; }
+
+.hero-actions { margin-top: 1.25rem; }
+
+.pagination {
   display: flex;
-  gap: .5rem;
-  align-items: baseline;
-  margin-bottom: .35rem;
-}
-.comment-body {
-  line-height: 1.6;
-  overflow-wrap: anywhere;
-}
-.comment-body > :last-child { margin-bottom: 0; }
-.comment p, .activity-title, .activity-detail {
-  font-size: 1rem;
-  line-height: 1.6;
-  overflow-wrap: anywhere;
-}
-.comment small, .activity-feed .muted {
-  font-size: .875rem;
+  flex-wrap: wrap;
+  gap: .6rem;
+  align-items: center;
+  margin-top: 1rem;
 }
-.diff { max-height: 70vh; }
-.file-table { width: 100%; border-collapse: collapse; }
-.file-table td { padding: .4rem 0; border-bottom: 1px solid var(--soft-border); }
-.file-kind { width: 4rem; }
-.commit-list, .file-list, .issue-list, .clean-list { padding-left: 0; list-style: none; }
-.commit-list li, .file-list li { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; }
-.tag-list li { display: grid; grid-template-columns: 1fr 18rem; gap: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--soft-border); }
-.tag-actions { display: grid; gap: .75rem; align-content: start; }
-.alert { margin-bottom: 1rem; color: var(--danger); }
-.notice { margin-bottom: 1rem; color: var(--notice); }
-.danger-zone { color: var(--danger); }
-.button.danger { color: var(--danger); border-color: var(--danger); }
-.button-link { background: none; border: none; padding: 0; color: var(--link); text-decoration: underline; cursor: pointer; font: inherit; }
-.ref-picker-toggle { display: inline-flex; gap: .25rem; align-items: center; background: none; border: none; padding: 0; color: var(--muted); cursor: pointer; font: inherit; font-size: smaller; }
+.pagination-current, .pagination-disabled { color: var(--muted); }
+.pagination-disabled { padding: .3rem 0; }
 
-@media (max-width: 760px) {
-  body { margin-top: 1rem; }
-  .repo-search { width: 100%; }
-  .repo-search-input { width: 100%; }
-  .repo-search-menu { left: 0; right: auto; width: 100%; }
-  .grid.two, .commit-list li, .tag-list li { grid-template-columns: 1fr; }
-}
+.alert { margin-bottom: 1rem; color: var(--ink); font-weight: 700; }
+.notice { margin-bottom: 1rem; color: var(--text); }
+.danger-zone { margin-top: 1rem; padding-top: 2rem; border-top: 1px solid var(--surface-border); }
+.danger-zone h2 { margin-top: 0; }
 
 .password-dialog {
   width: min(28rem, calc(100% - 2rem));
   max-height: calc(100dvh - 2rem);
   overflow: auto;
   padding: 1.5rem;
-  border: 1px solid var(--strong-border);
-  border-radius: .5rem;
+  border: 1px solid var(--surface-border);
+  border-radius: 8px;
   color: var(--text);
   background: var(--surface);
-  box-shadow: 0 12px 48px var(--shadow);
 }
-.password-dialog::backdrop { background: rgb(0 0 0 / .45); }
+.password-dialog::backdrop { background: rgb(0 0 0 / .6); }
 .password-dialog h2 { font-size: 1.25rem; margin-bottom: 1rem; }
-.password-dialog label { display: block; margin-bottom: 1rem; }
-.password-dialog input { margin-top: .25rem; }
 .password-dialog .muted { font-size: .875rem; }
 .password-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .75rem; }
 .password-dialog form { margin-bottom: 0; }
@@ -409,16 +497,48 @@ button, .button {
   margin: 0;
 }
 .mention-option {
-  flex-shrink: 0;
+  display: flex;
+  justify-content: space-between;
+  gap: .75rem;
+  align-items: baseline;
+  width: 100%;
+  min-height: 0;
+  padding: .5rem .6rem;
   border: 0;
+  border-radius: 6px;
   background: transparent;
+  color: var(--ink);
+  font-weight: 400;
   text-align: left;
   cursor: pointer;
 }
-.mention-option.active { background: var(--ref-hover); }
+.mention-option:hover, .mention-option.active {
+  background: var(--ink);
+  color: var(--page-bg);
+}
 .mention-option small {
   color: var(--muted);
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
+.mention-option:hover small, .mention-option.active small { color: inherit; }
+
+@media (max-width: 760px) {
+  .repo-search, .repo-search-input { width: 100%; }
+  .repo-search-menu { left: 0; right: auto; width: 100%; }
+  .commit-list li, .file-list li { grid-template-columns: 1fr; }
+  .repo-header { align-items: flex-start; }
+}
+
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border: 0;
+}
diff --git a/templates/base.tpl b/templates/base.tpl
index fe5774f..7a7f619 100644
--- a/templates/base.tpl
+++ b/templates/base.tpl
@@ -3,21 +3,20 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  <title>{{title or "GitMan"}}</title>
-  <link rel="icon" type="image/svg+xml" sizes="32x32" href="/static/git.svg">
-<link rel="icon" type="image/svg+xml" sizes="16x16" href="/static/git.svg">
-<link rel="apple-touch-icon" sizes="180x180" href="/static/git.svg">
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
-  <link rel="stylesheet" href="/static/styles.css?v=8">
+  <meta name="color-scheme" content="dark">
+  <meta name="theme-color" content="#000000">
+  <meta name="description" content="GitMan: free Git repository hosting for open source software.">
+  <title>{{(title + " · GitMan") if title else "GitMan"}}</title>
+  <link rel="icon" type="image/svg+xml" href="/static/git.svg">
+  <link rel="stylesheet" href="/static/styles.css?v=9">
 </head>
 <body>
   <header class="site-header">
-    <a class="brand" href="/">
-        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16">
+    <a class="brand" href="/" aria-label="GitMan home">
+        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-git" viewBox="0 0 16 16" aria-hidden="true">
           <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
         </svg>
-        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-arms-up" viewBox="0 0 16 16">
+        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-arms-up" viewBox="0 0 16 16" aria-hidden="true">
            <path d="M8 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>
            <path d="m5.93 6.704-.846 8.451a.768.768 0 0 0 1.523.203l.81-4.865a.59.59 0 0 1 1.165 0l.81 4.865a.768.768 0 0 0 1.523-.203l-.845-8.451A1.5 1.5 0 0 1 10.5 5.5L13 2.284a.796.796 0 0 0-1.239-.998L9.634 3.84a.7.7 0 0 1-.33.235c-.23.074-.665.176-1.304.176-.64 0-1.074-.102-1.305-.176a.7.7 0 0 1-.329-.235L4.239 1.286a.796.796 0 0 0-1.24.998l2.5 3.216c.317.316.475.758.43 1.204Z"/>
         </svg>
@@ -42,7 +41,7 @@
         </a>
       % else:
         <a href="/login">Log in</a>
-        <a class="button small" href="/signup">Sign up</a>
+        <a class="button" href="/signup">Sign up</a>
       % end
     </nav>
   </header>
@@ -56,8 +55,11 @@
     % end
     {{!base}}
   </main>
+  % import time
+  <footer class="colophon">
+    <p>© {{time.strftime("%Y")}} GitMan</p>
+  </footer>
   <script src="/static/mentions.js?v=1" defer></script>
-  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
   <script>
     (() => {
       const addLineNumbers = (code, rawText) => {
diff --git a/templates/index.tpl b/templates/index.tpl
index c85e177..510c12c 100644
--- a/templates/index.tpl
+++ b/templates/index.tpl
@@ -1,16 +1,16 @@
-% rebase("base.tpl", title="GitMan activity", user=user, error=error, notice=notice)
+% rebase("base.tpl", title="Recent activity", user=user, error=error, notice=notice)
 
-<section class="panel" style="border-bottom: none;">
+<section class="panel">
   <div class="panel-heading">
     <div>
             <p class ="eyebrow">GitMan.io: Free Git repository hosting for open source software</p>
             % if not user:
-              <div class="hero-actions" style="margin-bottom:50px;">
+              <div class="hero-actions">
                 <a class="button" href="/signup">Create an account</a>
-                <a class="button secondary" href="/login">Log in</a>
+                <a class="button" href="/login">Log in</a>
               </div>
             % else:
-              <div class="hero-actions" style="margin-bottom:50px;">
+              <div class="hero-actions">
               <div class="repo-search" data-repo-search data-repo-search-url="/-/repos/search">
                 <input
                   id="repo-search-input"
diff --git a/templates/notifications.tpl b/templates/notifications.tpl
index f5ccf9f..6f6bbf6 100644
--- a/templates/notifications.tpl
+++ b/templates/notifications.tpl
@@ -1,6 +1,6 @@
-% rebase("base.tpl", title="Notifications · GitMan", user=user, error=error, notice=notice)
+% rebase("base.tpl", title="Notifications", user=user, error=error, notice=notice)
 
-<section class="panel" style="border-bottom: none;">
+<section class="panel">
   <div class="panel-heading">
     <div>
       <h1>Notifications</h1>
diff --git a/tests/test_app.py b/tests/test_app.py
index c4806ef..a9e7191 100644
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -513,16 +513,17 @@ def test_security_headers_and_secure_cookie_flags(isolated_app):
     assert "Secure" in session_cookie
 
 
-def test_styles_support_system_dark_mode(isolated_app):
+def test_styles_apply_permanent_dark_theme(isolated_app):
     css = (REPO_ROOT_FOR_IMPORTS / "static" / "styles.css").read_text(encoding="utf-8")
     client = WsgiClient(isolated_app.app)
     response = client.get("/")
 
-    assert "@media (prefers-color-scheme: dark)" in css
     assert "color-scheme: dark" in css
-    assert "var(--page-bg)" in css
-    assert "github-dark.min.css" in response.text
-    assert 'media="(prefers-color-scheme: dark)"' in response.text
+    assert "--page-bg: #000000" in css
+    assert "prefers-color-scheme" not in css
+    assert "prefers-reduced-motion: no-preference" in css
+    assert "cdnjs.cloudflare.com" not in response.text
+    assert 'name="theme-color" content="#000000"' in response.text
 
 
 def test_styles_keep_comment_and_activity_text_readable_on_mobile(isolated_app):