Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions public/assets/css/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,47 @@ body { padding-top: 0; }
align-items: center;
justify-content: center;
overflow: hidden;
background: #0a0a0a;
background: #0c0817;
}

/* Blurred logo mountains — same technique Lovable uses with their hat */
.hero::before {
content: '';
position: absolute;
left: 50%;
top: 56%;
transform: translate(-50%, -50%);
width: 680px;
height: 680px;
background: url('/assets/img/logo/traverse_icon_512x512.svg') center / contain no-repeat;
filter: blur(72px);
opacity: 0.55;
z-index: 1;
pointer-events: none;
}

.hero-gradient {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 0;
background:
radial-gradient(ellipse 70% 55% at 18% 115%, rgba(255, 130, 20, 0.72) 0%, transparent 52%),
radial-gradient(ellipse 60% 50% at 55% 115%, rgba(210, 30, 120, 0.58) 0%, transparent 52%),
radial-gradient(ellipse 70% 55% at 88% 115%, rgba(110, 10, 220, 0.52) 0%, transparent 52%),
radial-gradient(ellipse 120% 45% at 50% 125%, rgba(60, 0, 160, 0.4) 0%, transparent 58%);
radial-gradient(ellipse 75% 60% at 15% 100%, rgba(255, 130, 20, 0.80) 0%, transparent 50%),
radial-gradient(ellipse 65% 55% at 52% 100%, rgba(210, 30, 120, 0.70) 0%, transparent 50%),
radial-gradient(ellipse 75% 60% at 88% 100%, rgba(110, 10, 220, 0.65) 0%, transparent 50%),
radial-gradient(ellipse 130% 50% at 50% 110%, rgba(60, 0, 160, 0.45) 0%, transparent 55%);
}
.hero-gradient::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to bottom, #0c0817 0%, #0c0817 18%, rgba(12,8,23,0.6) 52%, transparent 72%);
background: linear-gradient(
to bottom,
rgba(12,8,23,0.72) 0%,
rgba(12,8,23,0.45) 30%,
rgba(12,8,23,0.15) 58%,
transparent 78%
);
}
.hero-content {
position: relative;
Expand Down
29 changes: 22 additions & 7 deletions scripts/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ def get_json_ld(html: str) -> str:
return m.group(1).strip() if m else ""


def get_inline_styles(html: str) -> str:
"""Extract inline <style> blocks from <head>."""
styles = re.findall(r'<style[^>]*>(.*?)</style>', html, re.DOTALL)
combined = '\n'.join(s.strip() for s in styles)
return combined.strip()


def get_body_content(html: str) -> str:
"""Extract everything between the end of nav-mobile-menu div and the footer."""
# Find end of nav-mobile-menu
Expand All @@ -91,6 +98,9 @@ def get_body_content(html: str) -> str:
footer_start = html.rfind('</body>')

content = html[nav_end:footer_start].strip()
# Strip old inline breadcrumb divs (replaced by SubpageLayout's Breadcrumb component)
content = re.sub(r'<div\s+class=["\']breadcrumb["\'][^>]*>.*?</div>', '', content, flags=re.DOTALL)
content = re.sub(r'<nav\s+[^>]*aria-label=["\']breadcrumb["\'][^>]*>.*?</nav>', '', content, flags=re.DOTALL)
return content


Expand Down Expand Up @@ -118,8 +128,9 @@ def escape_backtick(s: str) -> str:
return s.replace('`', r'\`').replace('$', r'\$')


def convert_question(html_path: Path, rel_parts: list[str]):
html = html_path.read_text(encoding="utf-8")
def convert_question(html_path: Path, rel_parts: list[str], html: str = None):
if html is None:
html = html_path.read_text(encoding="utf-8")
title = get_tag_text(html, "title")
# Strip the site suffix for display
display_title = re.sub(r'\s*[—–-]\s*Traverse Framework$', '', title).strip()
Expand Down Expand Up @@ -163,8 +174,9 @@ def convert_question(html_path: Path, rel_parts: list[str]):
return out


def convert_subpage(html_path: Path, rel_parts: list[str]):
html = html_path.read_text(encoding="utf-8")
def convert_subpage(html_path: Path, rel_parts: list[str], html: str = None):
if html is None:
html = html_path.read_text(encoding="utf-8")
title = get_tag_text(html, "title")
description = get_description(html)
canonical = get_canonical(html)
Expand All @@ -185,6 +197,9 @@ def convert_subpage(html_path: Path, rel_parts: list[str]):
json_ld_const = f'\nconst _jsonLd = {json.dumps(json_ld, ensure_ascii=False)};' if json_ld else ''
json_ld_prop = '\n jsonLd={_jsonLd}' if json_ld else ''

inline_css = get_inline_styles(html)
style_slot = f'\n <style is:global slot="head">{inline_css}</style>' if inline_css else ''

out = f"""---
import SubpageLayout from '@layouts/SubpageLayout.astro';

Expand All @@ -195,7 +210,7 @@ def convert_subpage(html_path: Path, rel_parts: list[str]):
description={{{json.dumps(description, ensure_ascii=False)}}}
canonical={{{json.dumps(canonical, ensure_ascii=False)}}}{json_ld_prop}
crumbs={crumbs_code}
>
>{style_slot}
<Fragment set:html={{_body}} />
</SubpageLayout>
"""
Expand Down Expand Up @@ -231,9 +246,9 @@ def process_file(html_path: Path):
html = html_path.read_text(encoding="utf-8")

if folder == "questions":
content = convert_question(html_path, folder_parts)
content = convert_question(html_path, folder_parts, html)
else:
content = convert_subpage(html_path, folder_parts)
content = convert_subpage(html_path, folder_parts, html)

out_path.write_text(content, encoding="utf-8")
print(f" ✓ {rel} → src/pages/{'/'.join(folder_parts)}/{stem}.astro")
Expand Down
21 changes: 10 additions & 11 deletions src/layouts/SubpageLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,20 @@ export interface Props {
description: string;
canonical?: string;
jsonLd?: string;
label?: string;
crumbs?: Array<{ label: string; href?: string }>;
}

const { title, description, canonical, jsonLd, label, crumbs = [] } = Astro.props;
const { title, description, canonical, jsonLd, crumbs = [] } = Astro.props;
---
<BaseLayout {title} {description} {canonical} {jsonLd}>
<slot name="head" slot="head" />
<div class="subpage-wrap">
<div class="container">
<div class="subpage-header">
{crumbs.length > 0 && <Breadcrumb crumbs={crumbs} />}
{label && <div class="t-label" style="margin-bottom:0.75rem">{label}</div>}
<slot name="header" />
{crumbs.length > 0 && (
<div class="subpage-breadcrumb container">
<Breadcrumb crumbs={crumbs} />
</div>
<slot />
</div>
)}
<slot />
</div>
</BaseLayout>

Expand All @@ -31,7 +29,8 @@ const { title, description, canonical, jsonLd, label, crumbs = [] } = Astro.prop
padding-top: var(--nav-h);
min-height: 100vh;
}
.subpage-header {
padding: 3.5rem 0 2rem;
.subpage-breadcrumb {
padding-top: 1.25rem;
padding-bottom: 0;
}
</style>
185 changes: 185 additions & 0 deletions src/pages/about.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,190 @@ const _body = "<div class=\"container\">\n <div class=\"about-hero\">\n <spa
canonical={"https://traverse-framework.com/about.html"}
crumbs={[{ label: "Home", href: "/" }]}
>
<style is:global slot="head">body { padding-top: var(--nav-h); }

/* Hero */
.about-hero {
padding: 5rem 0 4rem;
max-width: 720px;
}
.about-hero .t-h1 {
margin: 0.75rem 0 1.5rem;
line-height: 1.15;
}
.about-hero .lead {
font-size: 1.1rem;
color: var(--fg-muted);
line-height: 1.7;
max-width: 600px;
}

/* Layout */
.about-body {
padding-bottom: 5rem;
display: grid;
grid-template-columns: 1fr 320px;
gap: 4rem;
align-items: start;
}
@media (max-width: 900px) {
.about-body { grid-template-columns: 1fr; gap: 3rem; }
}

/* Main prose */
.about-prose { display: flex; flex-direction: column; gap: 3.5rem; }

.prose-section-label {
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 1rem;
}
.prose-section h2 {
font-size: 1.35rem;
font-weight: 600;
color: var(--fg);
margin: 0 0 1rem;
line-height: 1.3;
}
.prose-section p {
color: var(--fg-muted);
font-size: 0.975rem;
line-height: 1.75;
margin: 0 0 0.9rem;
}
.prose-section p:last-child { margin-bottom: 0; }

/* Philosophy blockquote */
.philosophy-block {
border-left: 3px solid var(--accent);
padding: 1.25rem 1.5rem;
background: var(--bg-card);
border-radius: 0 10px 10px 0;
margin: 0;
}
.philosophy-block p {
font-size: 1.15rem !important;
font-weight: 500;
color: var(--fg) !important;
line-height: 1.55 !important;
margin: 0 !important;
}
.philosophy-block cite {
display: block;
margin-top: 0.75rem;
font-size: 0.8rem;
color: var(--fg-subtle);
font-style: normal;
}

/* Work timeline */
.work-list { display: flex; flex-direction: column; gap: 1.25rem; }

.work-item {
display: grid;
grid-template-columns: 72px 1fr;
gap: 1.25rem;
align-items: start;
}
.work-year {
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
color: var(--accent);
padding-top: 0.2rem;
font-weight: 500;
}
.work-content {}
.work-title {
font-size: 0.95rem;
font-weight: 600;
color: var(--fg);
margin: 0 0 0.35rem;
}
.work-desc {
font-size: 0.875rem;
color: var(--fg-muted);
line-height: 1.6;
margin: 0 0 0.5rem;
}
.work-link {
font-size: 0.8rem;
color: var(--accent);
text-decoration: none;
}
.work-link:hover { text-decoration: underline; }

/* Sidebar */
.about-sidebar { display: flex; flex-direction: column; gap: 1.5rem; }

.sidebar-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
}
.sidebar-card-title {
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--fg-subtle);
margin-bottom: 1rem;
}
.sidebar-stat {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border);
font-size: 0.875rem;
}
.sidebar-stat:last-child { border-bottom: none; padding-bottom: 0; }
.sidebar-stat-label { color: var(--fg-muted); }
.sidebar-stat-value { color: var(--fg); font-weight: 500; font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; }

.sidebar-links { display: flex; flex-direction: column; gap: 0.5rem; }
.sidebar-link {
display: flex;
align-items: center;
gap: 0.6rem;
font-size: 0.875rem;
color: var(--fg-muted);
text-decoration: none;
padding: 0.5rem 0.75rem;
border-radius: 7px;
transition: background 0.15s, color 0.15s;
}
.sidebar-link:hover { background: var(--bg-elevated); color: var(--fg); }
.sidebar-link-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
color: var(--fg-subtle);
}

/* Status bar */
.status-row {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.8rem;
color: var(--fg-muted);
}
.status-dot {
width: 7px;
height: 7px;
background: var(--success);
border-radius: 50%;
flex-shrink: 0;
box-shadow: 0 0 6px var(--success);
animation: pulse-dot 2.5s ease infinite;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}</style>
<Fragment set:html={_body} />
</SubpageLayout>
Loading
Loading