feat(links): redesign links directory to bryl-minimal aesthetic and add auto theme detection

- Redesign links page to clean 34rem narrow measure bryl-minimal layout
- Add automatic system theme detection (prefers-color-scheme) with live adaptation
- Add interactive theme toggle pill (Auto / Light / Dark) in the top navigation bar
- Display natural color day/night portraits, Geist Pixel typography, and clean bryl-cards
- Update navbar item ordering in Navbar.tsx
This commit is contained in:
ssbg04 committed 2026-10-23 00:57:19 +08:00
1 parent b7723b11d9
commit a169dc7d7d
2 files changed
+202 -83

No files matched your search

+1 -1
View File
@@ -7,8 +7,8 @@ const navItems = [
{ label: 'projects', href: '/projects' },
{ label: 'about', href: '/about' },
{ label: 'certifications', href: '/certifications' },
{ label: 'gallery', href: '/gallery' },
{ label: 'contact', href: '/contact' },
{ label: 'gallery', href: '/gallery' },
];
interface NavbarProps {
+201 -82
View File
@@ -1,6 +1,8 @@
---
import Layout from '../layouts/Layout.astro';
import { getSiteSettings, getSocialLinks } from '../lib/data';
import ppDay from '../assets/pp-day.webp';
import ppNight from '../assets/pp-night.webp';
const settings = await getSiteSettings();
const allLinks = await getSocialLinks();
@@ -10,13 +12,14 @@ const links = allLinks.length > 0
? allLinks.filter(l => l.showInLinksPage !== false)
: [
{ platform: 'GitHub', url: 'https://github.com/ssbg04', icon: 'github', order: 1 },
{ platform: 'Email', url: 'mailto:crischarlesgarcia345@gmail.com', icon: 'mail', order: 2 },
{ platform: 'LinkedIn', url: 'https://linkedin.com', icon: 'linkedin', order: 3 },
{ platform: 'TikTok', url: 'https://tiktok.com', icon: 'tiktok', order: 4 },
{ platform: 'Facebook', url: 'https://facebook.com', icon: 'facebook', order: 5 },
{ platform: 'LinkedIn', url: 'https://www.linkedin.com/in/cris-charles-garcia/', icon: 'linkedin', order: 2 },
{ platform: 'Email', url: 'mailto:crischarlesgarcia345@gmail.com', icon: 'mail', order: 3 },
{ platform: 'Facebook', url: 'https://www.facebook.com/kristyarls345/', icon: 'facebook', order: 4 },
{ platform: 'TikTok', url: 'https://www.tiktok.com/@sisibigi', icon: 'tiktok', order: 5 },
];
const profileAvatar = settings.heroImage || settings.logoImage || '/logo.png';
const dayAvatar = settings.heroImage || (ppDay as any).src || ppDay;
const nightAvatar = settings.heroImageNight || (ppNight as any).src || ppNight || dayAvatar;
const formatDisplayUrl = (url: string) => {
try {
@@ -30,26 +33,26 @@ const formatDisplayUrl = (url: string) => {
const getIconSvg = (platform: string) => {
const p = platform.toLowerCase();
if (p.includes('github')) {
return '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>';
return '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>';
}
if (p.includes('linkedin')) {
return '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.779-1.75-1.75s.784-1.75 1.75-1.75 1.75.779 1.75 1.75-.784 1.75-1.75 1.75zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>';
return '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.779-1.75-1.75s.784-1.75 1.75-1.75 1.75.779 1.75 1.75-.784 1.75-1.75 1.75zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>';
}
if (p.includes('facebook')) {
return '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874V12h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>';
return '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874V12h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>';
}
if (p.includes('tiktok')) {
return '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12.53.02C13.84 0 15.14.01 16.44 0c.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.09-1.03-1.87-1.09-2.93-3.16-2.61-5.25.38-2.52 2.5-4.46 5.04-4.46.73 0 1.43.16 2.07.46.06.03.11.05.16.08v4.18c-1.38-.24-2.81-.19-4.16.14-1.12.28-2.12 1.05-2.67 2.06-.55 1.01-.58 2.22-.09 3.25.48 1.01 1.41 1.72 2.49 2.03 1.1.32 2.27.32 3.37.01.69-.2 1.34-.55 1.87-.99.53-.44.97-.99 1.25-1.61.28-.62.43-1.28.46-1.95.06-2.69.02-5.38.02-8.07z"/></svg>';
return '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12.53.02C13.84 0 15.14.01 16.44 0c.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.09-1.03-1.87-1.09-2.93-3.16-2.61-5.25.38-2.52 2.5-4.46 5.04-4.46.73 0 1.43.16 2.07.46.06.03.11.05.16.08v4.18c-1.38-.24-2.81-.19-4.16.14-1.12.28-2.12 1.05-2.67 2.06-.55 1.01-.58 2.22-.09 3.25.48 1.01 1.41 1.72 2.49 2.03 1.1.32 2.27.32 3.37.01.69-.2 1.34-.55 1.87-.99.53-.44.97-.99 1.25-1.61.28-.62.43-1.28.46-1.95.06-2.69.02-5.38.02-8.07z"/></svg>';
}
if (p.includes('mail') || p.includes('email')) {
return '<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" /></svg>';
return '<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" /></svg>';
}
return '<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" /></svg>';
return '<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" /></svg>';
};
---
<Layout
title={`Links & Socials | ${settings.fullName}`}
title={`Links & Directory | ${settings.fullName}`}
description="Official social links, contact options, and developer directory for Cris Charles Garcia."
canonicalURL="https://links.crischarles.com"
>
@@ -57,92 +60,206 @@ const getIconSvg = (platform: string) => {
body {
padding-top: 0 !important;
}
/* Reset Layout.astro defaults on links page since there is no navbar */
#magnify-content {
padding-top: 0 !important;
padding-left: 0 !important;
}
@media (orientation: landscape) and (max-height: 500px) {
.links-main-container {
padding-top: 0.75rem !important;
padding-bottom: 2.5rem !important;
}
.links-profile-card {
padding: 0.875rem !important;
}
.links-avatar {
width: 3.25rem !important;
height: 3.25rem !important;
margin-bottom: 0.35rem !important;
}
.links-header-pills {
margin-bottom: 0.625rem !important;
}
}
</style>
{/* True Linktree Style: No Header Nav and No Footer */}
<main class="min-h-screen py-8 px-4 sm:px-6 flex flex-col items-center justify-start md:justify-center relative links-main-container">
{/* Auto-Detect Theme Script for Links Page */}
<script is:inline>
(function() {
function getSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function applyThemeMode(mode) {
var resolved = mode === 'system' ? getSystemTheme() : mode;
if (resolved === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
var pillText = document.getElementById('theme-pill-text');
var pillIcon = document.getElementById('theme-pill-icon');
if (pillText && pillIcon) {
if (mode === 'system') {
pillIcon.textContent = '◐';
pillText.textContent = 'AUTO (' + resolved + ')';
} else if (mode === 'dark') {
pillIcon.textContent = '☾';
pillText.textContent = 'DARK';
} else {
pillIcon.textContent = '☼';
pillText.textContent = 'LIGHT';
}
}
}
var currentMode = localStorage.getItem('theme') || 'system';
applyThemeMode(currentMode);
// Live listener for OS theme change
try {
var mq = window.matchMedia('(prefers-color-scheme: dark)');
var changeHandler = function() {
var stored = localStorage.getItem('theme');
if (!stored || stored === 'system') {
applyThemeMode('system');
}
};
if (mq.addEventListener) {
mq.addEventListener('change', changeHandler);
} else if (mq.addListener) {
mq.addListener(changeHandler);
}
} catch (e) {}
// Toggle button click handler
document.addEventListener('DOMContentLoaded', function() {
var btn = document.getElementById('theme-auto-pill');
if (btn) {
btn.addEventListener('click', function() {
var stored = localStorage.getItem('theme') || 'system';
var nextMode = 'system';
if (stored === 'system') nextMode = 'light';
else if (stored === 'light') nextMode = 'dark';
else nextMode = 'system';
localStorage.setItem('theme', nextMode);
applyThemeMode(nextMode);
window.dispatchEvent(new CustomEvent('theme-change', { detail: { theme: nextMode === 'system' ? getSystemTheme() : nextMode } }));
});
}
applyThemeMode(localStorage.getItem('theme') || 'system');
});
// Handle page load / Astro swaps
document.addEventListener('astro:page-load', function() {
applyThemeMode(localStorage.getItem('theme') || 'system');
});
})();
</script>
{/* Main Container — narrow measure (34rem) per bryl-minimal specification */}
<main class="min-h-screen py-8 sm:py-12 px-4 sm:px-6 flex flex-col items-center justify-start relative">
{/* Return to Main Portfolio Pill & Status */}
<div class="w-full max-w-[42rem] flex justify-between items-center mb-6 links-header-pills">
<a
href="https://crischarles.com"
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-card-custom hover:bg-muted-custom/40 text-muted-foreground-custom hover:text-foreground-custom text-xs font-mono border border-border-custom transition-all"
>
<span>←</span>
<span>portfolio</span>
</a>
<div class="w-full max-w-[34rem] flex flex-col gap-6 relative z-10 bryl-entrance stagger-1">
{settings.isAvailable !== false && (
<span class="text-[10px] font-mono text-foreground-custom flex items-center gap-1.5 px-2.5 py-1 rounded-md border border-border-custom bg-card-custom">
<span class="w-1.5 h-1.5 rounded-full bg-foreground-custom animate-pulse" />
{settings.availabilityStatus || 'available for work'}
</span>
)}
</div>
{/* Top Header Navigation: Back Link, Live Status, and Auto Theme Pill */}
<div class="w-full flex items-center justify-between gap-2.5 pb-2 border-b border-border-custom">
<a
href="https://crischarles.com"
class="inline-flex items-center gap-1.5 text-xs font-mono text-muted-foreground-custom hover:text-foreground-custom transition-colors group"
>
<span class="group-hover:-translate-x-1 transition-transform">←</span>
<span>portfolio</span>
</a>
{/* Content Container */}
<div class="w-full max-w-[42rem] flex flex-col gap-4 relative z-10">
{/* Profile Header Card */}
<div class="bryl-card p-6 text-center flex flex-col items-center links-profile-card">
<div class="w-16 h-16 sm:w-20 sm:h-20 rounded-2xl overflow-hidden border border-border-custom mb-3 bg-muted-custom/20 links-avatar">
<img
src={profileAvatar}
alt={settings.fullName}
class="w-full h-full object-cover"
loading="eager"
decoding="async"
/>
<div class="flex items-center gap-2">
{/* Live Availability Status */}
{settings.isAvailable !== false && (
<span class="text-[10px] font-mono text-foreground-custom inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md border border-border-custom bg-card-custom shadow-xs">
<span class="w-1.5 h-1.5 rounded-full bg-foreground-custom animate-pulse opacity-80" />
<span class="hidden sm:inline lowercase">{settings.availabilityStatus || 'available for work'}</span>
<span class="sm:hidden">available</span>
</span>
)}
{/* Interactive Auto-Detect Theme Pill */}
<button
id="theme-auto-pill"
type="button"
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md bg-card-custom hover:bg-elevated-custom text-foreground-custom text-[10px] font-mono font-semibold border border-border-custom transition-all cursor-pointer shadow-xs active:scale-95"
title="Auto-detects device theme. Tap to cycle: Auto → Light → Dark"
aria-label="Theme Mode Toggle"
>
<span id="theme-pill-icon" class="text-[11px]">◐</span>
<span id="theme-pill-text" class="tracking-wider uppercase">AUTO</span>
</button>
</div>
<h1 class="text-xl sm:text-2xl font-mono lowercase font-bold text-foreground-custom tracking-tight">
{settings.fullName}
</h1>
<p class="text-xs font-mono text-muted-foreground-custom mt-1">
{settings.title} // {settings.location}
</p>
<p class="text-xs font-mono text-muted-foreground-custom max-w-sm mt-2 leading-relaxed">
{settings.valueProposition || 'Direct links to my official profiles, repositories, and credentials.'}
</p>
</div>
{/* Social Links List */}
<div class="flex flex-col gap-2">
{/* Profile Header Card */}
<section class="bryl-card p-6 sm:p-7 text-center flex flex-col items-center relative overflow-hidden bryl-entrance stagger-2">
{/* Subtle halftone corner garnish */}
<div class="absolute -top-6 -right-6 w-28 h-28 halftone-bg opacity-40 pointer-events-none rounded-full mask-radial"></div>
{/* Natural Color Avatar (16px outer radius, 12px inner) */}
<div class="relative w-20 h-20 sm:w-22 sm:h-22 rounded-2xl border border-border-custom p-1 bg-card-custom shadow-sm mb-3.5">
<div class="w-full h-full rounded-[12px] overflow-hidden bg-elevated-custom">
<img
src={dayAvatar}
alt={settings.fullName}
class="w-full h-full object-cover dark:hidden transition-transform duration-500 ease-out hover:scale-105"
loading="eager"
decoding="async"
/>
<img
src={nightAvatar}
alt={settings.fullName}
class="w-full h-full object-cover hidden dark:block transition-transform duration-500 ease-out hover:scale-105"
loading="eager"
decoding="async"
/>
</div>
</div>
{/* Name — Geist Pixel Square with clean kerning */}
<h1 class="font-display text-2xl sm:text-3xl font-bold lowercase text-foreground-custom tracking-tight mb-1.5">
{settings.fullName.toLowerCase()}
</h1>
{/* Micro-label Role & Location */}
<p class="micro-label text-[10px] sm:text-[11px] text-muted-foreground-custom uppercase tracking-widest">
{settings.title ? settings.title.split('|')[0].trim() : 'Software Developer'} // {settings.location}
</p>
{/* Editorial Value Proposition Body */}
<p class="font-serif text-xs sm:text-sm text-muted-foreground-custom max-w-sm mt-3 leading-relaxed">
{settings.valueProposition || 'Direct links to my official profiles, repositories, and credentials.'}
</p>
{/* Primary Inverted Action Chip */}
<a
href={settings.resumeFile || '/CV-Cris-Charles-Garcia.pdf'}
target="_blank"
rel="noopener noreferrer"
class="chip-inverted text-xs py-1.5 px-4 font-mono mt-4 hover:opacity-90 inline-flex items-center gap-1.5 transition-all shadow-xs"
>
<span>download cv</span>
<span class="text-[10px]" aria-hidden="true">↗</span>
</a>
</section>
{/* Directory Section Header */}
<div class="flex items-center justify-between pt-2">
<div class="section-marker">
<span class="section-marker-num">01 —</span>
<span class="section-marker-title">official links</span>
</div>
<span class="micro-label text-[10px] text-muted-foreground-custom">
{links.length} verified channels
</span>
</div>
{/* Social Links List — Hairline bryl-cards with micro-interaction hover */}
<nav class="flex flex-col gap-2.5 bryl-entrance stagger-3" aria-label="Official Links">
{links.map((link) => (
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
class="bryl-card p-3.5 sm:p-4 flex items-center justify-between group cursor-pointer"
class="bryl-card p-3.5 sm:p-4 flex items-center justify-between group cursor-pointer transition-all duration-300 hover:-translate-y-0.5 hover:border-border-strong hover:shadow-md"
>
<div class="flex items-center gap-3.5 min-w-0">
<div class="w-8 h-8 rounded-lg bg-foreground-custom/5 border border-border-custom flex items-center justify-center text-foreground-custom shrink-0" set:html={getIconSvg(link.platform)}></div>
<div class="w-9 h-9 rounded-xl bg-foreground-custom/[0.04] border border-border-custom flex items-center justify-center text-foreground-custom shrink-0 group-hover:bg-foreground-custom group-hover:text-background-custom transition-all duration-200">
<Fragment set:html={getIconSvg(link.platform)} />
</div>
<div class="flex flex-col min-w-0">
<span class="text-xs sm:text-sm font-mono lowercase font-bold text-foreground-custom leading-tight">
<span class="text-xs sm:text-sm font-mono lowercase font-semibold text-foreground-custom leading-tight group-hover:underline">
{link.platform}
</span>
<span class="text-[10px] sm:text-[11px] font-mono text-muted-foreground-custom truncate leading-tight mt-0.5" title={link.url}>
@@ -151,17 +268,19 @@ const getIconSvg = (platform: string) => {
</div>
</div>
<span class="text-xs font-mono text-muted-foreground-custom group-hover:text-foreground-custom group-hover:translate-x-0.5 transition-all shrink-0 ml-3">
<span class="text-xs font-mono text-muted-foreground-custom group-hover:text-foreground-custom group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all shrink-0 ml-3">
↗
</span>
</a>
))}
</nav>
{/* Minimal Footer Stamp */}
<p class="text-[10px] font-mono text-muted-foreground-custom text-center mt-4">
{/* Minimal Footer Stamp */}
<footer class="text-center pt-4 pb-2">
<p class="micro-label text-[10px] text-muted-foreground-custom uppercase tracking-widest">
&copy; {new Date().getFullYear()} {settings.fullName} // directory
</p>
</div>
</footer>
</div>
</main>