From 79271aa3bf88ac849c2490c186ae80dee5b3767b Mon Sep 17 00:00:00 2001 From: Latte Date: Fri, 6 Mar 2026 21:00:06 +0100 Subject: [PATCH 01/23] Add 'Now' page and update navigation; refine styles and metadata --- AGENTS.md | 169 ++++++++++++++++++++++++++++++++ src/components/Nav.astro | 3 +- src/content/blog/hello-world.md | 4 +- src/data/now.md | 25 +++++ src/layouts/BaseLayout.astro | 36 ++++++- src/pages/404.astro | 2 +- src/pages/about.astro | 2 +- src/pages/blog/[...slug].astro | 2 +- src/pages/blog/index.astro | 2 +- src/pages/index.astro | 2 +- src/pages/now.astro | 152 ++++++++++++++++++++++++++++ src/pages/projects.astro | 2 +- 12 files changed, 390 insertions(+), 11 deletions(-) create mode 100644 AGENTS.md create mode 100644 src/data/now.md create mode 100644 src/pages/now.astro diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6a8ec35 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,169 @@ +# AGENTS.md + +This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. + +## Project Quick Reference + +**Project:** Cozy Den - Personal landing page for hiddenden.cafe +**Owner:** Latte (gay furry developer, values self-hosting and privacy) +**Tech Stack:** Astro 4.x, TypeScript, Vanilla CSS, Docker + Nginx +**Aesthetic:** Warm coffee/cappuccino theme, cozy hidden den vibes +**Deployment:** Docker containers pushed to Gitea registry at git.hiddenden.cafe + +## Core Design Principles + +1. **Cozy Aesthetic** - Warm colors, coffee/cappuccino theme, hidden den vibes +2. **Self-Hosted** - Everything runs on personal infrastructure (homelab/VPS) +3. **Privacy First** - No tracking, no external dependencies +4. **Lightweight** - Static HTML/CSS, minimal JavaScript +5. **Docker-Ready** - Easy deployment via containers + +## File Structure + +``` +cozy-den/ +├── src/ +│ ├── layouts/ +│ │ └── BaseLayout.astro # Base layout + global styles +│ └── pages/ +│ ├── index.astro # Main landing page +│ └── 404.astro # Custom 404 page +├── public/ +│ ├── favicon.svg # Coffee emoji favicon +│ └── robots.txt # Search engine directives +├── astro.config.mjs # Astro config with sitemap +├── package.json # Dependencies (Astro 4.x, @astrojs/sitemap) +├── Dockerfile # Multi-stage: Node builder + Nginx +├── docker-compose.yml # Local container orchestration +└── nginx.conf # Production web server config +``` + +## Architecture Notes + +This is a simple static site following standard Astro conventions: +- Layouts in `src/layouts/` for reusable page templates +- Pages in `src/pages/` (routes automatically based on filename) +- All content is on a single page (`index.astro`) with multiple sections +- Custom 404 page with cozy theming +- No client-side JavaScript - pure static HTML/CSS output +- CSS custom properties centralized in `BaseLayout.astro` for theming +- Accessibility improvements with ARIA labels and semantic HTML + +## Commands + +```bash +# Development +npm install # Install dependencies +npm run dev # Start dev server at http://localhost:4321 +npm run build # Build for production (runs astro check + astro build) +npm run preview # Preview production build + +# Docker +docker build -t cozy-den . +docker run -d -p 3000:80 --name cozy-den cozy-den +docker-compose up -d + +# Deployment to Gitea registry +docker tag cozy-den git.hiddenden.cafe/mats/cozy-den:latest +docker login git.hiddenden.cafe +docker push git.hiddenden.cafe/mats/cozy-den:latest +``` + +## Color System + +All colors use CSS custom properties in `BaseLayout.astro`: + +```css +--color-bg: #1a1410 /* Dark background (deep coffee) */ +--color-bg-light: #2a1f18 /* Lighter background for cards */ +--color-text: #f4e9d8 /* Cream text */ +--color-text-dim: #c4b5a0 /* Dimmed text */ +--color-accent: #d4a574 /* Warm accent (coffee with cream) */ +--color-accent-bright: #e8bf8e /* Brighter accent for highlights */ +--color-warm: #8b6f47 /* Warm brown for borders/accents */ +``` + +**To change theme:** Edit these variables. All components update automatically. + +## Common Modification Patterns + +### Adding a Section +```astro +
+
+
+

Section Title

+

Content

+
+
+
+``` + +### Adding a Service +```astro +
+

🔧 Service Name

+

Description of the service

+
+``` + +### Adding a New Page +Create new `.astro` file in `src/pages/`: +```astro +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +--- + + +
+

New Page

+
+
+``` +Note: Pages route based on filename (e.g., `about.astro` → `/about`) + +## Implementation Guidelines + +**DO:** +- Maintain cozy, warm aesthetic (coffee/cappuccino theme) +- Keep site lightweight - static HTML/CSS only, no JavaScript runtime +- Use CSS custom properties for all colors (defined in `src/layouts/BaseLayout.astro`) +- Use `.fade-in` class for animations, `.card` class for consistent card styling +- Test production builds and Docker builds after changes +- Ensure responsive design works on mobile +- Follow standard Astro structure (layouts in `src/layouts/`, pages in `src/pages/`) + +**DON'T:** +- Add tracking or external dependencies (privacy-first approach) +- Add client-side JavaScript unless absolutely necessary +- Break the coffee/warm color theme +- Create sterile or corporate design elements + +## Astro-Specific Notes + +- Frontmatter (code between `---`) runs at build time only +- ` diff --git a/src/pages/projects.astro b/src/pages/projects.astro index 5f5d610..e291d88 100644 --- a/src/pages/projects.astro +++ b/src/pages/projects.astro @@ -126,7 +126,7 @@ function getPrimaryLink(project: Project): string | undefined { .container { max-width: 700px; width: 100%; - background: rgba(30, 30, 46, 0.8); + background: var(--color-glass); backdrop-filter: blur(10px); border: 1px solid var(--color-surface); border-radius: 8px; From 4978044d9e0ba04db1830629a66e7b620df792a9 Mon Sep 17 00:00:00 2001 From: Latte Date: Fri, 6 Mar 2026 22:32:17 +0100 Subject: [PATCH 02/23] Add 'Uses' page and update navigation links; enhance layout responsiveness --- src/components/Nav.astro | 2 + src/pages/uses.astro | 568 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 570 insertions(+) create mode 100644 src/pages/uses.astro diff --git a/src/components/Nav.astro b/src/components/Nav.astro index 822f083..610af0a 100644 --- a/src/components/Nav.astro +++ b/src/components/Nav.astro @@ -6,6 +6,7 @@ const links = [ { href: "/about", label: "about" }, { href: "/projects", label: "projects" }, { href: "/now", label: "now" }, + { href: "/uses", label: "uses" }, { href: "/blog", label: "blog" }, ]; @@ -49,6 +50,7 @@ function isActive(href: string, current: string): boolean { max-width: 700px; margin: 0 auto; display: flex; + flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-xs); diff --git a/src/pages/uses.astro b/src/pages/uses.astro new file mode 100644 index 0000000..322653f --- /dev/null +++ b/src/pages/uses.astro @@ -0,0 +1,568 @@ +--- +import BaseLayout from "../layouts/BaseLayout.astro"; +--- + + + + +
+
+
+

Uses

+
══════════════════════════════
+

+ This page is a window into the workshop behind Hidden Den Cafe. + It is less about a perfect spec sheet and more about the tools I + actually reach for to write code, run infrastructure, and keep + exploring how much of the internet I can build on my own terms. + Some of it lives on my desk, some of it lives in the homelab, + and some of it lives in carefully chosen external systems. +

+
+ +
+

Personal Computing Environment

+
+
+

Windows + Ubuntu on WSL2

+

+ My day-to-day machine is a Windows workstation with an Ubuntu + environment inside WSL2. It gives me a practical desktop setup + while keeping most of my actual work in a Linux shell where I + can script, build, and debug with less friction. +

+
+
+

Terminal + Bash

+

+ Bash is still where a lot of real work happens for me. If a task + can be expressed as a clean command, script, or container build, + that is usually the route I prefer because it stays close to the + system instead of hiding it behind layers of UI. +

+
+
+

Zed

+

+ Zed + is my primary editor. I like it because it is extremely fast, + modern without feeling bloated, and built for the kind of + collaborative and AI-assisted development workflow I spend a lot + of time experimenting with. +

+
+
+

Fast tools, low friction

+

+ I am happiest when tools respond immediately and get out of the + way. Markdown, plain text configs, and lightweight utilities + matter to me because they keep the system inspectable and make it + easier to move from idea to experiment without losing momentum. +

+
+
+
+ +
+

Development Tools

+
+
+

Python first, Astro on purpose

+

+ A lot of my background is in Python and Flask-style thinking: + simple services, clear behavior, and tooling I can reason about. + Cozy Den is also where I am learning Astro because it fits the + small-web, static-first approach I want for personal sites. +

+
+
+

Git + self-hosted Gitea

+

+ Version control lives on my own Gitea instance at + git.hiddenden.cafe. + I use it both as a code forge and as part of the deployment path. +

+
+
+

Docker

+

+ Containers are the default way I package and move projects. I + like being able to build something once, understand its runtime, + and ship the same artifact to the machines that need it. +

+
+
+

TypeScript, Markdown, and plain files

+

+ I use typed configs where they help, Markdown where writing + should stay lightweight, and plain files wherever possible so the + project remains durable without a huge stack around it. +

+
+
+

Editor-driven exploration

+

+ A lot of experimentation starts in the editor: quick prototypes, + note fragments, config drafts, and partial implementations. I + prefer tools that make it easy to move between writing, coding, + terminal work, and AI-assisted iteration in one place. +

+
+
+
+ +
+

AI Tools

+
+
+

Model mix, not model worship

+

+ I actively experiment with multiple AI systems, including OpenAI, + OpenAI Business, Claude, Mistral, Ollama, OpenRouter, and + Microsoft Foundry. Different tools are better at different kinds + of work, so I treat them as instruments rather than as a single + source of truth. +

+
+
+

Development and drafting support

+

+ AI is useful for implementation support, debugging odd edge cases, + brainstorming approaches, writing rough drafts, and pressure-testing + architectural ideas. It helps me move faster, but it does not get + to replace judgment, taste, or authorship. +

+
+
+

Local and hosted experimentation

+

+ Some experiments belong in cloud APIs, some belong in local model + runtimes. I am interested in both, especially where privacy, + controllability, and cost start to matter. +

+
+
+

Human ideas stay human

+

+ The point is not to automate away authorship. The point is to + extend what I can build, test, and think through while keeping + the taste, priorities, and final decisions my own. +

+
+
+
+ +
+

Infrastructure & Homelab

+
+
+

Proxmox cluster

+

+ A lot of the lab side of my work is built around a Proxmox-based + cluster. It acts as a workshop environment where services and + ideas get built, tested, broken, rebuilt, and slowly turned into + something stable enough to keep. +

+
+
+

Multiple nodes, mixed workloads

+

+ The homelab is not a single-box setup. It spans multiple nodes + running both experimental and production services, which makes it + useful for trying things out without forcing every idea directly + into the same environment. +

+
+
+

Self-hosted services

+

+ Gitea is part of that stack already, and more services tend to + follow the same philosophy: if I can run it myself without making + life worse, I would rather understand the system than outsource + it by default. +

+
+
+

Persistent storage

+

+ Persistent data matters, so storage gets treated as infrastructure, + not an afterthought. That includes self-hosted storage with tools + like OpenMediaVault and the kind of planning that keeps the lab + useful as it grows rather than turning into a graveyard of disks. +

+
+
+

Networking foundation

+

+ The network is built around a UniFi UDM Pro Max, which acts as the + core router and network controller for the environment. I want the + network to be stable first, then segmented where useful, with + secure remote access and reliable routing between local services + and the systems that need to be reachable from the outside. +

+
+
+

Tailscale for remote reachability

+

+ Tailscale is part of the connective tissue that makes the lab feel + usable from anywhere. I like tools that make remote access simple + without turning the whole setup into a networking puzzle. +

+
+
+
+ +
+

Identity & Cloud Services

+
+
+

Microsoft Entra

+

+ Entra is part of how I think about identity and access in the + broader environment. It is useful where centralized identity, + policy, and account management make sense. +

+
+
+

Microsoft Intune

+

+ Intune fits into the device and policy side of the stack. I do not + see that as a replacement for understanding systems directly, but + it is practical where managed devices and consistent policy matter. +

+
+
+

Microsoft Azure

+

+ Azure is part of the cloud experimentation side of the workshop: + useful for testing ideas, running services that do not belong at + home, and understanding how managed infrastructure behaves in the + real world. +

+
+
+

Homelab first, cloud where it helps

+

+ These Microsoft services complement the homelab rather than + replacing it. I still prefer running and understanding systems + directly whenever possible, but I am not interested in pretending + cloud tooling has no value when it clearly does. +

+
+
+
+ +
+

External Hosting

+
+
+

OVH

+

+ OVH is part of the external VPS layer I use when a service needs + public accessibility, geographic separation, or a home outside the + local lab. +

+
+
+

VPS.play.hosting

+

+ VPS.play.hosting fills a similar role for additional external + services. I like having more than one place to run things when the + goal is resilience rather than putting every dependency in one box. +

+
+
+

Hybrid infrastructure

+

+ Not everything belongs in the homelab, and not everything belongs + in the cloud. The useful middle ground is a hybrid setup where + local infrastructure and external VPS systems complement each other + instead of competing. +

+
+
+
+ +
+

Security & Identity

+
+
+

Hardware-backed authentication

+

+ I prefer security that is boring and strong. Hardware keys such as + YubiKeys make more sense to me than pretending a password alone is + enough for important accounts. +

+
+
+

GPG and identity hygiene

+

+ Cryptographic identity is part of the workflow here. I publish my + keys, use GPG where it is useful, and try to keep trust anchored in + things I can verify instead of platforms asking to be trusted. +

+
+
+

Password managers and compartmentalization

+

+ Good security is mostly consistency. Unique secrets, sensible + separation between roles and systems, and fewer scattered accounts + beat dramatic security theater every time. +

+
+
+

Privacy as a design constraint

+

+ I do not chase privacy because it sounds noble. I care about it + because it changes architecture choices: fewer unnecessary + dependencies, less telemetry, tighter control over where data ends + up, and a better understanding of what the system is doing. +

+
+
+
+ +
+

Website Stack

+
+
+

Astro

+

+ Cozy Den itself is built with Astro because static HTML is still + one of the nicest ways to publish a personal site. +

+
+
+

Vanilla CSS + TypeScript

+

+ Styling stays close to the markup, and the TypeScript that exists + is there to make content and structure safer rather than heavier. +

+
+
+

Docker + nginx

+

+ The site builds in a container and ships as static files served by + nginx, which keeps deployment small, repeatable, and easy to audit. +

+
+
+

Gitea-driven deployment path

+

+ Source, registry, and release flow all stay close to my own + infrastructure instead of depending on a hosted publishing platform. +

+
+
+

Secondary to the workshop

+

+ The site stack matters, but it is only one part of the broader + ecosystem. Cozy Den exists because of the surrounding tools, + machines, services, and habits that make building on my own terms + possible. +

+
+
+
+ +
+

+ This page is a living snapshot of the tools and systems behind Cozy Den. + It changes from time to time as experiments evolve and the workshop grows. +

+
+ +
+

Made with love by Latte

+
+
+
+
+ + From acdb782ebe8efd7b1a4f6d7f11d622a6bac0665d Mon Sep 17 00:00:00 2001 From: Latte Date: Sat, 7 Mar 2026 11:56:32 +0100 Subject: [PATCH 03/23] Revise 'About' page content and structure; enhance personal narrative and values presentation --- src/pages/about.astro | 258 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 226 insertions(+), 32 deletions(-) diff --git a/src/pages/about.astro b/src/pages/about.astro index f1c18b1..dfbf80a 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -4,67 +4,189 @@ import BaseLayout from "../layouts/BaseLayout.astro";
+

Latte / Hidden Den

About

══════════════════════════════
-
- -
-

The Den

-

- Hidden Den Cafe is my little corner of the internet — self-hosted, - self-maintained, and free from corporate nonsense. No trackers, no ads, - no data harvesting. Just a cozy space that I built and control. +

+ Hi, I'm Latte. Hidden Den exists because I wanted a personal place on + the internet that feels the way I want technology to feel: calm, + understandable, warm, and fully mine.

-
+

Who I Am

- I'm Latte — an IT wizard with a homelab, - a love for privacy, and a deep distrust of companies that treat your data - like their product. I believe in owning your infrastructure, running your - own services, and keeping things under your own roof. + I'm Latte - an IT professional, a + developer, and someone who spends a lot of time close to systems. A + lot of my day-to-day thinking is shaped by infrastructure, maintenance, + deployment, networking, and the quiet work required to keep things + reliable. I like understanding how things fit together, not just using + them from a distance.

- I'm a gay furry developer who builds things because I want them to exist — - not because some product manager told me to. My stack leans toward Python - and self-hosted tooling, but I'm always exploring new things. + I run a homelab because I genuinely enjoy learning by building, + breaking, fixing, and gradually improving the systems I rely on. I tend + to prefer tools I can audit, services I can migrate, setups I can back + up, and infrastructure I can replace without begging a platform to keep + my life intact.

-

The Homelab

+

The Person Behind The Stack

- Most of what runs here lives on my own hardware — Gitea for code, Docker - for deployment, nginx for serving. Where physical infra doesn't make sense, - I rent VPS capacity from OVH and Play.hosting. For work, the Microsoft 365 - ecosystem does what it needs to do. + The technical side is real, but it is not the whole story. I'm also a + gay furry developer with a soft spot for cozy cafe aesthetics, warm + tones, coffee culture, quiet spaces, and slow building. I am much less + interested in performing some polished hacker persona than I am in + making a space that feels thoughtful, lived in, and unmistakably human.

- The goal isn't purity — it's control. Keep data minimal, choose providers - you understand, avoid surveillance-adjacent platforms. Self-host what you - can; rent infra where it's practical; use what you need without pretending - you don't. + Hidden Den reflects that mix. It is technical, but not sterile. It is + personal, but not performative. It is a place where infrastructure, + writing, projects, experiments, and internet philosophy can sit next to + warmth, identity, and the kinds of details that make a site feel like + someone actually lives there. +

+
+

+ More cozy tech wizard than cyberpunk hacker. +

+
+
+ +
+

How I Tend To Build

+
+
+

Understand It

+

+ I am most comfortable with systems I can inspect and reason + about. If I do not understand the tradeoffs, the failure modes, + or the path out, I do not feel like I really own the tool. +

+
+
+

Keep It Durable

+

+ I prefer setups that can be backed up, migrated, repaired, and + replaced. Durable systems are not always flashy, but they age + better and make better foundations for real life. +

+
+
+

Leave Room For Care

+

+ I care about interfaces and environments that feel intentional. + Warmth matters to me. I do not think technical spaces need to be + cold to be serious. +

+
+
+

Stay Practical

+

+ I do not treat purity as a goal. I self-host a lot because it + teaches me things and gives me control, but I still care about + workable systems more than ideological posturing. +

+
+
+
+ +
+

Why Hidden Den Exists

+

+ This site is not a portfolio, a startup brand, or a personal marketing + project. It exists because I wanted a real personal website again: a + place for writing, projects, experiments, infrastructure notes, and the + kinds of ideas that do not fit neatly into social platforms. +

+

+ I wanted something quieter than a feed and more honest than a polished + personal brand. Hidden Den gives me room to publish on my own terms and + let the site grow slowly, in the shape that actually suits me.

-

Ethos

-
    -
  • privacy: Your data is yours. Period.
  • -
  • self-hosting: If you can run it yourself, you should.
  • -
  • open source: Knowledge should be shared.
  • -
  • small web: The internet is better when it's personal.
  • +

    Why This Matters To Me

    +

    + Working with infrastructure changes how you see the internet. It makes + the hidden parts visible: who owns the platform, where the data goes, + what happens when the service changes, and how little control people + often have over the spaces they depend on. That matters to me both + technically and personally. +

    +

    + I care about technology that feels intentional instead of engineered for + surveillance, lock-in, or endless engagement. I want the tools around me + to be legible. I want the places I spend time in to respect the people + using them. Hidden Den is one small attempt to build that kind of space. +

    +
+ +
+

On The Internet I Want

+

+ Too much of the modern web is optimized for extraction: attention, + behavior, identity, and dependence. I prefer a smaller internet made of + personal sites, weird projects, community infrastructure, and spaces + that are allowed to be specific. Not everything needs to become a + platform, and not every page needs to be a funnel. +

+

+ I still believe the web is better when more people make places that feel + like their own. Places with taste. Places with personality. Places that + are maintained because someone cares about them, not because they have + been optimized against a dashboard. +

+
+ +
+

Privacy-First, In Practice

+

+ Privacy is part of the philosophy here, but it is also part of the + implementation. Hidden Den avoids trackers, ads, invasive analytics, and + unnecessary third-party dependencies. Static pages are not a compromise + for me. They are often the cleaner solution. +

+

+ The same goes for the infrastructure behind the site. I prefer systems I + can audit, migrate, back up, and replace. People should be able to visit + a personal website without quietly being turned into a behavioral profile. +

+
    +
  • no trackers: Visitors are guests, not telemetry.
  • +
  • minimal dependencies: Fewer external systems means fewer leaks.
  • +
  • self-hosting bias: Control matters when the tradeoff is reasonable.
  • +
  • human scale: The site is built to feel inhabited, not optimized.
+
+

What I Want This Place To Be

+

+ Hidden Den is meant to feel like a quiet corner of the internet: warm, + thoughtful, technical, and personal. A place where I can share what I am + building and thinking about without flattening myself into a bio, a + brand, or a feed. +

+

+ If this page does its job, it should feel clear that there is a real + person behind the site. Someone who likes systems and infrastructure, + cares about privacy, prefers warm light over neon, and still thinks the + internet is worth building on carefully. +

+
+

Made with love by Latte

@@ -117,6 +239,14 @@ import BaseLayout from "../layouts/BaseLayout.astro"; margin-bottom: var(--space-lg); } + .eyebrow { + color: var(--color-text-dim); + text-transform: uppercase; + letter-spacing: 0.24em; + font-size: 0.75rem; + margin-bottom: var(--space-sm); + } + .title { font-size: 2rem; font-weight: 700; @@ -131,6 +261,13 @@ import BaseLayout from "../layouts/BaseLayout.astro"; user-select: none; } + .lead { + color: var(--color-text); + line-height: 1.8; + max-width: 36rem; + margin: 0 auto; + } + .section { margin: var(--space-lg) 0; } @@ -153,11 +290,56 @@ import BaseLayout from "../layouts/BaseLayout.astro"; margin-bottom: 0; } + .callout { + margin-top: var(--space-md); + padding: var(--space-md); + border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent); + background: + linear-gradient( + 135deg, + color-mix(in srgb, var(--color-accent) 10%, transparent), + transparent 70% + ), + color-mix(in srgb, var(--color-bg-light) 84%, transparent); + border-radius: 8px; + } + + .callout p { + color: var(--color-text); + line-height: 1.7; + } + .highlight { color: var(--color-accent-bright); font-weight: 700; } + .value-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--space-md); + } + + .value-card { + padding: var(--space-md); + border-radius: 8px; + background: color-mix(in srgb, var(--color-bg-light) 88%, transparent); + border: 1px solid color-mix(in srgb, var(--color-surface) 70%, transparent); + } + + .value-card h3 { + color: var(--color-accent-bright); + font-size: 0.95rem; + text-transform: uppercase; + letter-spacing: 0.12em; + margin-bottom: var(--space-xs); + } + + .value-card p { + color: var(--color-text-dim); + line-height: 1.7; + } + .values { list-style: none; display: flex; @@ -175,6 +357,10 @@ import BaseLayout from "../layouts/BaseLayout.astro"; font-weight: 600; } + .compact { + margin-top: var(--space-md); + } + .footer { margin-top: var(--space-xl); text-align: center; @@ -221,6 +407,14 @@ import BaseLayout from "../layouts/BaseLayout.astro"; .divider { font-size: 0.6rem; } + + .lead { + font-size: 0.95rem; + } + + .value-grid { + grid-template-columns: 1fr; + } } @media (prefers-reduced-motion: reduce) { From 975e1422fc2388226a8f47c098da16560bcce6b7 Mon Sep 17 00:00:00 2001 From: Latte Date: Sat, 7 Mar 2026 11:59:06 +0100 Subject: [PATCH 04/23] just a little update --- src/pages/about.astro | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/about.astro b/src/pages/about.astro index dfbf80a..cc561c1 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -32,9 +32,9 @@ import BaseLayout from "../layouts/BaseLayout.astro"; them from a distance.

- I run a homelab because I genuinely enjoy learning by building, - breaking, fixing, and gradually improving the systems I rely on. I tend - to prefer tools I can audit, services I can migrate, setups I can back + I run a homelab because I enjoy learning by building, breaking, + fixing, and gradually improving the systems I rely on. I tend to + prefer tools I can audit, services I can migrate, setups I can back up, and infrastructure I can replace without begging a platform to keep my life intact.

@@ -119,9 +119,9 @@ import BaseLayout from "../layouts/BaseLayout.astro";

Why This Matters To Me

- Working with infrastructure changes how you see the internet. It makes - the hidden parts visible: who owns the platform, where the data goes, - what happens when the service changes, and how little control people + Working with infrastructure changes how you see the internet. It reveals + the parts most people never notice: who owns the platform, where the data + goes, what happens when the service changes, and how little control people often have over the spaces they depend on. That matters to me both technically and personally.

From 730a115de7dfe9f91a13afc3866348c3143d45b8 Mon Sep 17 00:00:00 2001 From: Latte Date: Sat, 7 Mar 2026 12:26:03 +0100 Subject: [PATCH 05/23] Add Start page and Nav link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new /start route (src/pages/start.astro) as an orientation for new visitors — includes recommended posts, highlighted projects, and page styles. Add the /start entry to src/components/Nav.astro. --- src/components/Nav.astro | 1 + src/pages/start.astro | 616 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 617 insertions(+) create mode 100644 src/pages/start.astro diff --git a/src/components/Nav.astro b/src/components/Nav.astro index 610af0a..08f494c 100644 --- a/src/components/Nav.astro +++ b/src/components/Nav.astro @@ -3,6 +3,7 @@ const currentPath = Astro.url.pathname; const links = [ { href: "/", label: "home" }, + { href: "/start", label: "start" }, { href: "/about", label: "about" }, { href: "/projects", label: "projects" }, { href: "/now", label: "now" }, diff --git a/src/pages/start.astro b/src/pages/start.astro new file mode 100644 index 0000000..65412a8 --- /dev/null +++ b/src/pages/start.astro @@ -0,0 +1,616 @@ +--- +import BaseLayout from "../layouts/BaseLayout.astro"; +import projects from "../data/projects.json"; +import { getCollection, type CollectionEntry } from "astro:content"; + +type Project = { + name: string; + description: string; + tags: string[]; + status: "stable" | "wip" | "concept"; + links: { + site?: string; + gitea?: string; + github?: string; + }; +}; + +const posts = (await getCollection("blog", ({ data }) => !data.draft)).sort( + (a, b) => b.data.date.valueOf() - a.data.date.valueOf(), +); + +const recommendedPosts = posts.slice(0, 3); + +const typedProjects = projects as Project[]; +const highlightedProjectNames = ["GuardDen", "openrabbit", "DevDen"]; + +const highlightedProjects = highlightedProjectNames + .map((name) => typedProjects.find((project) => project.name === name)) + .filter((project): project is Project => Boolean(project)); + +function formatDate(date: CollectionEntry<"blog">["data"]["date"]) { + return date.toISOString().split("T")[0]; +} + +function getProjectLink(project: Project) { + return project.links.site || project.links.gitea || project.links.github || "/projects"; +} + +function getProjectLinkLabel(project: Project) { + if (project.links.site) return "visit"; + if (project.links.gitea) return "gitea"; + if (project.links.github) return "github"; + return "projects"; +} +--- + + + + +
+
+
+

New here?

+

Start Here

+
==============================
+

+ This page is a small orientation point for first-time visitors. If + you have just found Hidden Den, this is the quickest way to get a + feel for what kind of place it is and where you might want to wander + next. +

+
+ +
+

What Hidden Den Is

+

+ Hidden Den is Latte's personal corner of the internet: part + writing space, part workshop, part quiet place to think out loud. It + holds projects, experiments, infrastructure-minded notes, and the + kind of personal web presence that does not need to behave like a + brand. +

+

+ The site leans toward privacy, ownership, and human-scale spaces + online. It is built to feel warm and readable rather than optimized, + noisy, or extractive. More cozy tech wizard than cyberpunk hacker. +

+
+

+ If you want the short version: this is a personal site for + writing, building, self-hosting, and keeping a small piece of the + web genuinely personal. +

+
+
+ +
+
+ + See all posts +
+

+ A few good starting points from the blog. These give a feel for the + den so far without asking you to dig through everything first. +

+ + {recommendedPosts.length === 0 ? ( +

The den is still quiet on the writing front.

+ ) : ( +
    + {recommendedPosts.map((post) => ( +
  • +
    +
    + posted + +
    +

    + {post.data.title} +

    +

    {post.data.description}

    +
    +
  • + ))} +
+ )} +
+ +
+

Explore The Site

+

+ If you would rather browse by section, these are the best places to + continue. +

+ + +
+ +
+
+

Projects Worth Seeing

+ Browse all projects +
+

+ A small curated subset, just enough to sketch the shape of the work + without turning this page into a full catalog. +

+ +
    + {highlightedProjects.map((project) => ( +
  • + +
  • + ))} +
+
+ +
+

Made with love by Latte

+
+
+
+
+ + From 42bea4d9bacd65382d40f8591ae991881822fc99 Mon Sep 17 00:00:00 2001 From: Latte Date: Sat, 7 Mar 2026 12:52:05 +0100 Subject: [PATCH 06/23] Add blog post and support pubDate/tags Add new post "After the Silence". Update content schema to use pubDate and include tags (default empty). Update blog listing, post page and start page to use pubDate, render tag lists, and compute/show up to two related posts by tag overlap. Misc formatting and small display tweaks. --- src/content/blog/after-the-silence.md | 133 ++++++++++++++ src/content/blog/hello-world.md | 7 +- src/content/blog/love-without-access.md | 27 +-- src/content/config.ts | 17 +- src/pages/blog/[...slug].astro | 214 ++++++++++++++++++++-- src/pages/blog/index.astro | 102 ++++++++--- src/pages/start.astro | 230 +++++++++++++++--------- 7 files changed, 580 insertions(+), 150 deletions(-) create mode 100644 src/content/blog/after-the-silence.md diff --git a/src/content/blog/after-the-silence.md b/src/content/blog/after-the-silence.md new file mode 100644 index 0000000..4cd7bb8 --- /dev/null +++ b/src/content/blog/after-the-silence.md @@ -0,0 +1,133 @@ +--- +title: "After the Silence" +description: "Reflections on what remains after a meaningful relationship ends, and how love can transform without disappearing." +pubDate: 2026-03-04 +tags: ["love", "reflection", "healing", "relationships", "personal"] +--- + +When a relationship ends, the world does not become quiet immediately. + +At first there is noise. + +Questions that circle endlessly. +Memories that appear without warning. +Moments where the absence of someone feels louder than their presence ever was. + +But eventually something changes. + +The storm that once lived inside your mind slowly settles. + +And what remains is something different. + +A silence that is not empty - but reflective. + +--- + +## The Quiet After Love + +The silence that follows a meaningful relationship is unlike any other. + +It is not the silence of indifference. +Nor the silence of forgetting. + +It is the quiet that appears when two lives that once moved together begin to move separately. + +Routines shift. +Spaces feel different. +The future you once imagined slowly dissolves into something undefined. + +At first, that silence can feel unsettling. + +But over time it becomes something else. + +A place where reflection becomes possible. + +--- + +## Love Does Not Always Disappear + +One of the more confusing realizations after a relationship ends is that love does not always vanish with it. + +Even when distance becomes necessary, care can remain. + +This can feel contradictory at first. + +We often expect emotional closure to mean the disappearance of feeling. + +But meaningful connections rarely dissolve that neatly. + +Sometimes love simply changes form. + +It moves from something shared into something carried quietly. + +And that does not make it less real. + +--- + +## The Quiet Transformation + +Time has a way of reshaping emotional intensity. + +What once felt overwhelming slowly becomes more understandable. + +Moments that once caused pain begin to look different when viewed from a distance. + +Not because the past changes. + +But because you do. + +Perspective grows. + +The urgency fades. + +And what once felt like chaos becomes something you can hold with calm understanding. + +This transformation rarely happens suddenly. + +It arrives slowly, through reflection, distance, and the quiet work of healing. + +--- + +## Carrying Without Clinging + +There is a difference between remembering someone and remaining attached to them. + +Clinging keeps you in the past. + +Carrying allows you to move forward. + +When you carry something, you acknowledge its importance without letting it control your direction. + +You recognize the place it had in your life. + +You respect the impact it left. + +But you also accept that some chapters belong where they ended. + +In that sense, remembering can become an act of peace rather than longing. + +--- + +## What Remains + +When the emotional storm has passed, something meaningful remains. + +Not the loss. + +Not the confusion. + +But the understanding. + +You understand more about the kind of connection that matters to you. + +You understand more about your own capacity to care deeply. + +And perhaps most importantly, you understand that love itself was never the mistake. + +Even when relationships end, the love that existed still shapes who we become. + +It leaves behind lessons, perspective, and a deeper awareness of what it means to connect with another human being. + +And sometimes, what remains after the silence is not emptiness. + +But growth. diff --git a/src/content/blog/hello-world.md b/src/content/blog/hello-world.md index a0008d0..1cf92c2 100644 --- a/src/content/blog/hello-world.md +++ b/src/content/blog/hello-world.md @@ -1,7 +1,8 @@ ---- +--- title: "Welcome to the Den" -date: 2026-03-01 description: "First proper post. Why I built this site, what it runs on, and what to expect." +pubDate: 2026-03-01 +tags: ["self-hosting", "privacy", "personal-web", "infrastructure"] draft: false --- @@ -11,7 +12,7 @@ So I finally got around to setting up a proper blog. Welcome. I wanted a place to write that wasn't owned by a corporation. No Medium, no Substack, no algorithm deciding who sees what. Just markdown files on my own server, served by nginx from a Docker container I control. -That's the whole point of the den — owning your own space on the internet. +That's the whole point of the den - owning your own space on the internet. ## What It Runs On diff --git a/src/content/blog/love-without-access.md b/src/content/blog/love-without-access.md index c4c6572..6e8c234 100644 --- a/src/content/blog/love-without-access.md +++ b/src/content/blog/love-without-access.md @@ -1,7 +1,8 @@ ---- +--- title: "Love Without Access" -date: 2026-03-01 -description: "A reflection on a first love — what it meant, what it cost, and why distance was the most loving thing left." +description: "A reflection on a first love - what it meant, what it cost, and why distance was the most loving thing left." +pubDate: 2026-03-01 +tags: ["love", "reflection", "healing", "relationships", "personal"] --- *by LATTE* @@ -16,7 +17,7 @@ Power and surrender that were, underneath it all, just different shapes of trust This isn't a story about blame. And it's not a story about anger. -It's a story about something that stayed real… +It's a story about something that stayed real... even after it stopped being reachable. --- @@ -25,7 +26,7 @@ even after it stopped being reachable. We were friends first. -Maybe that's what made it so deep. It didn't explode into existence — it grew. Slowly. Safely. +Maybe that's what made it so deep. It didn't explode into existence - it grew. Slowly. Safely. From gaming together to talking for hours. From talking to tension. From tension to touch. @@ -75,7 +76,7 @@ Silence. "I don't feel it anymore." -And something in me went very quiet — and very loud — at the same time. +And something in me went very quiet - and very loud - at the same time. Because I could still feel myself holding on. And carrying someone who no longer carries back @@ -98,7 +99,7 @@ But no longer together. A body that says yes. Words that say no. -That contradiction doesn't just hurt emotionally — it destabilizes you. +That contradiction doesn't just hurt emotionally - it destabilizes you. Hope becomes a reflex. And every time hope collapses, you fracture a little with it. @@ -199,7 +200,7 @@ That's architecture. ## On Being Replaced -Yes — he found someone new quickly. +Yes - he found someone new quickly. That hurt. @@ -222,7 +223,7 @@ Something ending does not mean it was nothing. ## For You -If you ever read this — +If you ever read this - I want you to know that what we had was real to me. Not experimental. Not temporary. Not a placeholder. @@ -243,7 +244,7 @@ Trying. Failing. Adjusting. Discovering what intimacy meant. Discovering what we meant. -You weren't just someone who entered my life — +You weren't just someone who entered my life - you were part of my becoming. That matters. @@ -262,7 +263,7 @@ I was trying to hold something I didn't yet know how to let go of. But even then, I wasn't against you. -I saw you as someone struggling — not someone malicious. +I saw you as someone struggling - not someone malicious. There were moments when you softened completely with me. Moments where you rested your full weight without guarding yourself. @@ -295,7 +296,7 @@ It changes shape. It becomes quieter. It becomes something I carry instead of something I reach for. -And yes — I'm going to be a little playful about it: +And yes - I'm going to be a little playful about it: You're on my website. Do you get that? @@ -333,4 +334,4 @@ to be worthy of being held. The wolf in me was never meant to become smaller. Only to find the right pack. -— LATTE +- LATTE diff --git a/src/content/config.ts b/src/content/config.ts index a6a1b8e..8ab26a5 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -1,13 +1,14 @@ -import { z, defineCollection } from 'astro:content'; +import { z, defineCollection } from "astro:content"; const blog = defineCollection({ - type: 'content', - schema: z.object({ - title: z.string(), - date: z.coerce.date(), - description: z.string(), - draft: z.boolean().optional().default(false), - }), + type: "content", + schema: z.object({ + title: z.string(), + description: z.string(), + pubDate: z.coerce.date(), + tags: z.array(z.string()).default([]), + draft: z.boolean().optional().default(false), + }), }); export const collections = { blog }; diff --git a/src/pages/blog/[...slug].astro b/src/pages/blog/[...slug].astro index 554d6a8..8ce849d 100644 --- a/src/pages/blog/[...slug].astro +++ b/src/pages/blog/[...slug].astro @@ -1,16 +1,43 @@ --- import BaseLayout from "../../layouts/BaseLayout.astro"; -import { getCollection } from "astro:content"; +import { getCollection, type CollectionEntry } from "astro:content"; + +type BlogPost = CollectionEntry<"blog">; export async function getStaticPaths() { const posts = await getCollection("blog", ({ data }) => !data.draft); + return posts.map((post) => ({ params: { slug: post.slug }, - props: { post }, + props: { + post, + relatedPosts: posts + .filter((candidate) => candidate.slug !== post.slug) + .map((candidate) => ({ + post: candidate, + score: candidate.data.tags.filter((tag) => + post.data.tags.includes(tag), + ).length, + })) + .sort((a, b) => { + if (b.score !== a.score) return b.score - a.score; + return ( + b.post.data.pubDate.valueOf() - + a.post.data.pubDate.valueOf() + ); + }) + .filter((candidate) => candidate.score > 0) + .slice(0, 2) + .map((candidate) => candidate.post), + }, })); } -const { post } = Astro.props; +const { post, relatedPosts = [] } = Astro.props as { + post: BlogPost; + relatedPosts: BlogPost[]; +}; + const { Content } = await post.render(); function formatDate(date: Date) { @@ -19,7 +46,7 @@ function formatDate(date: Date) { ---