This is an automated email from the ASF dual-hosted git repository.

sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-atr-experiments.git


The following commit(s) were added to refs/heads/main by this push:
     new f1a1222  Improve style
f1a1222 is described below

commit f1a1222049b7ac0cf5bdb792fb4aec780a7cdc47
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Feb 17 17:18:57 2025 +0200

    Improve style
---
 atr/server.py                               |   3 +
 atr/static/css/atr.css                      | 199 ++++++++++++++
 atr/static/css/normalize.css                | 405 ++++++++++++++++++++++++++++
 atr/static/css/root.css                     |   4 +
 atr/static/webfonts/inter-v.woff2           | Bin 0 -> 352240 bytes
 atr/static/webfonts/jost-v.woff2            | Bin 0 -> 50396 bytes
 atr/templates/add-release-candidate.html    | 209 +++++++-------
 atr/templates/data-browser.html             | 162 +++++------
 atr/templates/includes/footer.html          |   9 +-
 atr/templates/includes/sidebar.html         |  44 +++
 atr/templates/layouts/base.html             |  31 ++-
 atr/templates/release-signature-verify.html | 324 +++++++++++-----------
 atr/templates/user-uploads.html             | 164 +++++------
 13 files changed, 1118 insertions(+), 436 deletions(-)

diff --git a/atr/server.py b/atr/server.py
index c2a563f..e3e80d0 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -48,6 +48,9 @@ def create_app() -> QuartApp:
         raise ValueError("asfquart.construct is not set")
     app = asfquart.construct(__name__)
 
+    # # Configure static folder path before changing working directory
+    # app.static_folder = 
os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
+
     @app.context_processor
     async def app_wide():
         return {"current_user": await asfquart.session.read()}
diff --git a/atr/static/css/atr.css b/atr/static/css/atr.css
new file mode 100644
index 0000000..baa1218
--- /dev/null
+++ b/atr/static/css/atr.css
@@ -0,0 +1,199 @@
+@font-face {
+    font-family: "Jost";
+    src: url("../webfonts/jost-v.woff2") format("woff2");
+    font-weight: 100 900;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: Inter;
+    src: url("../webfonts/inter-v.woff2") format("woff2");
+    font-weight: 100 900;
+    font-style: normal;
+}
+
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe 
UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+    -webkit-font-smoothing: antialiased;
+    font-size: 17px;
+    line-height: 24px;
+    font-variation-settings: "opsz" 22;
+    font-weight: 425;
+}
+
+input, textarea, button {
+    font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe 
UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+    font-size: 17px;
+    line-height: 24px;
+    font-variation-settings: "opsz" 22;
+    font-weight: 425;
+}
+
+a { font-weight: 475; }
+
+h1, h2, h3 {
+    font-weight: 475;
+    font-family: Jost;
+}
+
+aside h1 {
+    font-size: 3rem;
+    line-height: 1.15;
+    text-align: center;
+}
+
+aside h1 span {
+    color: #777;
+}
+
+.site-title {
+    text-decoration: none;
+    color: inherit;
+}
+
+h1 { margin-bottom: 2rem; }
+h2, h3, p, ul { margin-bottom: 1rem; }
+
+footer {
+  padding: 2rem;
+  background: #eee;
+  font-size: 15px;
+  margin: 2rem;
+  color: #333;
+}
+
+.wrapper {
+    min-height: 100vh;
+    display: flex;
+    flex-direction: column;
+}
+
+.ribbon {
+    height: 8px;
+    background: linear-gradient(90deg, #282661 0%, #662f8f 20%, #9e2165 40%, 
#cb2138 60%, #ea7826 80%, #f7ae18 100%);
+}
+
+.content {
+    flex: 1;
+    display: flex;
+}
+
+.main-container {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+}
+
+.main-content {
+    flex: 1;
+    padding: 2rem;
+}
+
+.sidebar {
+    width: 250px;
+    background-color: #f6f7f8;
+    border-right: 1px solid #d1d2d3;
+    padding: 1rem;
+}
+
+.sidebar .user-section {
+    margin-bottom: 1.5rem;
+    text-align: center;
+    border-top: 1px solid #d1d2d3;
+    border-bottom: 1px solid #d1d2d3;
+    padding: 1.5rem 0;
+}
+
+.sidebar hr {
+    border: none;
+    border-top: 1px solid #999;
+    margin: 1.5rem auto;
+    width: 62%;
+    height: 0;
+}
+
+.sidebar nav {
+    margin-top: 1.5rem;
+}
+
+.sidebar nav ul {
+    list-style: none;
+}
+
+.sidebar nav li {
+    margin-bottom: 1rem;
+}
+
+.hamburger {
+    display: none;
+    background: none;
+    border: none;
+    cursor: pointer;
+    padding: 0;
+    z-index: 100;
+}
+
+.hamburger span {
+    display: block;
+    width: 25px;
+    height: 3px;
+    background-color: #333;
+    margin: 5px 0;
+    transition: 0.3s;
+}
+
+.nav-toggle {
+    display: none;
+}
+
+@media (max-width: 768px) {
+    .hamburger {
+        display: block;
+        position: fixed;
+        top: 20px;
+        padding-left: 2rem;
+        transition: 0.3s;
+    }
+
+    .sidebar {
+        position: fixed;
+        left: -250px;
+        top: 20px;
+        bottom: 0;
+        transition: 0.3s;
+        z-index: 99;
+    }
+
+    /* Show sidebar when checkbox is checked */
+    .nav-toggle:checked ~ .sidebar {
+        left: 0;
+    }
+
+    /* Move hamburger with sidebar */
+    .nav-toggle:checked ~ .hamburger {
+        padding-left: calc(250px + 2rem); /* sidebar width + padding */
+    }
+
+    .nav-toggle:checked ~ .hamburger span:nth-child(1) {
+        transform: rotate(45deg) translate(5px, 5px);
+    }
+
+    .nav-toggle:checked ~ .hamburger span:nth-child(2) {
+        opacity: 0;
+    }
+
+    .nav-toggle:checked ~ .hamburger span:nth-child(3) {
+        transform: rotate(-45deg) translate(7px, -7px);
+    }
+
+    .main-content {
+        margin-left: 0;
+        padding-top: 4rem;
+    }
+}
diff --git a/atr/static/css/normalize.css b/atr/static/css/normalize.css
new file mode 100644
index 0000000..46ab2ba
--- /dev/null
+++ b/atr/static/css/normalize.css
@@ -0,0 +1,405 @@
+
+
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1, 
viewport-fit=cover">
+
+    <meta property="og:url" content="https://classic.yarnpkg.com/en/package/"; 
/>
+    <meta property="og:site_name" content="Yarn"/>
+    <meta property="og:title" content="Yarn" />
+    <meta property="og:image" 
content="https://classic.yarnpkg.com/assets/og_image.png"; />
+    <meta property="og:description" content="Fast, reliable, and secure 
dependency management." />
+
+    <title>Yarn</title>
+    <meta name="description" content="Fast, reliable, and secure dependency 
management.">
+
+    <link rel="canonical" href="https://classic.yarnpkg.com/lang/en/package/";>
+    <link rel="icon" href="/favicon.ico" type="image/x-icon">
+
+    <link rel="search" href="/opensearch.xml" 
type="application/opensearchdescription+xml" title="Yarn">
+
+
+    <link rel="stylesheet" href="/css/main.css?t=2024-11-25T15:06:54+00:00">
+    <meta name="google-site-verification" 
content="DIcCyEkVaGHm864NWzItnt2n6Gg7hz3l47RBIRyxvcQ" />
+  </head>
+  <body>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<div class="news-container">
+  <a class="news-overlay" 
href="https://yarnpkg.com/getting-started/migration";></a>
+
+  <div class="news-inner">
+    <div class="news-line">
+      <span class="news-highlight">Important:</span> This documentation covers 
Yarn 1 (Classic).
+    </div>
+    <div class="news-line">
+      For Yarn 2+ docs and migration guide, see yarnpkg.com.
+    </div>
+  </div>
+</div>
+
+
+
+
+<nav class="navbar navbar-static-top navbar-light">
+  <div class="container">
+    <a href="/en/">
+      <svg class="logo navbar-logo" xmlns="http://www.w3.org/2000/svg"; 
viewBox="0 0 1154.8 518">
+  <defs>
+    <path id="main" d="
+      M718.6 257.8c-8 27.6-20.8 47.6-35.2 
63.6V181c0-9.6-8.4-17.6-21.6-17.6-5.6 0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 
12.8v64.4c-4.8 28-16.8 54-32.8 54-11.6 0-18.4-11.6-18.4-33.2 0-33.6 4.4-51.2 
11.6-80.8 1.6-6 13.2-22-6.4-22-21.2 0-18.4 8-21.2 14.8 0 0-13.4 47.6-13.4 90 0 
34.8 14.6 57.6 41.4 57.6 17.2 0 29.6-11.6 39.2-27.6V351c-26.4 23.2-49.6 
43.6-49.6 84 0 25.6 16 46 38.4 46 20.4 0 41.6-14.8 41.6-56.8V355c21.6-18.8 
44.8-42.4 58.4-88.8.4-1.6.4-3.6.4-4 0-7.6-7.6-16.4-14-16.4-4 0-7.2 3.6- [...]
+      M833.4 301c-9.6 0-13.6-9.6-13.6-18.4v-66c0-9.6-8.4-17.6-21.6-17.6-5.6 
0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 12.8v61.6C785 291.4 777.8 301 767 301c-14 
0-22.8-12-22.8-32.8 0-57.6 35.6-83.6 66-83.6 4 0 8 .8 11.6.8 4 0 5.2-2.4 
5.2-9.2 0-10.4-7.6-16.8-18.4-16.8-48.8 0-95.2 40.8-95.2 107.6 0 34 16.4 60.4 
47.6 60.4 15.2 0 26.4-7.2 34.4-16.4 6 9.6 16.8 16.4 30.8 16.4 34.4 0 50.4-36 
57.2-62.4.4-1.6.4-2.4.4-2.8 0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 
17.6-10.8 43.2-26.8 43.2z
+      M949 327.4c34.4 0 50-36 57.2-62.4 0-.8.4-1.6.4-2.8 
0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 17.6-10.4 43.2-28.8 43.2-10.8 
0-16-10.4-16-21.6 0-40 18-87.2 18-92 1.6-9.2-14.4-22.4-19.2-22.4h-20.8c-4 0-8 
0-21.2-1.6-4.4-16.4-15.6-21.2-25.2-21.2-10.4 0-20 7.2-20 18.4 0 11.6 7.2 20 
17.2 25.6-.4 20.4-2 53.6-6.4 69.6-3.6 13.6 17.2 28 22.4 11.2 7.2-23.2 9.6-58 
10-73.6h34.8c-12.8 34.4-20 62.8-20 88.4 0 35.2 22.4 45.6 41.2 45.6z
+      M984.6 309.8c0 14.8 11.2 17.6 19.2 17.6 11.6 0 11.2-9.6 
11.2-17.2v-58.4c2.8-31.6 27.6-66 39.2-66 7.6 0 8.4 10.4 8.4 22.8v81.2c0 20.4 
12.4 37.6 33.6 37.6 34.4 0 51.4-36 58.2-62.4.4-1.6.4-2.4.4-2.8 
0-7.6-7.6-16.4-14-16.4-4 0-8 3.6-9.6 12-3.6 17.6-11.8 43.2-27.8 43.2-10.4 
0-10.4-14.8-10.4-18.4v-82.8c0-18.4-6.4-40.4-33.2-40.4-19.6 0-34 17.2-44.8 
39.6v-18c0-9.6-8.4-17.6-21.6-17.6-5.6 0-10.4 2.8-10.4 6.8 0 2.8 1.6 5.2 1.6 
12.8v126.8z
+      M259 0c143 0 259 116 259 259S402 518 259 518 0 402 0 259 116 0 259 0z"/>
+  </defs>
+
+  <use class="logo-primary" xlink:href="#main" x="0" y="0"/>
+
+  <path class="logo-secondary" d="M435.2 337.5c-1.8-14.2-13.8-24-29.2-23.8-23 
.3-42.3 12.2-55.1 20.1-5 3.1-9.3 5.4-13 
7.1.8-11.6.1-26.8-5.9-43.5-7.3-20-17.1-32.3-24.1-39.4 8.1-11.8 19.2-29 
24.4-55.6 4.5-22.7 
3.1-58-7.2-77.8-2.1-4-5.6-6.9-10-8.1-1.8-.5-5.2-1.5-11.9.4C293.1 96 289.6 93.8 
286.9 92c-5.6-3.6-12.2-4.4-18.4-2.1-8.3 3-15.4 11-22.1 25.2-1 2.1-1.9 4.1-2.7 
6.1-12.7.9-32.7 5.5-49.6 23.8-2.1 2.3-6.2 4-10.5 5.6h.1c-8.8 3.1-12.8 10.3-17.7 
23.3-6.8 18.2.2 36.1 7.1 47.7-9.4 8.4-21.9 21.8 [...]
+</svg>
+
+      <span class="sr-only">Yarn</span>
+    </a>
+
+    <div class="clearfix hidden-lg-up">
+      <button class="navbar-toggler hidden-lg-up float-right" type="button" 
data-toggle="collapse" data-target="#navbar" aria-controls="exCollapsingNavbar2"
+        aria-expanded="false" aria-label="Toggle navigation"></button>
+    </div>
+
+    <div class="collapse navbar-toggleable-md" id="navbar">
+      <ul class="nav navbar-nav">
+        <li class="nav-item">
+          <a class="nav-link" href="/en/docs/getting-started">Getting 
Started</a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="/en/docs">Docs</a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="/en/packages">Packages</a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="/blog">Blog</a>
+        </li>
+      </ul>
+
+      <ul class="nav navbar-nav navbar-nav-right float-lg-right">
+
+        <li class="nav-item dropdown">
+          <a id="dropdownNavLanguage" class="nav-link dropdown-toggle" 
role="button" href="#" data-toggle="dropdown" aria-haspopup="true"
+            aria-expanded="false">
+            <svg class="language navbar-icon" 
xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 20 20">
+  <path d="M19.753 10.909c-.624-1.707-2.366-2.726-4.661-2.726-.09 
0-.176.002-.262.006l-.016-2.063 
3.525-.607c.115-.019.133-.119.109-.231-.023-.111-.167-.883-.188-.976-.027-.131-.102-.127-.207-.109-.104.018-3.25.461-3.25.461l-.013-2.078c-.001-.125-.069-.158-.194-.156l-1.025.016c-.105.002-.164.049-.162.148l.033
 2.307s-3.061.527-3.144.543c-.084.014-.17.053-.151.143.019.09.19 1.094.208 
1.172.018.08.072.129.188.107l2.924-.504.035 2.018c-1.077.281-1.801.824-2.256 
1.303-.768.807-1.207 1.887-1.2 [...]
+</svg>
+ English
+            </a>
+            <div class="dropdown-menu" aria-labelledby="dropdownNavLanguage" 
id="dropdownNavLanguageMenu">
+
+              <a href="/en/package" class="dropdown-item active"
+                data-lang="en">
+                English
+                </a>
+
+              <a href="/es-ES/package" class="dropdown-item"
+                data-lang="es">
+                Español
+                </a>
+
+              <a href="/fr/package" class="dropdown-item"
+                data-lang="fr">
+                Français
+                </a>
+
+              <a href="/id-ID/package" class="dropdown-item"
+                data-lang="id">
+                Bahasa Indonesia
+                </a>
+
+              <a href="/ja/package" class="dropdown-item"
+                data-lang="ja">
+                日本語
+                </a>
+
+              <a href="/pt-BR/package" class="dropdown-item"
+                data-lang="pt-br">
+                Português (Brasil)
+                </a>
+
+              <a href="/ru/package" class="dropdown-item"
+                data-lang="ru">
+                Русский
+                </a>
+
+              <a href="/tr/package" class="dropdown-item"
+                data-lang="tr">
+                Türkçe
+                </a>
+
+              <a href="/uk/package" class="dropdown-item"
+                data-lang="uk">
+                Українська
+                </a>
+
+              <a href="/zh-Hans/package" class="dropdown-item"
+                data-lang="zh-cn">
+                中文
+                </a>
+
+              <a href="/zh-Hant/package" class="dropdown-item"
+                data-lang="zh-hk">
+                繁體中文
+                </a>
+
+            </div>
+        </li>
+
+
+        <li class="nav-item">
+          <a class="nav-link" href="https://discord.gg/yarnpkg";>
+            <svg class="discord navbar-icon" 
xmlns="http://www.w3.org/2000/svg"; role="img" aria-labelledby="discord-svg" 
fill="none" viewBox="0 0 71 55">
+  <title id="discord-svg">Discord</title>
+<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 
0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 
4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 
1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 
0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 
4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 
45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5 [...]
+</svg>
+
+            <span class="sr-only">Discord</span>
+          </a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="https://twitter.com/yarnpkg";>
+            <svg class="twitter navbar-icon" 
xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 250 203.1" role="img" 
aria-labelledby="twitter-svg">
+  <title id="twitter-svg">Twitter</title>
+  <path d="M78.6 203.1c94.3 0 145.9-78.2 145.9-145.9 0-2.2 0-4.4-.1-6.6 10-7.3 
18.7-16.3 25.6-26.5-9.4 4.1-19.3 6.9-29.5 8.1 10.7-6.4 18.7-16.5 22.5-28.4-10.1 
6-21.1 10.2-32.6 12.4C191-4.5 158.5-5.5 137.8 14c-13.3 12.5-19 31.2-14.8 
49C81.9 60.9 43.4 41.4 17.4 9.4 3.8 32.8 10.7 62.8 33.3 
77.8c-8.2-.2-16.1-2.4-23.3-6.4v.6c0 24.4 17.2 45.4 41.2 50.3-7.6 2.1-15.5 
2.4-23.2.9 6.7 20.9 26 35.2 47.9 35.6-18.2 14.3-40.6 22-63.7 22-4.1 
0-8.2-.3-12.2-.7 23.5 15.1 50.7 23 78.6 23"/>
+</svg>
+
+            <span class="sr-only">Twitter</span>
+          </a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="https://www.facebook.com/yarnpkg";>
+            <svg class="facebook navbar-icon" 
xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 266.9 266.9" role="img" 
aria-labelledby="facebook-svg">
+  <title id="facebook-svg">Facebook</title>
+  <path d="M252.2 0H14.7C6.6 0 0 6.6 0 14.7v237.4c0 8.1 6.6 14.7 14.7 
14.7h127.8V163.5h-34.8v-40.3h34.8V93.6c0-34.5 21.1-53.2 51.8-53.2 14.7 0 27.4 
1.1 31.1 1.6v36h-21.3c-16.7 0-20 7.9-20 19.6v25.7H224l-5.2 
40.3h-34.7V267h68c8.1 0 14.7-6.6 14.7-14.7V14.7c.1-8.1-6.5-14.7-14.6-14.7z"/>
+</svg>
+
+            <span class="sr-only">Facebook</span>
+          </a>
+        </li>
+        <li class="nav-item">
+          <a class="nav-link" href="https://github.com/yarnpkg";>
+            <svg class="github navbar-icon" xmlns="http://www.w3.org/2000/svg"; 
viewBox="0 0 32.6 31.8" role="img" aria-labelledby="github-svg">
+  <title id="github-svg">GitHub</title>
+  <path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 
1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 
2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 
1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.1-.3-.7-2 .2-4.2 0 0 1.4-.4 
4.5 1.7 1.3-.4 2.7-.5 4.1-.5 1.4 0 2.8.2 4.1.5 3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 
3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3V31c0 
.4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 [...]
+</svg>
+
+            <span class="sr-only">GitHub</span>
+          </a>
+        </li>
+      </ul>
+    </div>
+  </div>
+</nav>
+
+    <div id="search">
+      <!-- Here to avoid flash of unstyled content on page load -->
+      <div class="ais-InstantSearch-root full-searchbox">
+        <form novalidate="" class="ais-SearchBox">
+          <div role="search" class="ais-SearchBox-form"></div>
+        </form>
+      </div>
+    </div>
+    <main>
+
+
+<div class="hero">
+  <div class="container">
+    <h1 class="hero-text display-4">Package detail</h1>
+  </div>
+</div>
+
+<div class="container">
+
+<div id="pkg-detail">
+  <div data-reactroot="" class="details row">
+    <section class="details-main col-lg-8">
+      <header class="details-main--header">
+        <h2 class="details-main--title d-inline-block m-2">…</h2>
+        <div class="details-main--info d-inline-block m-2"><a 
class="ais-Hit-ownerLink" href=""><img width="20" height="20" 
class="ais-Hit-ownerAvatar" 
src="https://res.cloudinary.com/hilnmyskv/image/fetch/w_40,h_40,f_auto,q_80,fl_lossy/https://avatars3.githubusercontent.com/u/22247014?v=3&amp;s=200";><!--
 react-text: 8 --><!-- /react-text --></a>
+          <span
+            class="ais-Hit-popular null" title="0 downloads in the last 30 
days">0</span>
+            <!-- react-empty: 10 -->
+            <!-- react-empty: 11 --><span class="ais-Hit-version"></span></div>
+        <div>
+          <p class="m-2 lead">…</p>
+        </div>
+        <!-- react-empty: 15 -->
+      </header>
+      <section id="readme" class="details-doc">
+        <h3 class="details-doc--title details-doc--title__readme py-1"><a 
href="#readme">readme</a></h3>
+      </section>
+    </section>
+    <aside class="details-side col-lg-4">
+      <article class="details-side--links">
+        <div class="detail-links">
+          <div target="_blank" rel="noopener noreferrer" 
class="details-links--link details-links--link__yarn"><img 
src="/assets/search/ico-yarn.svg" alt="">
+            <div class="copyable">
+              <div class="copyable--content"><span><a 
href="https://yarn.pm/…";><!-- react-text: 28 -->yarn.pm/<!-- /react-text 
--><!-- react-text: 29 -->…<!-- /react-text --></a></span></div>
+              <button
+                class="copyable--button user-select-none"><img 
src="/assets/detail/ico-copy-default.svg" alt="" class="copyable--button__img">
+                <!-- react-text: 32 -->copy
+                <!-- /react-text -->
+                </button>
+            </div>
+          </div><a target="_blank" rel="noopener noreferrer" 
href="https://github.com//"; class="details-links--link 
details-links--link__github"><img src="/assets/search/ico-github.svg" 
alt=""><!-- react-text: 35 -->/<!-- /react-text --></a>
+          <a
+            target="_blank" rel="noopener noreferrer" 
href="https://www.npmjs.com/package/…"; class="details-links--link 
details-links--link__npm"><img src="/assets/search/ico-npm.svg" alt="">
+            <!-- react-text: 38 -->…
+            <!-- /react-text -->
+            </a>
+        </div>
+      </article>
+      <article class="details-side--copy">
+        <h1>Use it</h1>
+        <div class="copyable"><code class="copyable--content"><!-- react-text: 
43 -->$ <!-- /react-text --><span><!-- react-text: 45 -->yarn add <!-- 
/react-text --><!-- react-text: 46 -->…<!-- /react-text --></span></code>
+          <button
+            class="copyable--button user-select-none"><img 
src="/assets/detail/ico-copy-default.svg" alt="" class="copyable--button__img">
+            <!-- react-text: 49 -->copy
+            <!-- /react-text -->
+            </button>
+        </div>
+        <div><a class="details-side--runkit" href="https://runkit.com/npm/…"; 
target="_blank" rel="noopener noreferrer">Try in RunKit</a>
+          <!-- react-text: 52 -->·
+          <!-- /react-text --><a href="#">Browse Files</a></div>
+      </article>
+      <article class="details-side--cdns">
+        <h1>CDNs</h1>
+        <dl></dl>
+      </article>
+      <article class="details-side--popularity">
+        <h1>Popularity</h1>
+        <dl></dl>
+      </article>
+      <!-- react-text: 57 -->
+      <!-- /react-text -->
+      <article class="details-side--usage">
+        <h1>Usage</h1>
+        <dl>
+          <div class="d-flex justify-items-between w-100"><img 
src="/assets/detail/ico-dependencies.svg" alt="">
+            <dt>Dependencies</dt><span class="dotted flex-grow"></span>
+            <dd>0</dd>
+          </div>
+          <div class="d-flex justify-items-between w-100"><img 
src="/assets/detail/ico-devdependencies.svg" alt="">
+            <dt>DevDependencies</dt><span class="dotted flex-grow"></span>
+            <dd>0</dd>
+          </div>
+          <div class="d-flex justify-items-between w-100"><img 
src="/assets/detail/ico-package-json.svg" alt="">
+            <dt>Packages</dt><span class="dotted flex-grow"></span>
+            <dd><a target="_blank" rel="noopener noreferrer" 
href="https://github.com///tree/master/package.json";>see package.json</a></dd>
+          </div>
+        </dl>
+      </article>
+      <article class="details-side--versions">
+        <h1>Versions</h1>
+        <dl></dl>
+      </article>
+      <article class="details-side--contributors">
+        <h1>Contributors</h1>
+        <ul class="list-unstyled m-2">
+          <li class="mb-1">
+            <a class="ais-Hit-ownerLink" href=""><img width="20" height="20" 
class="ais-Hit-ownerAvatar" 
src="https://res.cloudinary.com/hilnmyskv/image/fetch/w_40,h_40,f_auto,q_80,fl_lossy/https://avatars3.githubusercontent.com/u/22247014?v=3&amp;s=200";>
+            <!-- react-text: 86 -->
+            <!-- /react-text -->
+            </a>
+          </li>
+        </ul>
+      </article>
+    </aside>
+  </div>
+</div>
+
+</div>
+</main>
+
+
+
+<hr class="footer-divider">
+
+<div class="container">
+  <footer class="footer">
+    <div class="footer-left">
+      <span class="footer-item">Yarn</span>
+      <span class="footer-item"><a 
href="https://github.com/yarnpkg/yarn/blob/master/LICENSE";>Distributed under 
BSD License</a></span>
+      <span class="footer-item"><a href="/en/org/code-of-conduct">Code of 
Conduct</a></span>
+    </div>
+
+    <div class="footer-right">
+
+
+
+        <span class="footer-item"><a 
href="https://github.com/yarnpkg/website/edit/master/lang/en/package.html";>Edit 
this page</a></span>
+
+    </div>
+  </footer>
+</div>
+<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new 
Date();a=s.createElement(o),
+  
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', 'UA-85522875-1', 'auto');
+  ga('send', 'pageview');
+</script>
+
+
+
+<script>
+  var i18n_default = {"search_placeholder":"Search packages (i.e. babel, 
webpack, react…)","search_by_algolia":"Search by 
Algolia","search_by_read_more":"read how it works","no_package_found":"No 
package {name} was found","no_results_docsearch":"Were you looking for 
something in the 
{documentation_link}?","documentation":"documentation","downloads_in_last_30_days":"{count}
 downloads in the last 30 days","npm_page_for":"npm page for 
{name}","repository_of":"{provider} repository of {name} [...]
+  window.i18n = {"search_placeholder":"Search packages (i.e. babel, webpack, 
react…)","search_by_algolia":"Search by Algolia","search_by_read_more":"read 
how it works","no_package_found":"No package {name} was 
found","no_results_docsearch":"Were you looking for something in the 
{documentation_link}?","documentation":"documentation","downloads_in_last_30_days":"{count}
 downloads in the last 30 days","npm_page_for":"npm page for 
{name}","repository_of":"{provider} repository of {name}","np [...]
+  window.i18n.url_base = "/en";
+  window.i18n.active_language = "en";
+
+  // give defaults
+  function copyDefaults(from, to) {
+    for (var key in from) {
+      if (from[key] !== null && typeof from[key] === 'object') {
+        copyDefaults(from[key], to[key] || (to[key] = {}));
+        continue;
+      }
+      if (to.hasOwnProperty(key) === false || to[key] === null) {
+        to[key] = from[key];
+      }
+    }
+  }
+
+  copyDefaults(i18n_default, window.i18n);
+</script>
+
+    <script src="/js/build/vendor.bb983233e26fd3fd2fe3.js"></script>
+    <script src="/js/build/common.2a29ed27bfa751cd6609.js"></script>
+
+
+      <script src="/js/build/package.302c84e81c9aeae765da.js"></script>
+
+  </body>
+</html>
diff --git a/atr/static/css/root.css b/atr/static/css/root.css
index 8da932b..3cbc448 100644
--- a/atr/static/css/root.css
+++ b/atr/static/css/root.css
@@ -52,3 +52,7 @@ h1 {
     max-width: 800px;
     color: #555;
 }
+
+.ribbon {
+    background: linear-gradient(90deg, #282661 0%, #662f8f 20%, #9e2165 40%, 
#cb2138 60%, #ea7826 80%, #f7ae18 100%);
+}
diff --git a/atr/static/webfonts/inter-v.woff2 
b/atr/static/webfonts/inter-v.woff2
new file mode 100644
index 0000000..5a8d3e7
Binary files /dev/null and b/atr/static/webfonts/inter-v.woff2 differ
diff --git a/atr/static/webfonts/jost-v.woff2 b/atr/static/webfonts/jost-v.woff2
new file mode 100644
index 0000000..eedcc3f
Binary files /dev/null and b/atr/static/webfonts/jost-v.woff2 differ
diff --git a/atr/templates/add-release-candidate.html 
b/atr/templates/add-release-candidate.html
index d95d582..c8d2d34 100644
--- a/atr/templates/add-release-candidate.html
+++ b/atr/templates/add-release-candidate.html
@@ -1,107 +1,110 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-    <meta name="description" content="Add a release candidate to the 
database." />
-    <title>ATR | Add Release Candidate</title>
-    <link rel="stylesheet" href="{{ url_for('static', filename='root.css') }}" 
/>
-    <style>
-        .form-group {
-            margin-bottom: 1rem;
-        }
-
-        .form-group label {
-            display: block;
-            margin-bottom: 0.5rem;
-        }
-
-        .error-message {
-            color: #dc3545;
-            margin-top: 0.25rem;
-        }
-
-        select,
-        input[type="file"] {
-            display: block;
-            margin-bottom: 0.5rem;
-        }
-
-        small {
-            color: #666;
-        }
-
-        button {
-            margin-top: 1rem;
-            padding: 0.5rem 1rem;
-        }
-
-        button:disabled {
-            opacity: 0.5;
-            cursor: not-allowed;
-        }
-    </style>
-  </head>
-  <body>
-    <h1>Add Release Candidate</h1>
-    <p class="intro">On this page, you can add a release candidate to the 
database.</p>
-
-    <div class="user-info">
-      <p>
-        Welcome, <strong>{{ asf_id }}</strong>! You are authenticated as an 
ASF committer.
-      </p>
-
-      {% if pmc_memberships %}
-        <h3>Your PMC Memberships:</h3>
-        <ul>
-          {% for pmc in pmc_memberships %}<li>{{ pmc }}</li>{% endfor %}
-        </ul>
-      {% endif %}
+{% extends "layouts/base.html" %}
+
+{% block title %}
+  ATR | Add Release Candidate
+{% endblock title %}
+
+{% block description %}
+  Add a release candidate to the database.
+{% endblock description %}
+
+{% block stylesheets %}
+  {{ super() }}
+  <style>
+      .form-group {
+          margin-bottom: 1rem;
+      }
+
+      .form-group label {
+          display: block;
+          margin-bottom: 0.5rem;
+      }
+
+      .error-message {
+          color: #dc3545;
+          margin-top: 0.25rem;
+      }
+
+      select,
+      input[type="file"] {
+          display: block;
+          margin-bottom: 0.5rem;
+      }
+
+      small {
+          color: #666;
+      }
+
+      button {
+          margin-top: 1rem;
+          padding: 0.5rem 1rem;
+      }
 
-      {% if committer_projects %}
-        <h3>Your Committer Access:</h3>
-        <ul>
-          {% for project in committer_projects %}<li>{{ project }}</li>{% 
endfor %}
-        </ul>
+      button:disabled {
+          opacity: 0.5;
+          cursor: not-allowed;
+      }
+  </style>
+{% endblock stylesheets %}
+
+{% block content %}
+  <h1>Add Release Candidate</h1>
+  <p class="intro">On this page, you can add a release candidate to the 
database.</p>
+
+  <div class="user-info">
+    <p>
+      Welcome, <strong>{{ asf_id }}</strong>! You are authenticated as an ASF 
committer.
+    </p>
+
+    {% if pmc_memberships %}
+      <h3>Your PMC Memberships:</h3>
+      <ul>
+        {% for pmc in pmc_memberships %}<li>{{ pmc }}</li>{% endfor %}
+      </ul>
+    {% endif %}
+
+    {% if committer_projects %}
+      <h3>Your Committer Access:</h3>
+      <ul>
+        {% for project in committer_projects %}<li>{{ project }}</li>{% endfor 
%}
+      </ul>
+    {% endif %}
+  </div>
+
+  <form method="post" enctype="multipart/form-data">
+    <div class="form-group">
+      <label for="project_name">Project:</label>
+      <select id="project_name" name="project_name" required>
+        <option value="">Select a project...</option>
+        {% for pmc in pmc_memberships %}<option value="{{ pmc }}">{{ pmc 
}}</option>{% endfor %}
+      </select>
+      {% if not pmc_memberships %}
+        <p class="error-message">You must be a PMC member to submit a release 
candidate.</p>
       {% endif %}
     </div>
 
-    <form method="post" enctype="multipart/form-data">
-      <div class="form-group">
-        <label for="project_name">Project:</label>
-        <select id="project_name" name="project_name" required>
-          <option value="">Select a project...</option>
-          {% for pmc in pmc_memberships %}<option value="{{ pmc }}">{{ pmc 
}}</option>{% endfor %}
-        </select>
-        {% if not pmc_memberships %}
-          <!-- TODO: Should probably move this up and do if/elif -->
-          <p class="error-message">You must be a PMC member to submit a 
release candidate.</p>
-        {% endif %}
-      </div>
-
-      <div class="form-group">
-        <label for="release_artifact">Release Candidate Archive:</label>
-        <input type="file"
-               id="release_artifact"
-               name="release_artifact"
-               required
-               
accept="application/gzip,application/x-gzip,application/x-tar,application/zip,application/java-archive,.tar.gz,.tgz,.zip,.jar"
-               aria-describedby="artifact-help" />
-        <span id="artifact-help">Upload the release candidate archive (tar.gz, 
zip, or jar)</span>
-      </div>
-
-      <div class="form-group">
-        <label for="release_signature">Detached GPG Signature:</label>
-        <input type="file"
-               id="release_signature"
-               name="release_signature"
-               required
-               accept="application/pgp-signature,.asc"
-               aria-describedby="signature-help" />
-        <span id="signature-help">Upload the detached GPG signature (.asc) 
file for the release candidate</span>
-      </div>
-
-      <button type="submit" {% if not pmc_memberships %}disabled{% endif 
%}>Submit Release Candidate</button>
-    </form>
-  </body>
-</html>
+    <div class="form-group">
+      <label for="release_artifact">Release Candidate Archive:</label>
+      <input type="file"
+             id="release_artifact"
+             name="release_artifact"
+             required
+             
accept="application/gzip,application/x-gzip,application/x-tar,application/zip,application/java-archive,.tar.gz,.tgz,.zip,.jar"
+             aria-describedby="artifact-help" />
+      <span id="artifact-help">Upload the release candidate archive (tar.gz, 
zip, or jar)</span>
+    </div>
+
+    <div class="form-group">
+      <label for="release_signature">Detached GPG Signature:</label>
+      <input type="file"
+             id="release_signature"
+             name="release_signature"
+             required
+             accept="application/pgp-signature,.asc"
+             aria-describedby="signature-help" />
+      <span id="signature-help">Upload the detached GPG signature (.asc) file 
for the release candidate</span>
+    </div>
+
+    <button type="submit" {% if not pmc_memberships %}disabled{% endif 
%}>Submit Release Candidate</button>
+  </form>
+{% endblock content %}
diff --git a/atr/templates/data-browser.html b/atr/templates/data-browser.html
index 3281b48..d801071 100644
--- a/atr/templates/data-browser.html
+++ b/atr/templates/data-browser.html
@@ -1,89 +1,93 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-    <meta name="description" content="Browse all records in the database." />
-    <title>ATR | Data Browser</title>
-    <link rel="stylesheet" href="{{ url_for('static', filename='root.css') }}" 
/>
-    <style>
-        .model-nav {
-            margin: 1rem 0;
-            padding: 0.5rem;
-            background: #f5f5f5;
-            border-radius: 4px;
-        }
+{% extends "layouts/base.html" %}
 
-        .model-nav a {
-            margin-right: 1rem;
-            padding: 0.25rem 0.5rem;
-            text-decoration: none;
-            color: #333;
-        }
+{% block title %}
+  ATR | Data Browser
+{% endblock title %}
 
-        .model-nav a.active {
-            background: #333;
-            color: white;
-            border-radius: 2px;
-        }
+{% block description %}
+  Browse all records in the database.
+{% endblock description %}
 
-        .record {
-            border: 1px solid #ddd;
-            padding: 1rem;
-            margin-bottom: 1rem;
-            border-radius: 4px;
-        }
+{% block stylesheets %}
+  {{ super() }}
+  <style>
+      .model-nav {
+          margin: 1rem 0;
+          padding: 0.5rem;
+          background: #f5f5f5;
+          border-radius: 4px;
+      }
 
-        .record pre {
-            background: #f5f5f5;
-            padding: 0.5rem;
-            border-radius: 2px;
-            overflow-x: auto;
-        }
+      .model-nav a {
+          margin-right: 1rem;
+          padding: 0.25rem 0.5rem;
+          text-decoration: none;
+          color: #333;
+      }
 
-        .record-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 0.5rem;
-        }
+      .model-nav a.active {
+          background: #333;
+          color: white;
+          border-radius: 2px;
+      }
 
-        .record-meta {
-            color: #666;
-            font-size: 0.9em;
-        }
+      .record {
+          border: 1px solid #ddd;
+          padding: 1rem;
+          margin-bottom: 1rem;
+          border-radius: 4px;
+      }
 
-        .no-records {
-            color: #666;
-            font-style: italic;
-        }
-    </style>
-  </head>
-  <body>
-    <h1>Data Browser</h1>
-    <p class="intro">Browse all records in the database.</p>
+      .record pre {
+          background: #f5f5f5;
+          padding: 0.5rem;
+          border-radius: 2px;
+          overflow-x: auto;
+      }
 
-    <div class="model-nav">
-      {% for model_name in models %}
-        <a href="{{ url_for('root_admin_database', model=model_name) }}"
-           {% if model == model_name %}class="active"{% endif %}>{{ model_name 
}}</a>
-      {% endfor %}
-    </div>
+      .record-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 0.5rem;
+      }
+
+      .record-meta {
+          color: #666;
+          font-size: 0.9em;
+      }
+
+      .no-records {
+          color: #666;
+          font-style: italic;
+      }
+  </style>
+{% endblock stylesheets %}
 
-    {% if records %}
-      <div class="records">
-        {% for record in records %}
-          <div class="record">
-            <div class="record-header">
-              <h3>{{ record.get('id', record.get('storage_key', 'Unknown ID') 
) }}</h3>
-              <span class="record-meta">{{ model }}</span>
-            </div>
-            <pre>{{ record | tojson(indent=2) }}</pre>
+{% block content %}
+  <h1>Data Browser</h1>
+  <p class="intro">Browse all records in the database.</p>
+
+  <div class="model-nav">
+    {% for model_name in models %}
+      <a href="{{ url_for('root_admin_database', model=model_name) }}"
+         {% if model == model_name %}class="active"{% endif %}>{{ model_name 
}}</a>
+    {% endfor %}
+  </div>
+
+  {% if records %}
+    <div class="records">
+      {% for record in records %}
+        <div class="record">
+          <div class="record-header">
+            <h3>{{ record.get('id', record.get('storage_key', 'Unknown ID') ) 
}}</h3>
+            <span class="record-meta">{{ model }}</span>
           </div>
-        {% endfor %}
-      </div>
-    {% else %}
-      <p class="no-records">No records found for {{ model }}.</p>
-    {% endif %}
-  </body>
-</html>
+          <pre>{{ record | tojson(indent=2) }}</pre>
+        </div>
+      {% endfor %}
+    </div>
+  {% else %}
+    <p class="no-records">No records found for {{ model }}.</p>
+  {% endif %}
+{% endblock content %}
diff --git a/atr/templates/includes/footer.html 
b/atr/templates/includes/footer.html
index 38535c7..e39063a 100644
--- a/atr/templates/includes/footer.html
+++ b/atr/templates/includes/footer.html
@@ -1,5 +1,8 @@
 <footer class="footer mt-auto py-3">
-  <div class="container">
-    <span class="text-muted">Copyright © 2025, The Apache Software Foundation. 
Material licensed under the <a 
href="https://www.apache.org/licenses/LICENSE-2.0";>Apache License, version 
2.0</a>. For inquiries, contact [email protected]</span>
-  </div>
+  <p class="text-muted">
+    Copyright © 2025, The Apache Software Foundation. Material licensed under 
the <a href="https://www.apache.org/licenses/LICENSE-2.0";>Apache License, 
version 2.0</a>.
+  </p>
+  <p class="text-muted">
+    For inquiries, contact <a 
href="mailto:[email protected]";>[email protected]</a>
+  </p>
 </footer>
diff --git a/atr/templates/includes/sidebar.html 
b/atr/templates/includes/sidebar.html
new file mode 100644
index 0000000..6a230d1
--- /dev/null
+++ b/atr/templates/includes/sidebar.html
@@ -0,0 +1,44 @@
+<aside class="sidebar">
+  <div class="sidebar-header">
+    <a href="{{ url_for('root') }}" class="site-title">
+      <h1>
+        A<span>pache</span>
+        <br />
+        T<span>rusted</span>
+        <br />
+        R<span>elease</span>
+      </h1>
+    </a>
+  </div>
+  <div class="user-section">
+    {% if current_user %}
+      <div class="user-info">
+        <span>{{ current_user.fullname }}</span>
+        (<code>{{ current_user.uid }}</code>)
+      </div>
+      <a href="#"
+         onclick="location.href='/auth?logout=' + window.location.pathname;"
+         class="logout-link">Logout</a>
+    {% else %}
+      <a href="#"
+         onclick="location.href='/auth?login=' + window.location.pathname;"
+         class="login-link">Login</a>
+    {% endif %}
+  </div>
+  <nav>
+    <ul>
+      <li>
+        <a href="{{ url_for('root') }}"
+           {% if request.endpoint == 'root' %}class="active"{% endif 
%}>Home</a>
+      </li>
+      <li>
+        <a href="{{ url_for('root_pages') }}"
+           {% if request.endpoint == 'root_pages' %}class="active"{% endif 
%}>Pages</a>
+      </li>
+      <li>
+        <a href="{{ url_for('root_pmc_directory') }}"
+           {% if request.endpoint == 'root_pmc_directory' %}class="active"{% 
endif %}>PMC Directory</a>
+      </li>
+    </ul>
+  </nav>
+</aside>
diff --git a/atr/templates/layouts/base.html b/atr/templates/layouts/base.html
index 7805157..87880cc 100644
--- a/atr/templates/layouts/base.html
+++ b/atr/templates/layouts/base.html
@@ -11,27 +11,36 @@
     </title>
     {% block stylesheets %}
       <link rel="stylesheet"
-            href="{{ url_for('static', filename='css/bootstrap.min.css') }}" />
+            href="{{ url_for('static', filename='css/normalize.css') }}" />
       <link rel="stylesheet"
-            href="{{ url_for('static', filename='css/fontawesome.all.min.css') 
}}" />
-      <link rel="stylesheet"
-            href="{{ url_for('static', filename='css/root.css') }}" />
+            href="{{ url_for('static', filename='css/atr.css') }}" />
     {% endblock stylesheets %}
   </head>
   <body class="{%- block body_class -%}{%- endblock body_class -%}">
     <div class="wrapper">
-      {% include "includes/navigation.html" %}
+      <div class="ribbon"></div>
+      <div class="content">
+        <input type="checkbox" id="nav-toggle" class="nav-toggle" />
+        <label for="nav-toggle" class="hamburger" aria-label="Menu">
+          <span class="hamburger-line"></span>
+          <span class="hamburger-line"></span>
+          <span class="hamburger-line"></span>
+        </label>
+
+        {% include "includes/sidebar.html" %}
 
-      {% block content %}
-      {% endblock content %}
-      {% include "includes/footer.html" %}
+        <div class="main-container">
+          <main class="main-content">
+            {% block content %}
+            {% endblock content %}
+          </main>
+          {% include "includes/footer.html" %}
 
+        </div>
+      </div>
     </div>
 
     {% block javascripts %}
-      <script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') 
}}"></script>
-
     {% endblock javascripts %}
-
   </body>
 </html>
diff --git a/atr/templates/release-signature-verify.html 
b/atr/templates/release-signature-verify.html
index 39e895c..2b4d14f 100644
--- a/atr/templates/release-signature-verify.html
+++ b/atr/templates/release-signature-verify.html
@@ -1,164 +1,168 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-    <meta name="description" content="Verify release candidate signatures." />
-    <title>ATR | Verify Release Signatures</title>
-    <link rel="stylesheet" href="{{ url_for('static', filename='root.css') }}" 
/>
-    <style>
-        .release-info {
-            margin-bottom: 2rem;
-        }
-
-        .package-list {
-            margin: 1rem 0;
-        }
-
-        .package {
-            border: 1px solid #ddd;
-            padding: 1rem;
-            margin: 1rem 0;
-            border-radius: 4px;
-        }
-
-        .package-info {
-            margin-bottom: 1rem;
-        }
-
-        .verification-status {
-            margin-top: 1rem;
-            padding: 1rem;
-            border-radius: 4px;
-            background: #f5f5f5;
-        }
-
-        .navigation {
-            margin-top: 2rem;
-        }
-
-        .navigation a {
-            margin-right: 1rem;
-        }
-
-        .error {
-            color: #dc3545;
-            font-weight: bold;
-        }
-
-        .status.success {
-            color: #28a745;
-        }
-
-        .status.failure {
-            color: #dc3545;
-        }
-
-        .signature-details {
-            margin-top: 1rem;
-            padding: 1rem;
-            border-radius: 4px;
-            background: #f5f5f5;
-        }
-
-        .debug-info {
-            margin-top: 1rem;
-            padding: 1rem;
-            border-radius: 4px;
-            background: #f8f9fa;
-            border: 1px solid #dee2e6;
-        }
-
-        .debug-info h3 {
-            margin-top: 0;
-            color: #666;
-        }
-
-        .debug-info dl {
-            margin: 0;
-            display: grid;
-            grid-template-columns: auto 1fr;
-            gap: 0.5rem 1rem;
-        }
-
-        .debug-info dt {
-            font-weight: bold;
-            color: #666;
-        }
-
-        .debug-info dd {
-            margin: 0;
-            word-break: break-all;
-        }
-
-        pre.stderr {
-            background: #f8f9fa;
-            padding: 0.5rem;
-            border-radius: 2px;
-            overflow-x: auto;
-            margin: 0.5rem 0;
-            white-space: pre-wrap;
-        }
-    </style>
-  </head>
-  <body>
-    <h1>Verify Release Signatures</h1>
-
-    <div class="release-info">
-      <h2>{{ release.pmc.project_name }}</h2>
-      <p>
-        Stage: {{ release.stage.value }}
-        •
-        Phase: {{ release.phase.value }}
-      </p>
-    </div>
-
-    <div class="package-list">
-      {% for result in verification_results %}
-        <div class="package">
-          <div class="package-info">
-            <div>File: {{ result.file }}</div>
-          </div>
-
-          <div class="verification-status">
-            {% if result.error %}
-              <p class="error">Error: {{ result.error }}</p>
-            {% else %}
-              <p class="status {% if result.verified %}success{% else 
%}failure{% endif %}">Status: {{ result.status }}</p>
-              {% if result.verified %}
-                <div class="signature-details">
-                  <p>Key ID: {{ result.key_id }}</p>
-                  <p>Signed by: {{ result.username }} &lt;{{ result.email 
}}&gt;</p>
-                  <p>Timestamp: {{ result.timestamp }}</p>
-                </div>
-              {% endif %}
-            {% endif %}
+{% extends "layouts/base.html" %}
+
+{% block title %}
+  ATR | Verify Release Signatures
+{% endblock title %}
+
+{% block description %}
+  Verify release candidate signatures.
+{% endblock description %}
+
+{% block stylesheets %}
+  {{ super() }}
+  <style>
+      .release-info {
+          margin-bottom: 2rem;
+      }
+
+      .package-list {
+          margin: 1rem 0;
+      }
+
+      .package {
+          border: 1px solid #ddd;
+          padding: 1rem;
+          margin: 1rem 0;
+          border-radius: 4px;
+      }
+
+      .package-info {
+          margin-bottom: 1rem;
+      }
+
+      .verification-status {
+          margin-top: 1rem;
+          padding: 1rem;
+          border-radius: 4px;
+          background: #f5f5f5;
+      }
+
+      .navigation {
+          margin-top: 2rem;
+      }
+
+      .navigation a {
+          margin-right: 1rem;
+      }
+
+      .error {
+          color: #dc3545;
+          font-weight: bold;
+      }
+
+      .status.success {
+          color: #28a745;
+      }
+
+      .status.failure {
+          color: #dc3545;
+      }
+
+      .signature-details {
+          margin-top: 1rem;
+          padding: 1rem;
+          border-radius: 4px;
+          background: #f5f5f5;
+      }
+
+      .debug-info {
+          margin-top: 1rem;
+          padding: 1rem;
+          border-radius: 4px;
+          background: #f8f9fa;
+          border: 1px solid #dee2e6;
+      }
+
+      .debug-info h3 {
+          margin-top: 0;
+          color: #666;
+      }
+
+      .debug-info dl {
+          margin: 0;
+          display: grid;
+          grid-template-columns: auto 1fr;
+          gap: 0.5rem 1rem;
+      }
+
+      .debug-info dt {
+          font-weight: bold;
+          color: #666;
+      }
+
+      .debug-info dd {
+          margin: 0;
+          word-break: break-all;
+      }
+
+      pre.stderr {
+          background: #f8f9fa;
+          padding: 0.5rem;
+          border-radius: 2px;
+          overflow-x: auto;
+          margin: 0.5rem 0;
+          white-space: pre-wrap;
+      }
+  </style>
+{% endblock stylesheets %}
+
+{% block content %}
+  <h1>Verify Release Signatures</h1>
+
+  <div class="release-info">
+    <h2>{{ release.pmc.project_name }}</h2>
+    <p>
+      Stage: {{ release.stage.value }}
+      •
+      Phase: {{ release.phase.value }}
+    </p>
+  </div>
+
+  <div class="package-list">
+    {% for result in verification_results %}
+      <div class="package">
+        <div class="package-info">
+          <div>File: {{ result.file }}</div>
+        </div>
 
-            {% if result.debug_info %}
-              <div class="debug-info">
-                <h3>Debug Information</h3>
-                <dl>
-                  {% for key, value in result.debug_info.items() %}
-                    <dt>{{ key }}</dt>
-                    <dd>
-                      {% if key == 'stderr' and value != 'Not available' %}
-                        <pre class="stderr">{{ value }}</pre>
-                      {% else %}
-                        {{ value }}
-                      {% endif %}
-                    </dd>
-                  {% endfor %}
-                </dl>
+        <div class="verification-status">
+          {% if result.error %}
+            <p class="error">Error: {{ result.error }}</p>
+          {% else %}
+            <p class="status {% if result.verified %}success{% else 
%}failure{% endif %}">Status: {{ result.status }}</p>
+            {% if result.verified %}
+              <div class="signature-details">
+                <p>Key ID: {{ result.key_id }}</p>
+                <p>Signed by: {{ result.username }} &lt;{{ result.email 
}}&gt;</p>
+                <p>Timestamp: {{ result.timestamp }}</p>
               </div>
             {% endif %}
-          </div>
+          {% endif %}
+
+          {% if result.debug_info %}
+            <div class="debug-info">
+              <h3>Debug Information</h3>
+              <dl>
+                {% for key, value in result.debug_info.items() %}
+                  <dt>{{ key }}</dt>
+                  <dd>
+                    {% if key == 'stderr' and value != 'Not available' %}
+                      <pre class="stderr">{{ value }}</pre>
+                    {% else %}
+                      {{ value }}
+                    {% endif %}
+                  </dd>
+                {% endfor %}
+              </dl>
+            </div>
+          {% endif %}
         </div>
-      {% endfor %}
-    </div>
-
-    <div class="navigation">
-      <a href="{{ url_for('root_user_uploads') }}">Back to Your Uploads</a>
-      <a href="{{ url_for('root_pages') }}">Return to Main Page</a>
-    </div>
-  </body>
-</html>
+      </div>
+    {% endfor %}
+  </div>
+
+  <div class="navigation">
+    <a href="{{ url_for('root_user_uploads') }}">Back to Your Uploads</a>
+    <a href="{{ url_for('root_pages') }}">Return to Main Page</a>
+  </div>
+{% endblock content %}
diff --git a/atr/templates/user-uploads.html b/atr/templates/user-uploads.html
index 363b2db..ed36f60 100644
--- a/atr/templates/user-uploads.html
+++ b/atr/templates/user-uploads.html
@@ -1,89 +1,93 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
-    <meta name="description" content="Your uploaded release candidates." />
-    <title>ATR | Your Uploads</title>
-    <link rel="stylesheet" href="{{ url_for('static', filename='root.css') }}" 
/>
-    <style>
-        .release-list {
-            margin: 1rem 0;
-        }
+{% extends "layouts/base.html" %}
 
-        .release {
-            border: 1px solid #ddd;
-            padding: 1rem;
-            margin-bottom: 1rem;
-            border-radius: 4px;
-        }
+{% block title %}
+  ATR | Your Uploads
+{% endblock title %}
 
-        .release-header {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            margin-bottom: 0.5rem;
-        }
+{% block description %}
+  Your uploaded release candidates.
+{% endblock description %}
 
-        .release-meta {
-            color: #666;
-            font-size: 0.9em;
-        }
+{% block stylesheets %}
+  {{ super() }}
+  <style>
+      .release-list {
+          margin: 1rem 0;
+      }
 
-        .package-list {
-            margin-top: 0.5rem;
-        }
+      .release {
+          border: 1px solid #ddd;
+          padding: 1rem;
+          margin-bottom: 1rem;
+          border-radius: 4px;
+      }
 
-        .package {
-            background: #f5f5f5;
-            padding: 0.5rem;
-            margin: 0.25rem 0;
-            border-radius: 2px;
-        }
+      .release-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 0.5rem;
+      }
 
-        .no-releases {
-            color: #666;
-            font-style: italic;
-        }
-    </style>
-  </head>
-  <body>
-    <h1>Your Uploads</h1>
-    <p class="intro">Here are all the release candidates you've uploaded.</p>
+      .release-meta {
+          color: #666;
+          font-size: 0.9em;
+      }
 
-    {% if releases %}
-      <div class="release-list">
-        {% for release in releases %}
-          <div class="release">
-            <div class="release-header">
-              <h3>{{ release.pmc.project_name }}</h3>
-              <span class="release-meta">
-                Stage: {{ release.stage.value }}
-                •
-                Phase: {{ release.phase.value }}
-              </span>
-            </div>
-            <div class="package-list">
-              {% for package in release.packages %}
-                <div class="package">
-                  <div>File: {{ package.file }}</div>
-                  <div>Signature: {{ package.signature }}</div>
-                  <div>Checksum (SHA-512): {{ package.checksum }}</div>
-                  <p class="package-actions">
-                    <a href="{{ url_for('root_release_signatures_verify', 
release_key=release.storage_key) }}">Verify Signatures</a>
-                  </p>
-                </div>
-              {% endfor %}
-            </div>
+      .package-list {
+          margin-top: 0.5rem;
+      }
+
+      .package {
+          background: #f5f5f5;
+          padding: 0.5rem;
+          margin: 0.25rem 0;
+          border-radius: 2px;
+      }
+
+      .no-releases {
+          color: #666;
+          font-style: italic;
+      }
+  </style>
+{% endblock stylesheets %}
+
+{% block content %}
+  <h1>Your Uploads</h1>
+  <p class="intro">Here are all the release candidates you've uploaded.</p>
+
+  {% if releases %}
+    <div class="release-list">
+      {% for release in releases %}
+        <div class="release">
+          <div class="release-header">
+            <h3>{{ release.pmc.project_name }}</h3>
+            <span class="release-meta">
+              Stage: {{ release.stage.value }}
+              •
+              Phase: {{ release.phase.value }}
+            </span>
+          </div>
+          <div class="package-list">
+            {% for package in release.packages %}
+              <div class="package">
+                <div>File: {{ package.file }}</div>
+                <div>Signature: {{ package.signature }}</div>
+                <div>Checksum (SHA-512): {{ package.checksum }}</div>
+                <p class="package-actions">
+                  <a href="{{ url_for('root_release_signatures_verify', 
release_key=release.storage_key) }}">Verify Signatures</a>
+                </p>
+              </div>
+            {% endfor %}
           </div>
-        {% endfor %}
-      </div>
-    {% else %}
-      <p class="no-releases">You haven't uploaded any release candidates 
yet.</p>
-    {% endif %}
+        </div>
+      {% endfor %}
+    </div>
+  {% else %}
+    <p class="no-releases">You haven't uploaded any release candidates yet.</p>
+  {% endif %}
 
-    <p>
-      <a href="{{ url_for('root_add_release_candidate') }}">Upload a new 
release candidate</a>
-    </p>
-  </body>
-</html>
+  <p>
+    <a href="{{ url_for('root_add_release_candidate') }}">Upload a new release 
candidate</a>
+  </p>
+{% endblock content %}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to