2026-07-04 22:53:10 +00:00
|
|
|
---
|
|
|
|
|
import Icon from "./Icon.astro";
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
import { ThemeToggle } from "@/components/ui/be-ui-theme-toggle";
|
|
|
|
|
import { CurvedMenu } from "@/components/ui/curved-menu";
|
2026-07-04 22:53:10 +00:00
|
|
|
import { SITE } from "../lib/blog-data.js";
|
|
|
|
|
|
|
|
|
|
const nav = [
|
|
|
|
|
{ to: "/services", label: "Services" },
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
{ to: "/projects", label: "My Projects" },
|
|
|
|
|
{ to: "/about", label: "About" },
|
|
|
|
|
{ to: "/resume", label: "Resume" },
|
|
|
|
|
{ to: "/contact", label: "Contact Me" },
|
2026-07-24 00:14:28 +00:00
|
|
|
{ to: "/zzz-queue-test", label: "Queue Test" },
|
2026-07-04 22:53:10 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const current = Astro.url.pathname.replace(/\/$/, "") || "/";
|
|
|
|
|
const isHome = current === "/";
|
|
|
|
|
const activeClass = isHome ? "home-header-link is-active" : "text-foreground";
|
|
|
|
|
const inactiveClass = isHome
|
|
|
|
|
? "home-header-link"
|
|
|
|
|
: "text-muted-foreground transition-colors hover:text-foreground";
|
|
|
|
|
const headerClass = isHome
|
|
|
|
|
? "sticky top-0 z-40 border-b border-transparent bg-transparent text-white"
|
|
|
|
|
: "sticky top-0 z-40 border-b border-border/60 bg-background/80 backdrop-blur-md";
|
|
|
|
|
const iconClass = isHome
|
|
|
|
|
? "home-header-action inline-flex h-9 w-9 items-center justify-center rounded-full transition-colors"
|
|
|
|
|
: "inline-flex h-9 w-9 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
<header class={headerClass} data-home-header={isHome ? "true" : undefined} data-scrolled="false">
|
|
|
|
|
<div class="relative mx-auto flex h-16 max-w-6xl items-center justify-between px-5">
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
<a href="/" class="ca-header-brand flex items-center gap-2.5" aria-label={SITE.name}>
|
|
|
|
|
<img
|
|
|
|
|
src="/brand/carlos-arias-signature.svg"
|
|
|
|
|
alt={SITE.name}
|
|
|
|
|
width="2077"
|
|
|
|
|
height="520"
|
|
|
|
|
class="ca-header-logo h-6 w-auto sm:h-7"
|
|
|
|
|
/>
|
|
|
|
|
<span class="ca-header-seal" aria-hidden="true">印</span>
|
2026-07-04 22:53:10 +00:00
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<nav class="hidden items-center gap-8 md:flex" aria-label="Primary navigation">
|
|
|
|
|
{
|
|
|
|
|
nav.map((item) => {
|
|
|
|
|
const exact = item.to === "/";
|
|
|
|
|
const active = exact ? current === "/" : current.startsWith(item.to);
|
|
|
|
|
return (
|
|
|
|
|
<a
|
|
|
|
|
href={item.to}
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
class={`ca-nav-link text-sm ${active ? activeClass : inactiveClass}`}
|
2026-07-04 22:53:10 +00:00
|
|
|
aria-current={active ? "page" : undefined}
|
|
|
|
|
>
|
|
|
|
|
{item.label}
|
|
|
|
|
</a>
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center gap-1">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
data-search-toggle
|
|
|
|
|
aria-label="Search"
|
|
|
|
|
aria-controls="site-search"
|
|
|
|
|
aria-expanded="false"
|
|
|
|
|
class={iconClass}
|
|
|
|
|
>
|
|
|
|
|
<Icon name="search" class="h-4 w-4" />
|
|
|
|
|
</button>
|
|
|
|
|
<a
|
|
|
|
|
href="/rss.xml"
|
|
|
|
|
aria-label="RSS feed"
|
|
|
|
|
class={`${iconClass} hidden sm:inline-flex`}
|
|
|
|
|
>
|
|
|
|
|
<Icon name="rss" class="h-4 w-4" />
|
|
|
|
|
</a>
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
<!-- React island. Only this component ships JS; the rest of the page
|
|
|
|
|
stays static. client:load so the icon is correct before paint. -->
|
|
|
|
|
<ThemeToggle
|
|
|
|
|
client:load
|
|
|
|
|
variant="circle-blur"
|
|
|
|
|
start="top-right"
|
|
|
|
|
className={iconClass}
|
|
|
|
|
iconClassName="h-4 w-4"
|
|
|
|
|
/>
|
|
|
|
|
<!-- Full-page mobile menu. client:media so desktop ships none of
|
|
|
|
|
this JS at all — the island only hydrates below the md breakpoint. -->
|
|
|
|
|
<CurvedMenu
|
|
|
|
|
client:media="(max-width: 767px)"
|
|
|
|
|
items={nav}
|
|
|
|
|
current={current}
|
|
|
|
|
triggerClassName={`${iconClass} md:hidden`}
|
|
|
|
|
/>
|
2026-07-04 22:53:10 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="site-search" data-search-panel class="border-t border-border/60 bg-background text-foreground" hidden>
|
|
|
|
|
<form action="/blog" method="get" class="mx-auto max-w-6xl px-5 py-3">
|
|
|
|
|
<input
|
|
|
|
|
data-search-input
|
|
|
|
|
name="q"
|
|
|
|
|
aria-label="Search essays, field notes, interviews"
|
|
|
|
|
placeholder="Search essays, field notes, interviews..."
|
|
|
|
|
class="w-full border-0 bg-transparent py-2 font-serif text-lg outline-none placeholder:text-muted-foreground"
|
|
|
|
|
/>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
const searchToggle = document.querySelector("[data-search-toggle]");
|
|
|
|
|
const searchPanel = document.querySelector("[data-search-panel]");
|
|
|
|
|
const searchInput = document.querySelector("[data-search-input]");
|
|
|
|
|
const homeHeader = document.querySelector("[data-home-header]");
|
|
|
|
|
|
|
|
|
|
const setPanelOpen = () => {
|
|
|
|
|
if (!homeHeader) return;
|
Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
|
|
|
const open = !searchPanel.hidden;
|
2026-07-04 22:53:10 +00:00
|
|
|
homeHeader.dataset.panelOpen = String(open);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setSearch = (open) => {
|
|
|
|
|
searchPanel.hidden = !open;
|
|
|
|
|
searchToggle?.setAttribute("aria-expanded", String(open));
|
|
|
|
|
if (open) searchInput?.focus();
|
|
|
|
|
setPanelOpen();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const setHeaderScrolled = () => {
|
|
|
|
|
if (!homeHeader) return;
|
|
|
|
|
homeHeader.dataset.scrolled = window.scrollY > 8 ? "true" : "false";
|
|
|
|
|
};
|
|
|
|
|
setHeaderScrolled();
|
|
|
|
|
window.addEventListener("scroll", setHeaderScrolled, { passive: true });
|
|
|
|
|
|
|
|
|
|
searchToggle?.addEventListener("click", () => {
|
|
|
|
|
setSearch(searchPanel.hidden);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener("keydown", (event) => {
|
|
|
|
|
if (event.key !== "Escape") return;
|
|
|
|
|
|
|
|
|
|
if (!searchPanel.hidden) {
|
|
|
|
|
setSearch(false);
|
|
|
|
|
searchToggle?.focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
</script>
|