Merge pull request 'feat: redesign site as personal page with Catppuccin theme' (#6) from feat/personal-site-redesign into main
AI Codebase Quality Review / ai-codebase-review (push) Successful in 20s

Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
2026-01-03 12:05:00 +00:00
3 changed files with 349 additions and 255 deletions
+14 -12
View File
@@ -52,7 +52,7 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
name="keywords" name="keywords"
content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den" content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den"
/> />
<meta name="theme-color" content="#d4a574" /> <meta name="theme-color" content="#cba6f7" />
<meta name="color-scheme" content="dark" /> <meta name="color-scheme" content="dark" />
<!-- Favicons --> <!-- Favicons -->
@@ -86,14 +86,18 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
<style is:global> <style is:global>
:root { :root {
/* Cozy Den Color Palette */ /* Catppuccin Mocha Color Palette */
--color-bg: #1a1410; --color-bg: #1e1e2e;
--color-bg-light: #2a1f18; --color-bg-light: #313244;
--color-text: #f4e9d8; --color-surface: #45475a;
--color-text-dim: #c4b5a0; --color-text: #cdd6f4;
--color-accent: #d4a574; --color-text-dim: #a6adc8;
--color-accent-bright: #e8bf8e; --color-accent: #cba6f7;
--color-warm: #8b6f47; --color-accent-bright: #f5c2e7;
--color-warm: #f38ba8;
--color-blue: #89b4fa;
--color-green: #a6e3a1;
--color-peach: #fab387;
/* Spacing */ /* Spacing */
--space-xs: 0.5rem; --space-xs: 0.5rem;
@@ -103,9 +107,7 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
--space-xl: 3rem; --space-xl: 3rem;
/* Typography */ /* Typography */
--font-body: --font-body: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
sans-serif;
} }
* { * {
+68 -92
View File
@@ -4,30 +4,29 @@ import BaseLayout from "../layouts/BaseLayout.astro";
<BaseLayout <BaseLayout
title="404 - Page Not Found | Hidden Den Cafe" title="404 - Page Not Found | Hidden Den Cafe"
description="This page doesn't exist in the Hidden Den. Let's get you back to somewhere cozy." description="This page doesn't exist in the Hidden Den."
> >
<div class="matrix-bg" aria-hidden="true"></div>
<main class="not-found-page"> <main class="not-found-page">
<div class="container"> <div class="container">
<div class="card fade-in"> <div class="content fade-in">
<div class="not-found-content"> <h1 class="error-code">404</h1>
<h1 class="error-code">404</h1> <div class="divider">══════════════════════════════</div>
<h2 class="error-title">Lost in the Den?</h2> <h2 class="error-title">Lost in the Den?</h2>
<p class="error-message"> <p class="error-message">
Oops! This cozy corner doesn't seem to exist. Maybe it's This corner doesn't exist yet.
still being built, or perhaps you've wandered into a </p>
part of the den that hasn't been opened yet. <div class="actions">
</p> <a href="/" class="btn">[Back to Home]</a>
<div class="actions"> <a
<a href="/" class="btn-primary"> Back to Home </a> href="https://git.hiddenden.cafe"
<a class="btn"
href="https://git.hiddenden.cafe" target="_blank"
class="btn-secondary" rel="noopener noreferrer"
target="_blank" >
rel="noopener noreferrer" [Gitea]
> </a>
Visit Gitea
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -35,6 +34,26 @@ import BaseLayout from "../layouts/BaseLayout.astro";
</BaseLayout> </BaseLayout>
<style> <style>
.matrix-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.03;
background:
linear-gradient(var(--color-accent) 1px, transparent 1px),
linear-gradient(90deg, var(--color-accent) 1px, transparent 1px);
background-size: 50px 50px;
animation: grid-move 20s linear infinite;
}
@keyframes grid-move {
0% { transform: translate(0, 0); }
100% { transform: translate(50px, 50px); }
}
.not-found-page { .not-found-page {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -44,45 +63,40 @@ import BaseLayout from "../layouts/BaseLayout.astro";
} }
.container { .container {
max-width: 600px; max-width: 500px;
width: 100%; width: 100%;
} background: rgba(30, 30, 46, 0.8);
backdrop-filter: blur(10px);
.card { border: 1px solid var(--color-surface);
background: var(--color-bg-light); border-radius: 8px;
border-radius: 12px;
padding: var(--space-xl); padding: var(--space-xl);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(212, 165, 116, 0.1);
text-align: center; text-align: center;
} }
.not-found-content {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.error-code { .error-code {
font-size: 6rem; font-size: 5rem;
font-weight: bold; font-weight: bold;
color: var(--color-accent); color: var(--color-warm);
line-height: 1; line-height: 1;
margin: 0; margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); }
.divider {
color: var(--color-surface);
font-size: 0.75rem;
margin: var(--space-md) 0;
user-select: none;
} }
.error-title { .error-title {
font-size: 2rem; font-size: 1.25rem;
margin: 0; margin: 0 0 var(--space-sm) 0;
color: var(--color-accent-bright); color: var(--color-accent);
} }
.error-message { .error-message {
font-size: 1.1rem;
color: var(--color-text-dim); color: var(--color-text-dim);
line-height: 1.6; margin: 0 0 var(--space-lg) 0;
margin: 0;
} }
.actions { .actions {
@@ -90,65 +104,27 @@ import BaseLayout from "../layouts/BaseLayout.astro";
gap: var(--space-md); gap: var(--space-md);
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: var(--space-md);
} }
.btn-primary, .btn {
.btn-secondary { color: var(--color-blue);
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-lg);
border-radius: 8px;
font-weight: 500;
transition: all 0.2s ease;
text-decoration: none; text-decoration: none;
transition: color 0.2s ease;
} }
.btn-primary { .btn:hover {
background: var(--color-accent);
color: var(--color-bg);
}
.btn-primary:hover {
background: var(--color-accent-bright);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(212, 165, 116, 0.3);
}
.btn-secondary {
background: transparent;
color: var(--color-accent-bright); color: var(--color-accent-bright);
border: 2px solid var(--color-accent);
} }
.btn-secondary:hover { @media (max-width: 600px) {
background: rgba(212, 165, 116, 0.1);
border-color: var(--color-accent-bright);
transform: translateY(-2px);
}
@media (max-width: 768px) {
.error-code { .error-code {
font-size: 4rem; font-size: 3rem;
} }
}
.error-title { @media (prefers-reduced-motion: reduce) {
font-size: 1.5rem; .matrix-bg {
} animation: none;
.error-message {
font-size: 1rem;
}
.actions {
flex-direction: column;
}
.btn-primary,
.btn-secondary {
width: 100%;
justify-content: center;
} }
} }
</style> </style>
+267 -151
View File
@@ -1,203 +1,319 @@
--- ---
import BaseLayout from "../layouts/BaseLayout.astro"; import BaseLayout from "../layouts/BaseLayout.astro";
import Card from "../components/Card.astro";
import Section from "../components/Section.astro";
import ServiceItem from "../components/ServiceItem.astro";
--- ---
<BaseLayout <BaseLayout
title="Hidden Den Cafe - Cozy Self-Hosted Services" title="Hidden Den Cafe"
description="Welcome to Hidden Den Cafe - a warm, self-hosted space where technology meets comfort. Privacy-focused, furry-friendly, and built with love by Latte." description="Latte's cozy corner of the internet. Gay furry wizard, IT enthusiast, and self-hosting advocate."
> >
<main id="main-content"> <div class="matrix-bg" aria-hidden="true"></div>
<!-- Hero Section -->
<section class="hero" aria-labelledby="hero-title"> <main class="main">
<div class="container"> <div class="container">
<div class="hero-content fade-in"> <!-- Header -->
<h1 id="hero-title" class="hero-title">Hidden Den Cafe</h1> <header class="header fade-in">
<p class="hero-subtitle">A cozy corner of the internet</p> <h1 class="title">Hidden Den Cafe</h1>
<div class="divider">══════════════════════════════</div>
</header>
<!-- About -->
<section class="section fade-in">
<p class="intro">
Hey, I'm <span class="highlight">Latte</span> (he/him).
</p>
<p class="tagline">gay furry wizard</p>
<p class="desc">
IT wizard with a homelab. I believe in self-hosting, privacy,
and keeping control of my own data. Companies don't get to sell
or misuse what's mine.
</p>
</section>
<div class="divider">══════════════════════════════</div>
<!-- Games -->
<section class="section fade-in">
<h2>Games</h2>
<p class="games">
Minecraft, No Man's Sky, Warframe, Cyberpunk 2077
</p>
</section>
<div class="divider">══════════════════════════════</div>
<!-- Projects -->
<section class="section fade-in">
<h2>Projects</h2>
<div class="project">
<h3><a href="https://git.hiddenden.cafe/Hiddenden/openrabbit" target="_blank" rel="noopener noreferrer">OpenRabbit</a></h3>
<p>AI code review system for Gitea with automated PR review, issue triage, interactive chat, and codebase analysis.</p>
</div> </div>
</div> <p class="note">More projects coming soon...</p>
</section> </section>
<!-- About Hidden Den Section --> <div class="divider">══════════════════════════════</div>
<Section class="about-den" ariaLabelledby="about-den-heading">
<Card>
<h2 id="about-den-heading">About Hidden Den</h2>
<p>
Welcome to Hidden Den Cafe - a warm, self-hosted space where
technology meets comfort. This is a personal corner of the
internet built with love, care, and a strong belief in
privacy and open-source values.
</p>
<p>
Everything here runs on self-hosted infrastructure, giving
complete control over data and services. No cloud
dependencies, no tracking, just a cozy digital home.
</p>
</Card>
</Section>
<!-- About Latte Section --> <!-- Links -->
<Section class="about-me" ariaLabelledby="about-me-heading"> <section class="section fade-in">
<Card> <h2>Links</h2>
<h2 id="about-me-heading">About Me</h2> <ul class="links">
<p> <li><a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer">Gitea</a> — Self-hosted git server</li>
Hey there! I'm Latte, a gay furry developer who loves </ul>
building things and being part of the warm, welcoming furry </section>
community. I'm passionate about self-hosting, open-source
software, and creating cozy spaces both online and off.
</p>
<p>
I work primarily with Python and Flask, though I'm always
learning new things (currently exploring JavaScript and the
C stack). When I get those surges of creative energy, I love
diving into new projects - from Reddit downloaders to
Telegram sticker tools, and everything in between.
</p>
<p>Coffee enthusiast • Linux lover • Self-hosting advocate</p>
</Card>
</Section>
<!-- Services Section --> <div class="divider">══════════════════════════════</div>
<Section class="services" ariaLabelledby="services-heading">
<Card>
<h2 id="services-heading">Services</h2>
<p>Here are the services currently running in the den:</p>
<div class="service-list" role="list"> <!-- Donate -->
<ServiceItem <section class="section fade-in">
title="Gitea" <h2>Support</h2>
description="Self-hosted Git service for all my projects and code repositories." <div class="donate">
url="https://git.hiddenden.cafe" <div class="crypto">
/> <span class="crypto-label">[XMR]</span>
<ServiceItem <code class="crypto-addr">41uiUeBru8jhtzjQz3M5CKV1uFpern7juStdfveNQS52LQ9aw3mNkdbc8akM81YnxuE2RT9K2Cmyp9cfyi1osrbVBjBbzQ3</code>
title="More Coming Soon" </div>
description="The den is always growing! More services will be added as they're developed and deployed." <div class="crypto">
comingSoon={true} <span class="crypto-label">[ETH]</span>
/> <code class="crypto-addr">0x3Dfc92458267b91BFa6bF8f6c86bAE809Ab76Cb4</code>
</div>
</div> </div>
</Card> </section>
</Section>
<!-- Footer --> <!-- Footer -->
<footer class="footer" role="contentinfo"> <footer class="footer fade-in">
<div class="container"> <div class="divider">══════════════════════════════</div>
<p>Made with love by Latte</p> <p>Made with love by Latte</p>
<nav aria-label="Footer navigation"> </footer>
<p class="footer-links"> </div>
<a
href="https://git.hiddenden.cafe"
target="_blank"
rel="noopener noreferrer">Gitea</a
>
</p>
</nav>
</div>
</footer>
</main> </main>
<!-- Scroll Animation Script -->
<script>
// Intersection Observer for scroll animations
const observerOptions = {
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
// Optionally unobserve after animation
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe all elements with scroll-animate class
document.addEventListener("DOMContentLoaded", () => {
const animateElements =
document.querySelectorAll(".scroll-animate");
animateElements.forEach((el) => observer.observe(el));
});
</script>
</BaseLayout> </BaseLayout>
<style> <style>
main { /* Matrix-style animated background */
display: flex; .matrix-bg {
flex-direction: column; position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.03;
background:
linear-gradient(var(--color-accent) 1px, transparent 1px),
linear-gradient(90deg, var(--color-accent) 1px, transparent 1px);
background-size: 50px 50px;
animation: grid-move 20s linear infinite;
}
@keyframes grid-move {
0% { transform: translate(0, 0); }
100% { transform: translate(50px, 50px); }
}
.main {
min-height: 100vh; min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-lg) var(--space-md);
} }
.container { .container {
max-width: 800px; max-width: 700px;
margin: 0 auto;
padding: 0 var(--space-md);
width: 100%; width: 100%;
background: rgba(30, 30, 46, 0.8);
backdrop-filter: blur(10px);
border: 1px solid var(--color-surface);
border-radius: 8px;
padding: var(--space-xl);
} }
/* Hero Section */ .header {
.hero {
padding: var(--space-xl) 0;
text-align: center; text-align: center;
background: linear-gradient( margin-bottom: var(--space-lg);
135deg,
var(--color-bg) 0%,
var(--color-bg-light) 100%
);
} }
.hero-title { .title {
font-size: 3rem; font-size: 2rem;
font-weight: 700;
letter-spacing: 2px;
}
.divider {
color: var(--color-surface);
text-align: center;
font-size: 0.75rem;
margin: var(--space-md) 0;
user-select: none;
}
.section {
margin: var(--space-md) 0;
}
.section h2 {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: var(--space-sm); margin-bottom: var(--space-sm);
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); color: var(--color-accent);
} }
.hero-subtitle { .intro {
font-size: 1.25rem; font-size: 1.1rem;
margin-bottom: var(--space-xs);
}
.highlight {
color: var(--color-accent-bright);
font-weight: 700;
}
.tagline {
color: var(--color-accent);
font-style: italic;
margin-bottom: var(--space-sm);
}
.desc {
color: var(--color-text-dim);
line-height: 1.7;
}
.games {
color: var(--color-text-dim); color: var(--color-text-dim);
} }
/* Service List */ .project {
.service-list { margin-bottom: var(--space-sm);
margin-top: var(--space-md); }
display: flex;
flex-direction: column; .project h3 {
gap: var(--space-md); font-size: 1rem;
margin-bottom: var(--space-xs);
}
.project h3 a {
color: var(--color-blue);
}
.project h3 a:hover {
color: var(--color-accent-bright);
}
.project p {
color: var(--color-text-dim);
font-size: 0.9rem;
}
.note {
color: var(--color-surface);
font-style: italic;
font-size: 0.85rem;
}
.links {
list-style: none;
}
.links li {
margin-bottom: var(--space-xs);
color: var(--color-text-dim);
}
.links a {
color: var(--color-blue);
}
.links a:hover {
color: var(--color-accent-bright);
}
.donate {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.crypto {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: var(--space-xs);
}
.crypto-label {
color: var(--color-peach);
font-weight: 700;
font-size: 0.85rem;
}
.crypto-addr {
font-size: 0.7rem;
color: var(--color-text-dim);
word-break: break-all;
background: var(--color-bg);
padding: 2px 6px;
border-radius: 4px;
} }
/* Footer */
.footer { .footer {
margin-top: auto; margin-top: var(--space-lg);
padding: var(--space-lg) 0;
text-align: center; text-align: center;
border-top: 1px solid rgba(212, 165, 116, 0.2);
background: var(--color-bg-light);
} }
.footer p { .footer p {
color: var(--color-text-dim); color: var(--color-text-dim);
margin-bottom: var(--space-xs); font-size: 0.85rem;
} }
.footer-links { /* Animations */
display: flex; .fade-in {
justify-content: center; animation: fadeIn 0.6s ease-out forwards;
align-items: center; opacity: 0;
gap: var(--space-sm); }
.fade-in:nth-child(1) { animation-delay: 0.1s; }
.fade-in:nth-child(2) { animation-delay: 0.2s; }
.fade-in:nth-child(3) { animation-delay: 0.3s; }
.fade-in:nth-child(4) { animation-delay: 0.4s; }
.fade-in:nth-child(5) { animation-delay: 0.5s; }
.fade-in:nth-child(6) { animation-delay: 0.6s; }
.fade-in:nth-child(7) { animation-delay: 0.7s; }
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 600px) {
.hero-title { .container {
font-size: 2rem; padding: var(--space-md);
} }
.hero-subtitle { .title {
font-size: 1rem; font-size: 1.5rem;
}
.divider {
font-size: 0.6rem;
}
}
@media (prefers-reduced-motion: reduce) {
.matrix-bg {
animation: none;
}
.fade-in {
animation: none;
opacity: 1;
} }
} }
</style> </style>