diff --git a/canary.sh b/canary.sh
index a5f349f..781c316 100755
--- a/canary.sh
+++ b/canary.sh
@@ -1,4 +1,16 @@
-python canary.py
-git add - A .
-git commit -m "New canary"
-git push origin main
+#!/bin/bash
+
+# Run the canary script
+python3 sw1tch/canary.py
+
+# Check if the canary.txt was generated successfully
+if [ $? -eq 0 ] && [ -f sw1tch/data/canary.txt ]; then
+    # Stage all changes, commit, and push
+    git add sw1tch/data/canary.txt
+    git commit -m "Update warrant canary - $(date +%Y-%m-%d)"
+    git push origin main
+    echo "Warrant canary updated and pushed to repository."
+else
+    echo "Failed to generate or find canary.txt. Git operations aborted."
+    exit 1
+fi
diff --git a/sw1tch/__main__.py b/sw1tch/__main__.py
index 1ee97b8..6d2ffee 100644
--- a/sw1tch/__main__.py
+++ b/sw1tch/__main__.py
@@ -2,16 +2,14 @@ import os
 from fastapi import FastAPI
 from fastapi.staticfiles import StaticFiles
 from sw1tch import BASE_DIR, CustomLoggingMiddleware
-from sw1tch.routes.public import router as public_router
-from sw1tch.routes.admin import router as admin_router
-from sw1tch.routes.canary import router as canary_router 
+from sw1tch.routes import admin, canary, public
 
 app = FastAPI()
 app.add_middleware(CustomLoggingMiddleware)
 app.mount("/static", StaticFiles(directory=os.path.join(BASE_DIR, "static")), name="static")
-app.include_router(public_router)
-app.include_router(admin_router)
-app.include_router(canary_router)
+
+app.include_router(admin.router)
+app.include_router(public.router)
 
 if __name__ == "__main__":
     import uvicorn
diff --git a/sw1tch/canary.py b/sw1tch/canary.py
old mode 100755
new mode 100644
index 3ed5fef..d8c2465
--- a/sw1tch/canary.py
+++ b/sw1tch/canary.py
@@ -12,32 +12,52 @@ from pathlib import Path
 from requests.adapters import HTTPAdapter
 from urllib3.util.retry import Retry
 
-# File paths
-CONFIG_FILE = "config.yaml"
-OUTPUT_FILE = "canary.txt"
-TEMP_MESSAGE_FILE = "temp_canary_message.txt"
+# File paths relative to the sw1tch/ directory
+BASE_DIR = Path(__file__).parent
+CONFIG_FILE = BASE_DIR / "config" / "config.yaml"
+ATTESTATIONS_FILE = BASE_DIR / "config" / "attestations.txt"
+OUTPUT_FILE = BASE_DIR / "data" / "canary.txt"
+TEMP_MESSAGE_FILE = BASE_DIR / "data" / "temp_canary_message.txt"
 
 def load_config():
     """Load configuration from YAML file."""
     try:
-        if not os.path.exists(CONFIG_FILE):
+        if not CONFIG_FILE.exists():
             print(f"Error: Configuration file '{CONFIG_FILE}' not found.")
             sys.exit(1)
         with open(CONFIG_FILE, 'r') as file:
             config = yaml.safe_load(file)
-        required = [('gpg', 'key_id'), ('canary', 'organization'), ('canary', 'attestations')]
-        for section, field in required:
-            if section not in config or field not in config[section]:
-                print(f"Error: Missing required field '{section}.{field}' in config.")
-                sys.exit(1)
+        # Adjust to match config.yaml structure
+        required = [
+            ('canary', 'organization'),
+            ('canary', 'gpg_key_id'),
+            ('canary', 'credentials', 'username'),
+            ('canary', 'credentials', 'password'),
+            ('canary', 'room')
+        ]
+        for path in required:
+            current = config
+            for key in path:
+                if key not in current:
+                    print(f"Error: Missing required field '{'.'.join(path)}' in config.")
+                    sys.exit(1)
+                current = current[key]
         return config
     except Exception as e:
         print(f"Error loading configuration: {e}")
         sys.exit(1)
 
-def get_current_date():
-    """Return the current date in YYYY-MM-DD format."""
-    return datetime.datetime.now().strftime("%Y-%m-%d")
+def load_attestations():
+    """Load attestations from attestations.txt."""
+    try:
+        if not ATTESTATIONS_FILE.exists():
+            print(f"Error: Attestations file '{ATTESTATIONS_FILE}' not found.")
+            sys.exit(1)
+        with open(ATTESTATIONS_FILE, 'r') as f:
+            return [line.strip() for line in f if line.strip()]
+    except Exception as e:
+        print(f"Error loading attestations: {e}")
+        sys.exit(1)
 
 def get_nist_time():
     """Get the current time from NIST or fallback servers."""
@@ -66,7 +86,7 @@ def get_nist_time():
 def get_rss_headline(config):
     """Get the latest headline and link from the configured RSS feed."""
     try:
-        rss_config = config.get('rss', {})
+        rss_config = config['canary'].get('rss', {})
         rss_url = rss_config.get('url', 'https://www.democracynow.org/democracynow.rss')
         feed = feedparser.parse(rss_url)
         if feed.entries and len(feed.entries) > 0:
@@ -100,11 +120,12 @@ def get_bitcoin_latest_block():
         return None
 
 def collect_attestations(config):
-    """Prompt user for each attestation from config."""
+    """Prompt user for each attestation from attestations.txt."""
+    attestations = load_attestations()
     selected_attestations = []
     org = config['canary']['organization']
     print("\nPlease confirm each attestation separately:")
-    for i, attestation in enumerate(config['canary']['attestations'], 1):
+    for i, attestation in enumerate(attestations, 1):
         while True:
             response = input(f"Confirm: '{org} {attestation}' (y/n): ").lower()
             if response in ['y', 'n']:
@@ -121,7 +142,6 @@ def get_optional_note():
 
 def create_warrant_canary_message(config):
     """Create the warrant canary message with updated formatting."""
-    current_date = get_current_date()
     nist_time = get_nist_time()
     rss_data = get_rss_headline(config)
     bitcoin_block = get_bitcoin_latest_block()
@@ -129,7 +149,7 @@ def create_warrant_canary_message(config):
     if not all([nist_time, rss_data, bitcoin_block]):
         missing = []
         if not nist_time: missing.append("NIST time")
-        if not rss_data: missing.append(f"{config['rss'].get('name', 'RSS')} headline")
+        if not rss_data: missing.append(f"{config['canary']['rss'].get('name', 'RSS')} headline")
         if not bitcoin_block: missing.append("Bitcoin block data")
         print(f"Error: Could not fetch: {', '.join(missing)}")
         return None
@@ -145,9 +165,7 @@ def create_warrant_canary_message(config):
     org = config['canary']['organization']
     admin_name = config['canary'].get('admin_name', 'Admin')
     admin_title = config['canary'].get('admin_title', 'administrator')
-    rss_name = config['rss'].get('name', 'RSS Feed')
     
-    # No leading \n; GPG adds one blank line after Hash: SHA512
     message = f"{org} Warrant Canary · {nist_time}\n"
     message += f"I, {admin_name}, the {admin_title} of {org}, state this {datetime.datetime.now().strftime('%dth day of %B, %Y')}:\n"
     for i, attestation in enumerate(attestations, 1):
@@ -162,12 +180,12 @@ def create_warrant_canary_message(config):
     message += f"  BTC block:   #{bitcoin_block['height']}, {bitcoin_block['time']}\n"
     message += f"  Block hash:  {bitcoin_block['hash']}\n"
     
-    return message.rstrip() + "\n"  # Single newline before signature
+    return message.rstrip() + "\n"
 
 def sign_with_gpg(message, gpg_key_id):
-    """Sign the warrant canary message with GPG, ensuring no extra newline after signature header."""
+    """Sign the warrant canary message with GPG."""
     try:
-        with open(TEMP_MESSAGE_FILE, "w", newline='\n') as f:  # Unix line endings
+        with open(TEMP_MESSAGE_FILE, "w", newline='\n') as f:
             f.write(message)
         cmd = ["gpg", "--clearsign", "--default-key", gpg_key_id, TEMP_MESSAGE_FILE]
         subprocess.run(cmd, check=True)
@@ -175,13 +193,11 @@ def sign_with_gpg(message, gpg_key_id):
             signed_message = f.read()
         os.remove(TEMP_MESSAGE_FILE)
         os.remove(f"{TEMP_MESSAGE_FILE}.asc")
-        # Fix GPG's extra newline after -----BEGIN PGP SIGNATURE-----
         lines = signed_message.splitlines()
         signature_idx = next(i for i, line in enumerate(lines) if line == "-----BEGIN PGP SIGNATURE-----")
         if lines[signature_idx + 1] == "":
-            lines.pop(signature_idx + 1)  # Remove blank line
-        signed_message = "\n".join(lines)
-        return signed_message
+            lines.pop(signature_idx + 1)
+        return "\n".join(lines)
     except subprocess.CalledProcessError as e:
         print(f"GPG signing error: {e}")
         return None
@@ -202,13 +218,10 @@ def save_warrant_canary(signed_message):
 
 async def post_to_matrix(config, signed_message):
     """Post the signed warrant canary to Matrix room."""
-    if not config.get('matrix', {}).get('enabled', False):
-        print("Matrix posting is disabled in config")
-        return False
     try:
-        from nio import AsyncClient, LoginResponse
-        matrix = config['matrix']
-        client = AsyncClient(matrix['homeserver'], matrix['username'])
+        from nio import AsyncClient
+        matrix = config['canary']['credentials']
+        client = AsyncClient(config['base_url'], matrix['username'])
         await client.login(matrix['password'])
         
         full_message = (
@@ -227,7 +240,7 @@ async def post_to_matrix(config, signed_message):
                 f"<pre>{signed_message}</pre>"
             )
         }
-        await client.room_send(matrix['room_id'], "m.room.message", content)
+        await client.room_send(config['canary']['room'], "m.room.message", content)
         await client.logout()
         await client.close()
         print("Posted to Matrix successfully")
@@ -253,14 +266,17 @@ def main():
         print("Operation cancelled")
         sys.exit(0)
     
-    signed_message = sign_with_gpg(message, config['gpg']['key_id'])
+    signed_message = sign_with_gpg(message, config['canary']['gpg_key_id'])
     if not signed_message:
         print("Failed to sign message")
         sys.exit(1)
     
-    if save_warrant_canary(signed_message) and config.get('matrix', {}).get('enabled', False):
-        if input("Post to Matrix? (y/n): ").lower() == 'y':
-            asyncio.run(post_to_matrix(config, signed_message))
+    if not save_warrant_canary(signed_message):
+        print("Failed to save canary")
+        sys.exit(1)
+    
+    if input("Post to Matrix? (y/n): ").lower() == 'y':
+        asyncio.run(post_to_matrix(config, signed_message))
 
 if __name__ == "__main__":
     main()
diff --git a/sw1tch/routes/admin.py b/sw1tch/routes/admin.py
index b967258..761d4a3 100644
--- a/sw1tch/routes/admin.py
+++ b/sw1tch/routes/admin.py
@@ -1,16 +1,73 @@
-from fastapi import APIRouter, Form, Depends
-from fastapi.responses import JSONResponse
+from fastapi import APIRouter, Form, Depends, Request, HTTPException
+from fastapi.responses import HTMLResponse, JSONResponse
+from fastapi.templating import Jinja2Templates
 from datetime import datetime, timedelta
 import httpx
 import re
+import os
+import hashlib
 
-from sw1tch import config, logger, load_registrations, save_registrations, verify_admin_auth
+from sw1tch import BASE_DIR, config, logger, load_registrations, save_registrations, verify_admin_auth
 from sw1tch.utilities.matrix import get_matrix_users, deactivate_user
 
-router = APIRouter(prefix="/_admin", dependencies=[Depends(verify_admin_auth)])
+router = APIRouter(prefix="/_admin")
+templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates"))
+
+@router.get("/", response_class=HTMLResponse)
+async def admin_panel(request: Request, auth_token: str = Depends(verify_admin_auth)):
+    return templates.TemplateResponse("admin.html", {"request": request, "authenticated": True})
+
+@router.get("/login", response_class=HTMLResponse)
+async def admin_login_page(request: Request):
+    return templates.TemplateResponse("admin.html", {"request": request, "authenticated": False})
+
+@router.post("/login", response_class=HTMLResponse)
+async def admin_login(request: Request, password: str = Form(...)):
+    expected_password = config["matrix_admin"].get("password", "")
+    hashed_password = hashlib.sha256(password.encode()).hexdigest()
+    if hashed_password == hashlib.sha256(expected_password.encode()).hexdigest():
+        return HTMLResponse(
+            content=f"""
+            <html>
+                <head>
+                    <meta http-equiv="refresh" content="0;url=/_admin/?auth_token={hashed_password}">
+                </head>
+                <body>
+                    <p>Redirecting to admin panel...</p>
+                </body>
+            </html>
+            """
+        )
+    else:
+        return templates.TemplateResponse("admin.html", {"request": request, "authenticated": False, "error": "Invalid password"})
+
+@router.get("/view_unfulfilled", response_class=HTMLResponse)
+async def view_unfulfilled_registrations(request: Request, auth_token: str = Depends(verify_admin_auth)):
+    registrations = load_registrations()
+    unfulfilled = []
+    if registrations:
+        current_time = datetime.utcnow()
+        async with httpx.AsyncClient() as client:
+            for entry in registrations:
+                username = entry["requested_name"]
+                reg_date = datetime.fromisoformat(entry["datetime"])
+                age = current_time - reg_date
+                url = f"{config['base_url']}/_matrix/client/v3/register/available?username={username}"
+                try:
+                    response = await client.get(url, timeout=5)
+                    if response.status_code == 200 and response.json().get("available", False):
+                        unfulfilled.append({
+                            "username": username,
+                            "email": entry["email"],
+                            "registration_date": entry["datetime"],
+                            "age_hours": age.total_seconds() / 3600
+                        })
+                except httpx.RequestError as ex:
+                    logger.error(f"Error checking username {username}: {ex}")
+    return templates.TemplateResponse("unfulfilled_registrations.html", {"request": request, "registrations": unfulfilled})
 
 @router.post("/purge_unfulfilled_registrations", response_class=JSONResponse)
-async def purge_unfulfilled_registrations(min_age_hours: int = Form(default=24)):
+async def purge_unfulfilled_registrations(min_age_hours: int = Form(default=24), auth_token: str = Depends(verify_admin_auth)):
     registrations = load_registrations()
     if not registrations:
         return JSONResponse({"message": "No registrations found to clean up"})
@@ -62,8 +119,21 @@ async def purge_unfulfilled_registrations(min_age_hours: int = Form(default=24))
     logger.info(f"Cleanup complete: {result}")
     return JSONResponse(result)
 
+@router.get("/view_undocumented", response_class=HTMLResponse)
+async def view_undocumented_users(request: Request, auth_token: str = Depends(verify_admin_auth)):
+    registrations = load_registrations()
+    matrix_users = await get_matrix_users()
+    registered_usernames = {entry["requested_name"].lower() for entry in registrations}
+    homeserver = config["homeserver"].lower()
+    undocumented_users = [
+        user for user in matrix_users
+        if user.lower().startswith("@") and user[1:].lower().split(":", 1)[1] == homeserver
+        and user[1:].lower().split(":", 1)[0] not in registered_usernames
+    ]
+    return templates.TemplateResponse("undocumented_users.html", {"request": request, "users": undocumented_users})
+
 @router.post("/deactivate_undocumented_users", response_class=JSONResponse)
-async def deactivate_undocumented_users():
+async def deactivate_undocumented_users(auth_token: str = Depends(verify_admin_auth)):
     registrations = load_registrations()
     matrix_users = await get_matrix_users()
     registered_usernames = {entry["requested_name"].lower() for entry in registrations}
@@ -93,7 +163,7 @@ async def deactivate_undocumented_users():
     return JSONResponse(result)
 
 @router.post("/retroactively_document_users", response_class=JSONResponse)
-async def retroactively_document_users():
+async def retroactively_document_users(auth_token: str = Depends(verify_admin_auth)):
     registrations = load_registrations()
     matrix_users = await get_matrix_users()
     registered_usernames = {entry["requested_name"].lower() for entry in registrations}
diff --git a/sw1tch/routes/canary.py b/sw1tch/routes/canary.py
index 4e7eaff..b3b1f96 100644
--- a/sw1tch/routes/canary.py
+++ b/sw1tch/routes/canary.py
@@ -13,7 +13,7 @@ from urllib3.util.retry import Retry
 from sw1tch import BASE_DIR, config, logger, verify_admin_auth
 from sw1tch.utilities.matrix import AsyncClient
 
-router = APIRouter(prefix="/_admin/warrant_canary", dependencies=[Depends(verify_admin_auth)])
+router = APIRouter(prefix="/_admin/canary")
 templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates"))
 
 ATTESTATIONS_FILE = os.path.join(BASE_DIR, "config", "attestations.txt")
@@ -45,8 +45,8 @@ def get_nist_time():
                 return data["dateTime"] + " UTC"
             elif "utc_datetime" in data:
                 return data["utc_datetime"] + " UTC"
-        except requests.RequestException:
-            pass
+        except requests.RequestException as e:
+            logger.error(f"Failed to fetch NIST time from {url}: {e}")
     raise HTTPException(status_code=500, detail="Failed to fetch NIST time")
 
 def get_rss_headline():
@@ -60,18 +60,19 @@ def get_rss_headline():
 def get_bitcoin_latest_block():
     try:
         response = requests.get("https://blockchain.info/latestblock", timeout=10)
-        if response.status_code == 200:
-            data = response.json()
-            block_response = requests.get(f"https://blockchain.info/rawblock/{data['hash']}", timeout=10)
-            if block_response.status_code == 200:
-                block_data = block_response.json()
-                hash_str = data["hash"].lstrip("0") or "0"
-                return {
-                    "height": data["height"],
-                    "hash": hash_str,
-                    "time": datetime.datetime.fromtimestamp(block_data["time"]).strftime("%Y-%m-%d %H:%M:%S UTC")
-                }
-    except Exception:
+        response.raise_for_status()
+        data = response.json()
+        block_response = requests.get(f"https://blockchain.info/rawblock/{data['hash']}", timeout=10)
+        block_response.raise_for_status()
+        block_data = block_response.json()
+        hash_str = data["hash"].lstrip("0") or "0"
+        return {
+            "height": data["height"],
+            "hash": hash_str,
+            "time": datetime.datetime.fromtimestamp(block_data["time"]).strftime("%Y-%m-%d %H:%M:%S UTC")
+        }
+    except requests.RequestException as e:
+        logger.error(f"Failed to fetch Bitcoin block data: {e}")
         raise HTTPException(status_code=500, detail="Failed to fetch Bitcoin block data")
 
 def create_warrant_canary_message(attestations: List[str], note: str):
@@ -99,7 +100,7 @@ def sign_with_gpg(message: str, gpg_key_id: str, passphrase: str):
         with open(TEMP_CANARY_FILE, "w", newline='\n') as f:
             f.write(message)
         cmd = ["gpg", "--batch", "--yes", "--passphrase", passphrase, "--clearsign", "--default-key", gpg_key_id, TEMP_CANARY_FILE]
-        subprocess.run(cmd, check=True)
+        subprocess.run(cmd, check=True, capture_output=True, text=True)
         with open(f"{TEMP_CANARY_FILE}.asc", "r") as f:
             signed_message = f.read()
         os.remove(TEMP_CANARY_FILE)
@@ -110,7 +111,11 @@ def sign_with_gpg(message: str, gpg_key_id: str, passphrase: str):
             lines.pop(signature_idx + 1)
         return "\n".join(lines)
     except subprocess.CalledProcessError as e:
-        raise HTTPException(status_code=500, detail=f"GPG signing failed: {e}")
+        logger.error(f"GPG signing failed: {e.stderr}")
+        raise HTTPException(status_code=500, detail=f"GPG signing failed: {e.stderr}")
+    except Exception as e:
+        logger.error(f"Error during GPG signing: {e}")
+        raise HTTPException(status_code=500, detail=f"Error during GPG signing: {e}")
 
 async def post_to_matrix(signed_message: str):
     try:
@@ -132,36 +137,62 @@ async def post_to_matrix(signed_message: str):
                 f"<pre>{signed_message}</pre>"
             )
         }
-        await client.room_send(config['matrix_admin']['room'], "m.room.message", content)
+        await client.room_send(config['canary']['room'], "m.room.message", content)
         await client.logout()
         await client.close()
+        logger.info("Warrant canary posted to Matrix successfully")
         return True
     except Exception as e:
         logger.error(f"Error posting to Matrix: {e}")
         return False
 
 @router.get("/", response_class=HTMLResponse)
-async def warrant_canary_form(request: Request):
+async def warrant_canary_form(request: Request, auth_token: str = Depends(verify_admin_auth)):
     attestations = load_attestations()
-    return templates.TemplateResponse("canary_form.html", {
+    return templates.TemplateResponse("canary.html", {
         "request": request,
         "attestations": attestations,
         "organization": config["canary"]["organization"]
     })
 
 @router.post("/preview", response_class=HTMLResponse)
-async def warrant_canary_preview(request: Request, attestations: List[str] = Form(...), note: str = Form(default="")):
-    message = create_warrant_canary_message(attestations, note)
-    return templates.TemplateResponse("canary_preview.html", {"request": request, "message": message})
+async def warrant_canary_preview(
+    request: Request,
+    selected_attestations: List[str] = Form(...),
+    note: str = Form(default=""),
+    auth_token: str = Depends(verify_admin_auth)
+):
+    message = create_warrant_canary_message(selected_attestations, note)
+    return templates.TemplateResponse("canary_preview.html", {
+        "request": request,
+        "message": message,
+        "selected_attestations": selected_attestations,
+        "note": note
+    })
 
 @router.post("/sign", response_class=HTMLResponse)
-async def warrant_canary_sign(request: Request, message: str = Form(...), passphrase: str = Form(...)):
+async def warrant_canary_sign(
+    request: Request,
+    message: str = Form(...),
+    passphrase: str = Form(...),
+    auth_token: str = Depends(verify_admin_auth)
+):
     signed_message = sign_with_gpg(message, config["canary"]["gpg_key_id"], passphrase)
     with open(CANARY_OUTPUT_FILE, "w") as f:
         f.write(signed_message)
-    return templates.TemplateResponse("canary_success.html", {"request": request, "signed_message": signed_message})
+    logger.info(f"Warrant canary saved to {CANARY_OUTPUT_FILE}")
+    return templates.TemplateResponse("canary_success.html", {
+        "request": request,
+        "signed_message": signed_message
+    })
 
 @router.post("/post", response_class=JSONResponse)
-async def warrant_canary_post(signed_message: str = Form(...)):
+async def warrant_canary_post(
+    signed_message: str = Form(...),
+    auth_token: str = Depends(verify_admin_auth)
+):
     success = await post_to_matrix(signed_message)
-    return JSONResponse({"message": "Posted to Matrix" if success else "Failed to post to Matrix"})
+    return JSONResponse({
+        "message": "Posted to Matrix successfully" if success else "Failed to post to Matrix",
+        "success": success
+    })
diff --git a/sw1tch/static/styles.css b/sw1tch/static/styles.css
index 3f07797..fa6bd74 100644
--- a/sw1tch/static/styles.css
+++ b/sw1tch/static/styles.css
@@ -33,7 +33,7 @@ body {
     border-radius: 0.75rem;
     padding: 2rem;
     width: 100%;
-    max-width: 24rem;
+    max-width: 80%; /* Changed from 24rem to match results-container */
     box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
 }
 
@@ -135,4 +135,82 @@ button:hover, .button:hover {
 
 .message p {
     margin: 0.5rem 0;
-}
\ No newline at end of file
+}
+
+.list-container {
+    margin-top: 0; /* Remove top margin since it’s now in results-container */
+}
+
+.data-table {
+    width: 100%;
+    border-collapse: collapse;
+    margin-top: 1rem;
+}
+
+.data-table th,
+.data-table td {
+    padding: 0.75rem;
+    border: 1px solid var(--border-color);
+    text-align: left;
+    overflow-wrap: break-word; /* Ensure long text wraps */
+}
+
+.data-table th {
+    background-color: var(--input-bg);
+    font-weight: 500;
+}
+
+.data-table tr:nth-child(even) {
+    background-color: var(--card-bg);
+}
+
+.data-table tr:hover {
+    background-color: var(--input-bg);
+}
+
+/* Add to the end of sw1tch/static/styles.css */
+
+fieldset {
+    border: 1px solid var(--border-color);
+    padding: 1rem;
+    margin-bottom: 1rem;
+    border-radius: 0.375rem;
+}
+
+legend {
+    padding: 0 0.5rem;
+    color: var(--text-secondary);
+}
+
+.checkbox-label {
+    display: block;
+    margin: 0.5rem 0;
+    color: var(--text-primary);
+}
+
+textarea {
+    width: 100%;
+    padding: 0.5rem 1rem;
+    background-color: var(--input-bg);
+    border: 1px solid var(--border-color);
+    border-radius: 0.375rem;
+    color: var(--text-primary);
+    font-size: 1rem;
+    transition: border-color 0.15s ease;
+    resize: vertical;
+}
+
+textarea:focus {
+    outline: none;
+    border-color: var(--accent);
+}
+
+.canary-preview {
+    background-color: var(--input-bg);
+    padding: 1rem;
+    border-radius: 0.375rem;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    margin: 1rem 0;
+    font-family: monospace;
+}
diff --git a/sw1tch/templates/admin.html b/sw1tch/templates/admin.html
new file mode 100644
index 0000000..e8fdf72
--- /dev/null
+++ b/sw1tch/templates/admin.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Admin Panel</title>
+    <link rel="stylesheet" href="/static/styles.css">
+    <style>
+        .grid-container {
+            display: grid;
+            grid-template-columns: 1fr 1fr;
+            gap: 1rem;
+            margin-bottom: 2rem;
+        }
+        .admin-button {
+            padding: 1rem;
+            font-size: 1rem;
+        }
+        .login-container {
+            display: flex;
+            flex-direction: column;
+            gap: 1rem;
+        }
+        .results-container {
+            width: 80%;
+            margin: 0 auto 2rem auto;
+            max-height: none; /* Allow height to adjust to content */
+        }
+    </style>
+</head>
+<body>
+    <div class="card">
+        <div class="logo-container">
+            <img src="/static/logo.png" alt="Logo" class="logo">
+        </div>
+
+        {% if not authenticated %}
+        <div class="login-container">
+            <form id="login-form" method="post" action="/_admin/login">
+                <input type="password" name="password" placeholder="Admin Password" required>
+                <button type="submit">Login</button>
+            </form>
+            {% if error %}
+            <p class="message" style="color: var(--error-color);">{{ error }}</p>
+            {% endif %}
+        </div>
+        {% else %}
+        <div class="grid-container">
+            <button onclick="viewUnfulfilled('{{ request.query_params.auth_token }}')" class="admin-button">View Unfulfilled Registrations</button>
+            <button onclick="viewUndocumented('{{ request.query_params.auth_token }}')" class="admin-button">View Undocumented Users</button>
+        </div>
+        <div id="results" class="results-container"></div>
+        <div class="grid-container">
+            <button onclick="purgeUnfulfilled('{{ request.query_params.auth_token }}')" class="admin-button">Purge Unfulfilled Registrations</button>
+            <button onclick="deactivateUndocumented('{{ request.query_params.auth_token }}')" class="admin-button">Deactivate Undocumented Users</button>
+        </div>
+        <p class="info-text">Logged in as admin</p>
+        {% endif %}
+    </div>
+
+    <script>
+        async function viewUnfulfilled(auth_token) {
+            const response = await fetch(`/_admin/view_unfulfilled?auth_token=${auth_token}`);
+            const html = await response.text();
+            document.getElementById('results').innerHTML = html;
+        }
+
+        async function purgeUnfulfilled(auth_token) {
+            if (confirm("Are you sure you want to purge unfulfilled registrations?")) {
+                const response = await fetch(`/_admin/purge_unfulfilled_registrations?auth_token=${auth_token}`, {
+                    method: "POST",
+                    headers: { "Content-Type": "application/x-www-form-urlencoded" },
+                    body: "min_age_hours=24"
+                });
+                const result = await response.json();
+                alert(`Cleanup complete:\nKept existing: ${result.kept_existing}\nKept recent: ${result.kept_recent}\nRemoved: ${result.removed}`);
+                viewUnfulfilled(auth_token); // Refresh the list
+            }
+        }
+
+        async function viewUndocumented(auth_token) {
+            const response = await fetch(`/_admin/view_undocumented?auth_token=${auth_token}`);
+            const html = await response.text();
+            document.getElementById('results').innerHTML = html;
+        }
+
+        async function deactivateUndocumented(auth_token) {
+            if (confirm("Are you sure you want to deactivate undocumented users?")) {
+                const response = await fetch(`/_admin/deactivate_undocumented_users?auth_token=${auth_token}`, {
+                    method: "POST"
+                });
+                const result = await response.json();
+                alert(`${result.message}\nFailed deactivations: ${result.failed_deactivations || 'None'}`);
+                viewUndocumented(auth_token); // Refresh the list
+            }
+        }
+    </script>
+</body>
+</html>
diff --git a/sw1tch/templates/canary.html b/sw1tch/templates/canary.html
new file mode 100644
index 0000000..0151284
--- /dev/null
+++ b/sw1tch/templates/canary.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Warrant Canary</title>
+    <link rel="stylesheet" href="/static/styles.css">
+</head>
+<body>
+    <div class="card">
+        <div class="logo-container">
+            <img src="/static/logo.png" alt="Logo" class="logo">
+        </div>
+        <h2>Create Warrant Canary</h2>
+        <form method="post" action="/_admin/canary/preview?auth_token={{ request.query_params.auth_token }}">
+            <fieldset>
+                <legend>Select Attestations for {{ organization }}</legend>
+                {% for attestation in attestations %}
+                <label class="checkbox-label">
+                    <input type="checkbox" name="selected_attestations" value="{{ attestation }}">
+                    {{ organization }} {{ attestation }}
+                </label>
+                {% endfor %}
+            </fieldset>
+            <label for="note">Optional Note:</label>
+            <textarea id="note" name="note" rows="3" placeholder="Add an optional note"></textarea>
+            <button type="submit">Preview Canary</button>
+        </form>
+        <p class="info-text"><a href="/_admin/?auth_token={{ request.query_params.auth_token }}">Back to Admin Panel</a></p>
+    </div>
+</body>
+</html>
diff --git a/sw1tch/templates/canary_preview.html b/sw1tch/templates/canary_preview.html
index 361091a..454f226 100644
--- a/sw1tch/templates/canary_preview.html
+++ b/sw1tch/templates/canary_preview.html
@@ -1,16 +1,25 @@
 <!DOCTYPE html>
-<html>
+<html lang="en">
 <head>
-    <title>Warrant Canary Preview</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Preview Warrant Canary</title>
+    <link rel="stylesheet" href="/static/styles.css">
 </head>
 <body>
-    <h1>Warrant Canary Preview</h1>
-    <pre>{{ message }}</pre>
-    <form method="post" action="/_admin/warrant_canary/sign">
-        <input type="hidden" name="message" value="{{ message|escape }}">
-        <input type="password" name="passphrase" placeholder="GPG Passphrase" required>
-        <input type="hidden" name="auth_token" value="{{ request.query_params.auth_token }}">
-        <button type="submit">Sign</button>
-    </form>
+    <div class="card">
+        <div class="logo-container">
+            <img src="/static/logo.png" alt="Logo" class="logo">
+        </div>
+        <h2>Preview Warrant Canary</h2>
+        <pre class="canary-preview">{{ message }}</pre>
+        <form method="post" action="/_admin/canary/sign?auth_token={{ request.query_params.auth_token }}">
+            <input type="hidden" name="message" value="{{ message | escape }}">
+            <label for="passphrase">GPG Passphrase:</label>
+            <input type="password" id="passphrase" name="passphrase" required>
+            <button type="submit">Sign and Save</button>
+        </form>
+        <p class="info-text"><a href="/_admin/canary/?auth_token={{ request.query_params.auth_token }}">Back to Form</a></p>
+    </div>
 </body>
 </html>
diff --git a/sw1tch/templates/canary_success.html b/sw1tch/templates/canary_success.html
index 60e614f..fb3e6ca 100644
--- a/sw1tch/templates/canary_success.html
+++ b/sw1tch/templates/canary_success.html
@@ -1,15 +1,33 @@
 <!DOCTYPE html>
-<html>
+<html lang="en">
 <head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Warrant Canary Signed</title>
+    <link rel="stylesheet" href="/static/styles.css">
 </head>
 <body>
-    <h1>Warrant Canary Signed</h1>
-    <pre>{{ signed_message }}</pre>
-    <form method="post" action="/_admin/warrant_canary/post">
-        <input type="hidden" name="signed_message" value="{{ signed_message|escape }}">
-        <input type="hidden" name="auth_token" value="{{ request.query_params.auth_token }}">
-        <button type="submit">Post to Matrix</button>
-    </form>
+    <div class="card">
+        <div class="logo-container">
+            <img src="/static/logo.png" alt="Logo" class="logo">
+        </div>
+        <h2>Warrant Canary Signed</h2>
+        <pre class="canary-preview">{{ signed_message }}</pre>
+        <button onclick="postToMatrix('{{ signed_message | escape }}', '{{ request.query_params.auth_token }}')">Post to Matrix</button>
+        <p id="post-result" class="message"></p>
+        <p class="info-text"><a href="/_admin/?auth_token={{ request.query_params.auth_token }}">Back to Admin Panel</a></p>
+    </div>
+    <script>
+        async function postToMatrix(signed_message, auth_token) {
+            const response = await fetch(`/_admin/canary/post?auth_token=${auth_token}`, {
+                method: "POST",
+                headers: { "Content-Type": "application/x-www-form-urlencoded" },
+                body: `signed_message=${encodeURIComponent(signed_message)}`
+            });
+            const result = await response.json();
+            document.getElementById("post-result").textContent = result.message;
+            document.getElementById("post-result").style.color = result.success ? "var(--success-color)" : "var(--error-color)";
+        }
+    </script>
 </body>
 </html>
diff --git a/sw1tch/templates/undocumented_users.html b/sw1tch/templates/undocumented_users.html
new file mode 100644
index 0000000..ca598af
--- /dev/null
+++ b/sw1tch/templates/undocumented_users.html
@@ -0,0 +1,21 @@
+<div class="list-container">
+    <h2>Undocumented Users</h2>
+    {% if users %}
+    <table class="data-table">
+        <thead>
+            <tr>
+                <th>User ID</th>
+            </tr>
+        </thead>
+        <tbody>
+            {% for user in users %}
+            <tr>
+                <td>{{ user }}</td>
+            </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+    {% else %}
+    <p class="message">No undocumented users found.</p>
+    {% endif %}
+</div>
diff --git a/sw1tch/templates/unfulfilled_registrations.html b/sw1tch/templates/unfulfilled_registrations.html
new file mode 100644
index 0000000..9bac1f6
--- /dev/null
+++ b/sw1tch/templates/unfulfilled_registrations.html
@@ -0,0 +1,27 @@
+<div class="list-container">
+    <h2>Unfulfilled Registrations</h2>
+    {% if registrations %}
+    <table class="data-table">
+        <thead>
+            <tr>
+                <th>Username</th>
+                <th>Email</th>
+                <th>Registration Date</th>
+                <th>Age (Hours)</th>
+            </tr>
+        </thead>
+        <tbody>
+            {% for reg in registrations %}
+            <tr>
+                <td>{{ reg.username }}</td>
+                <td>{{ reg.email }}</td>
+                <td>{{ reg.registration_date }}</td>
+                <td>{{ reg.age_hours | round(1) }}</td>
+            </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+    {% else %}
+    <p class="message">No unfulfilled registrations found.</p>
+    {% endif %}
+</div>