2
0
Files
gitcaddy-server/templates/pages/open-source-hero.tmpl
logikonline c5e35e3466 refactor(pages): move app store badges to hero section and add section IDs
Move Google Play and App Store badges from downloads section to hero section for better visibility. Removes redundant display when both downloads and app stores are present. Add id attributes to major sections (hero, stats, downloads, social-proof, cta, etc.) for anchor link navigation. Applies to all 9 page templates. Improves UX for mobile app landing pages.
2026-03-17 03:25:14 -04:00

1559 lines
48 KiB
Handlebars

{{template "pages/base_head" .}}
<style>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=IBM+Plex+Sans:wght@300;400;500;600;700&display=swap');
:root {
--osh-bg: #0a0a0b;
--osh-surface: #111113;
--osh-elevated: #18181b;
--osh-text: #e4e4e7;
--osh-muted: #71717a;
--osh-dim: #3f3f46;
--osh-accent: {{if .Config.Theme.PrimaryColor}}{{.Config.Theme.PrimaryColor}}{{else}}#22c55e{{end}};
--osh-accent-dark: {{if .Config.Theme.AccentColor}}{{.Config.Theme.AccentColor}}{{else}}#16a34a{{end}};
--osh-glow: color-mix(in srgb, var(--osh-accent) 15%, transparent);
--osh-glow-strong: color-mix(in srgb, var(--osh-accent) 40%, transparent);
}
html {
scroll-behavior: smooth;
}
html, body.pages-body {
overflow-x: hidden;
background: var(--osh-bg) !important;
}
/* Scan line overlay for terminal feel */
.osh-page::before {
content: "";
position: fixed;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.03) 2px,
rgba(0, 0, 0, 0.03) 4px
);
pointer-events: none;
z-index: 9999;
}
/* Dot grid pattern */
.osh-page::after {
content: "";
position: fixed;
inset: 0;
background-image: radial-gradient(circle, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 20px 20px;
pointer-events: none;
z-index: 0;
}
.osh-page {
position: relative;
min-height: 100vh;
background: var(--osh-bg);
color: var(--osh-text);
font-family: 'IBM Plex Sans', -apple-system, sans-serif;
-webkit-font-smoothing: antialiased;
}
/* Reveal animation system */
.osh-reveal {
opacity: 0;
transform: translateY(32px);
transition: opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1), transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
.osh-reveal.visible {
opacity: 1;
transform: translateY(0);
}
.osh-reveal-delay-1 { transition-delay: 0.1s; }
.osh-reveal-delay-2 { transition-delay: 0.2s; }
.osh-reveal-delay-3 { transition-delay: 0.3s; }
.osh-reveal-delay-4 { transition-delay: 0.4s; }
/* Hero gradient with phosphor glow */
.osh-hero-gradient {
position: absolute;
top: -40%;
left: 50%;
transform: translateX(-50%);
width: 140%;
height: 100%;
background: radial-gradient(ellipse at center, var(--osh-glow) 0%, transparent 55%);
pointer-events: none;
animation: osh-breathe 8s ease-in-out infinite;
}
@keyframes osh-breathe {
0%, 100% { opacity: 0.6; transform: translateX(-50%) scale(1); }
50% { opacity: 1; transform: translateX(-50%) scale(1.05); }
}
/* Navigation */
.osh-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 0 40px;
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(10, 10, 11, 0.85);
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
border-bottom: 1px solid rgba(255,255,255,0.04);
z-index: 100;
transition: background 0.3s ease;
}
.osh-nav-brand {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: inherit;
}
.osh-nav-logo {
width: 30px;
height: 30px;
background: var(--osh-accent);
border-radius: 7px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px var(--osh-glow);
transition: box-shadow 0.3s ease;
}
.osh-nav-brand:hover .osh-nav-logo {
box-shadow: 0 0 30px var(--osh-glow-strong);
}
.osh-nav-name {
font-family: 'IBM Plex Mono', monospace;
font-weight: 600;
font-size: 16px;
letter-spacing: -0.02em;
}
.osh-nav-links {
display: flex;
align-items: center;
gap: 8px;
}
.osh-nav-link {
color: var(--osh-muted);
text-decoration: none;
font-size: 13px;
font-weight: 500;
padding: 8px 14px;
border-radius: 6px;
transition: all 0.2s ease;
letter-spacing: 0.01em;
}
.osh-nav-link:hover {
color: var(--osh-text);
background: rgba(255,255,255,0.04);
}
.osh-nav-cta {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
color: var(--osh-text);
font-size: 13px;
font-weight: 500;
border-radius: 6px;
text-decoration: none;
transition: all 0.2s ease;
}
.osh-nav-cta:hover {
background: rgba(255,255,255,0.1);
border-color: rgba(255,255,255,0.15);
}
/* Mobile menu toggle */
.osh-menu-toggle {
display: none;
background: none;
border: none;
color: var(--osh-muted);
cursor: pointer;
padding: 8px;
}
/* Buttons */
.osh-btn-primary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
background: var(--osh-accent);
color: #000;
font-weight: 600;
font-size: 14px;
border-radius: 8px;
text-decoration: none;
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
border: none;
cursor: pointer;
letter-spacing: 0.01em;
box-shadow: 0 0 0 0 var(--osh-glow);
}
.osh-btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 32px var(--osh-glow-strong), 0 0 0 1px var(--osh-accent);
}
.osh-btn-secondary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
background: transparent;
color: var(--osh-text);
font-weight: 500;
font-size: 14px;
border-radius: 8px;
text-decoration: none;
border: 1px solid var(--osh-dim);
transition: all 0.25s ease;
}
.osh-btn-secondary:hover {
background: rgba(255,255,255,0.04);
border-color: rgba(255,255,255,0.2);
}
/* Hero Section */
.osh-hero {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 140px 40px 100px;
text-align: center;
}
.osh-hero-content {
position: relative;
z-index: 1;
max-width: 800px;
}
/* Terminal-style version badge */
.osh-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
background: color-mix(in srgb, var(--osh-accent) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--osh-accent) 15%, transparent);
border-radius: 6px;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
font-weight: 500;
color: var(--osh-accent);
margin-bottom: 32px;
letter-spacing: 0.02em;
}
.osh-badge-dot {
width: 6px;
height: 6px;
background: var(--osh-accent);
border-radius: 50%;
animation: osh-pulse 2s ease-in-out infinite;
box-shadow: 0 0 8px var(--osh-glow-strong);
}
@keyframes osh-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.osh-hero h1 {
font-size: clamp(40px, 6vw, 68px);
font-weight: 600;
line-height: 1.08;
margin-bottom: 24px;
letter-spacing: -0.035em;
background: linear-gradient(180deg, #ffffff 0%, #a1a1aa 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.osh-hero-sub {
font-size: clamp(16px, 2vw, 20px);
color: #a1a1aa;
line-height: 1.65;
margin-bottom: 40px;
max-width: 560px;
margin-left: auto;
margin-right: auto;
font-weight: 300;
}
.osh-hero-ctas {
display: flex;
gap: 12px;
justify-content: center;
margin-bottom: 48px;
}
/* Terminal code block */
.osh-code-block {
display: inline-flex;
align-items: center;
gap: 16px;
padding: 16px 20px;
background: var(--osh-surface);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 10px;
font-family: 'IBM Plex Mono', monospace;
font-size: 13px;
max-width: 480px;
margin: 0 auto;
position: relative;
overflow: hidden;
}
/* Subtle left accent bar */
.osh-code-block::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--osh-accent);
border-radius: 3px 0 0 3px;
}
.osh-code-prompt {
color: var(--osh-accent);
font-weight: 500;
user-select: none;
}
.osh-code-block code {
color: var(--osh-text);
flex: 1;
text-align: left;
}
.osh-copy-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 6px;
color: var(--osh-muted);
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
}
.osh-copy-btn:hover {
background: rgba(255,255,255,0.08);
color: var(--osh-text);
}
/* Stats section with monospace numbers */
.osh-stats {
padding: 64px 40px;
border-top: 1px solid rgba(255,255,255,0.04);
border-bottom: 1px solid rgba(255,255,255,0.04);
background: var(--osh-surface);
}
.osh-stats-inner {
display: flex;
justify-content: center;
align-items: center;
max-width: 800px;
margin: 0 auto;
flex-wrap: wrap;
gap: 0;
}
.osh-stat-item {
text-align: center;
padding: 0 40px;
position: relative;
}
.osh-stat-item:not(:last-child)::after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 40px;
background: rgba(255,255,255,0.06);
}
.osh-stat-value {
font-family: 'IBM Plex Mono', monospace;
font-size: 28px;
font-weight: 600;
color: var(--osh-text);
letter-spacing: -0.02em;
}
.osh-stat-label {
font-size: 12px;
color: var(--osh-muted);
margin-top: 6px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 500;
}
/* Downloads section */
.osh-downloads {
padding: 80px 40px;
position: relative;
}
.osh-downloads-inner {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
.osh-downloads h2 {
font-size: clamp(24px, 3vw, 32px);
font-weight: 600;
margin-bottom: 8px;
letter-spacing: -0.025em;
}
.osh-downloads p {
color: var(--osh-muted);
margin-bottom: 24px;
font-size: 15px;
}
.osh-downloads-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.osh-download-item {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 8px;
color: var(--osh-text);
text-decoration: none;
font-size: 14px;
transition: all 0.2s;
}
.osh-download-item:hover {
background: rgba(255,255,255,0.08);
border-color: var(--osh-accent);
}
.osh-download-size {
font-size: 12px;
color: var(--osh-muted);
}
/* Feature cards */
.osh-features {
padding: 120px 40px;
position: relative;
}
.osh-features-inner {
max-width: 1100px;
margin: 0 auto;
}
.osh-section-label {
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
font-weight: 500;
color: var(--osh-accent);
text-transform: uppercase;
letter-spacing: 0.12em;
margin-bottom: 16px;
text-align: center;
}
.osh-section-header {
text-align: center;
margin-bottom: 64px;
}
.osh-section-header h2 {
font-size: clamp(28px, 4vw, 42px);
font-weight: 600;
margin-bottom: 16px;
letter-spacing: -0.025em;
}
.osh-section-header p {
font-size: 17px;
color: var(--osh-muted);
max-width: 480px;
margin: 0 auto;
font-weight: 300;
}
.osh-features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.osh-feature-card {
padding: 32px;
background: var(--osh-surface);
border: 1px solid rgba(255,255,255,0.04);
border-radius: 12px;
transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1);
position: relative;
overflow: hidden;
}
.osh-feature-card::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--osh-accent), transparent);
opacity: 0;
transition: opacity 0.35s ease;
}
.osh-feature-card:hover {
background: var(--osh-elevated);
border-color: rgba(255,255,255,0.08);
transform: translateY(-4px);
}
.osh-feature-card:hover::before {
opacity: 0.6;
}
.osh-feature-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--osh-accent) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--osh-accent) 12%, transparent);
border-radius: 8px;
color: var(--osh-accent);
margin-bottom: 20px;
}
.osh-feature-title {
font-size: 16px;
font-weight: 600;
color: #fafafa;
margin-bottom: 8px;
letter-spacing: -0.01em;
}
.osh-feature-desc {
font-size: 14px;
color: #a1a1aa;
line-height: 1.6;
font-weight: 300;
}
/* Social proof */
.osh-social-proof {
padding: 100px 40px;
background: var(--osh-surface);
border-top: 1px solid rgba(255,255,255,0.04);
border-bottom: 1px solid rgba(255,255,255,0.04);
}
.osh-social-proof-inner {
max-width: 1100px;
margin: 0 auto;
}
.osh-logos {
display: flex;
justify-content: center;
gap: 48px;
margin-bottom: 64px;
flex-wrap: wrap;
}
.osh-logo-item {
padding: 10px 20px;
font-family: 'IBM Plex Mono', monospace;
font-size: 13px;
font-weight: 500;
color: var(--osh-dim);
letter-spacing: 0.02em;
transition: color 0.3s ease;
}
.osh-logo-item:hover {
color: var(--osh-muted);
}
.osh-testimonial {
background: var(--osh-bg);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 16px;
padding: 48px;
max-width: 680px;
margin: 0 auto;
position: relative;
}
.osh-testimonial::before {
content: "";
position: absolute;
inset: -1px;
border-radius: 16px;
background: linear-gradient(135deg, color-mix(in srgb, var(--osh-accent) 10%, transparent), transparent 60%);
z-index: -1;
pointer-events: none;
}
.osh-testimonial-quote {
font-size: 20px;
line-height: 1.65;
color: #fafafa;
margin-bottom: 28px;
font-weight: 300;
font-style: italic;
}
.osh-testimonial-author {
font-weight: 600;
color: var(--osh-text);
font-size: 15px;
}
.osh-testimonial-role {
font-size: 13px;
color: var(--osh-muted);
}
/* README */
.osh-readme {
padding: 100px 40px;
max-width: 900px;
margin: 0 auto;
}
.osh-readme-content {
background: var(--osh-surface);
border: 1px solid rgba(255,255,255,0.04);
border-radius: 12px;
padding: 48px;
}
/* Pricing */
.osh-pricing {
padding: 120px 40px;
}
.osh-pricing-inner {
max-width: 1100px;
margin: 0 auto;
}
.osh-pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
margin-top: 64px;
}
.osh-pricing-card {
background: var(--osh-surface);
border: 1px solid rgba(255,255,255,0.04);
border-radius: 14px;
padding: 36px;
position: relative;
transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.osh-pricing-card:hover {
border-color: rgba(255,255,255,0.1);
transform: translateY(-4px);
}
.osh-pricing-card.featured {
border-color: color-mix(in srgb, var(--osh-accent) 25%, transparent);
background: linear-gradient(180deg, color-mix(in srgb, var(--osh-accent) 4%, transparent) 0%, var(--osh-surface) 100%);
}
.osh-pricing-badge {
position: absolute;
top: -11px;
left: 50%;
transform: translateX(-50%);
padding: 5px 14px;
background: var(--osh-accent);
color: #000;
font-family: 'IBM Plex Mono', monospace;
font-size: 11px;
font-weight: 600;
border-radius: 6px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.osh-pricing-name {
font-size: 18px;
font-weight: 600;
color: #fafafa;
margin-bottom: 8px;
}
.osh-pricing-price {
font-family: 'IBM Plex Mono', monospace;
font-size: 44px;
font-weight: 600;
color: var(--osh-text);
line-height: 1;
margin-bottom: 4px;
letter-spacing: -0.03em;
}
.osh-pricing-period {
font-size: 13px;
color: var(--osh-muted);
margin-bottom: 28px;
}
.osh-pricing-features {
list-style: none;
padding: 0;
margin: 0 0 32px 0;
}
.osh-pricing-features li {
padding: 10px 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
font-size: 13px;
color: #a1a1aa;
display: flex;
align-items: center;
gap: 10px;
}
.osh-pricing-features li::before {
content: "→";
font-family: 'IBM Plex Mono', monospace;
color: var(--osh-accent);
font-size: 12px;
flex-shrink: 0;
}
.osh-pricing-features li:last-child {
border-bottom: none;
}
.osh-pricing-cta {
display: block;
width: 100%;
padding: 13px 24px;
text-align: center;
background: rgba(255,255,255,0.04);
color: var(--osh-text);
font-weight: 600;
font-size: 13px;
border-radius: 8px;
text-decoration: none;
border: 1px solid rgba(255,255,255,0.06);
transition: all 0.25s ease;
letter-spacing: 0.01em;
}
.osh-pricing-cta:hover {
background: rgba(255,255,255,0.08);
border-color: rgba(255,255,255,0.12);
}
.osh-pricing-card.featured .osh-pricing-cta {
background: var(--osh-accent);
color: #000;
border: none;
}
.osh-pricing-card.featured .osh-pricing-cta:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px var(--osh-glow-strong);
}
/* CTA section */
.osh-cta-section {
padding: 120px 40px;
text-align: center;
position: relative;
}
.osh-cta-section::before {
content: "";
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120%;
height: 60%;
background: radial-gradient(ellipse at center bottom, var(--osh-glow) 0%, transparent 60%);
pointer-events: none;
}
.osh-cta-inner {
max-width: 600px;
margin: 0 auto;
position: relative;
z-index: 1;
}
.osh-cta-section h2 {
font-size: clamp(28px, 4vw, 44px);
font-weight: 600;
margin-bottom: 16px;
letter-spacing: -0.025em;
}
.osh-cta-section p {
font-size: 17px;
color: var(--osh-muted);
margin-bottom: 36px;
font-weight: 300;
}
/* Footer */
.osh-footer {
padding: 32px 40px;
border-top: 1px solid rgba(255,255,255,0.04);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 24px;
}
.osh-footer-brand {
display: flex;
align-items: center;
gap: 8px;
}
.osh-footer-logo {
width: 22px;
height: 22px;
background: color-mix(in srgb, var(--osh-accent) 15%, transparent);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}
.osh-footer-text {
font-size: 13px;
color: var(--osh-dim);
}
.osh-footer-links {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.osh-footer-link {
color: var(--osh-muted);
text-decoration: none;
font-size: 13px;
transition: color 0.2s ease;
}
.osh-footer-link:hover {
color: var(--osh-text);
}
.osh-footer-social {
display: flex;
gap: 12px;
}
.osh-footer-social a {
color: var(--osh-dim);
transition: color 0.2s ease;
padding: 4px;
}
.osh-footer-social a:hover {
color: var(--osh-text);
}
/* Responsive */
@media (max-width: 768px) {
.osh-nav { padding: 0 20px; }
.osh-nav-links { display: none; }
.osh-menu-toggle { display: flex; }
.osh-hero { padding: 120px 24px 80px; }
.osh-hero-ctas { flex-direction: column; align-items: center; }
.osh-btn-primary, .osh-btn-secondary { width: 100%; justify-content: center; }
.osh-features, .osh-social-proof, .osh-cta-section, .osh-pricing { padding: 80px 24px; }
.osh-stats { padding: 48px 24px; }
.osh-stat-item { padding: 16px 24px; }
.osh-stat-item:not(:last-child)::after { display: none; }
.osh-stats-inner { flex-direction: column; }
.osh-features-grid { grid-template-columns: 1fr; }
.osh-footer { flex-direction: column; text-align: center; padding: 24px; }
.osh-testimonial { padding: 32px 24px; }
.osh-code-block { font-size: 12px; }
}
@media (max-width: 480px) {
.osh-hero h1 { font-size: 32px; }
.osh-section-header h2 { font-size: 28px; }
.osh-pricing-price { font-size: 36px; }
}
/* Blog content markdown styles */
.osh-blog-content h1, .osh-blog-content h2, .osh-blog-content h3, .osh-blog-content h4 {
color: var(--osh-text); font-family: 'IBM Plex Sans', sans-serif; margin: 1.5em 0 0.5em;
}
.osh-blog-content h2 { font-size: 1.5em; border-bottom: 1px solid var(--osh-dim); padding-bottom: 8px; }
.osh-blog-content h3 { font-size: 1.25em; }
.osh-blog-content a { color: var(--osh-accent); text-decoration: underline; }
.osh-blog-content a:hover { opacity: 0.8; }
.osh-blog-content code { background: var(--osh-elevated); padding: 2px 6px; border-radius: 4px; font-family: 'IBM Plex Mono', monospace; font-size: 0.9em; }
.osh-blog-content pre { background: var(--osh-surface); border: 1px solid var(--osh-dim); border-radius: 8px; padding: 16px; overflow-x: auto; margin: 16px 0; }
.osh-blog-content pre code { background: none; padding: 0; }
.osh-blog-content blockquote { border-left: 3px solid var(--osh-accent); padding-left: 16px; color: var(--osh-muted); margin: 16px 0; font-style: italic; }
.osh-blog-content img { max-width: 100%; border-radius: 8px; margin: 16px 0; }
.osh-blog-content ul, .osh-blog-content ol { padding-left: 24px; margin: 12px 0; }
.osh-blog-content li { margin: 4px 0; }
.osh-blog-content table { border-collapse: collapse; width: 100%; margin: 16px 0; }
.osh-blog-content th, .osh-blog-content td { border: 1px solid var(--osh-dim); padding: 8px 12px; text-align: left; }
.osh-blog-content th { background: var(--osh-surface); font-weight: 600; }
.osh-blog-content hr { border: none; border-top: 1px solid var(--osh-dim); margin: 24px 0; }
</style>
<div class="osh-page">
<!-- Navigation -->
<nav class="osh-nav">
<a href="{{.LandingURL}}" class="osh-nav-brand">
{{if .LogoURL}}
<img src="{{.LogoURL}}" alt="{{.Config.Brand.Name}}" style="height: 30px; border-radius: 4px;">
{{else}}
<div class="osh-nav-logo">
{{svg "octicon-zap" 14}}
</div>
{{end}}
<span class="osh-nav-name">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}</span>
</a>
<div class="osh-nav-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="osh-nav-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="osh-nav-link">Docs</a>{{end}}
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="osh-nav-link">API</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-nav-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-nav-link">Issues</a>{{end}}
{{if .Config.ValueProps}}<a href="{{.LandingURL}}#value-props" class="osh-nav-link">Why Us</a>{{end}}
{{if .Config.Features}}<a href="{{.LandingURL}}#features" class="osh-nav-link">Features</a>{{end}}
{{if .Config.Pricing.Plans}}<a href="{{.LandingURL}}#pricing" class="osh-nav-link">Pricing</a>{{end}}
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}{{.LandingURL}}#blog{{end}}" class="osh-nav-link">Blog</a>{{end}}
{{if .Config.Gallery.Enabled}}<a href="{{.LandingURL}}#gallery" class="osh-nav-link">Gallery</a>{{end}}
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}<a href="{{.LandingURL}}#comparison" class="osh-nav-link">Compare</a>{{end}}
{{if .Config.Navigation.ShowRepository}}
<a href="{{.RepoURL}}" class="osh-nav-cta">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
Repository
</a>
{{end}}
{{if .LangSwitcherEnabled}}
<div class="pages-lang-switcher">
<button class="pages-lang-btn" onclick="this.nextElementSibling.classList.toggle('open')">
{{svg "octicon-globe" 14}} {{index $.LanguageNames .ActiveLang}}
</button>
<div class="pages-lang-dropdown">
{{range .AvailableLanguages}}
<a href="?lang={{.}}" class="pages-lang-option{{if eq . $.ActiveLang}} active{{end}}">{{index $.LanguageNames .}}</a>
{{end}}
</div>
</div>
{{end}}
</div>
<button class="osh-menu-toggle" onclick="this.parentElement.querySelector('.osh-nav-links').style.display=this.parentElement.querySelector('.osh-nav-links').style.display==='flex'?'none':'flex'">
{{svg "octicon-three-bars" 20}}
</button>
</nav>
{{if .PageIsBlogDetail}}
<!-- Blog Detail View -->
<section class="osh-hero" style="padding-top: 120px; min-height: auto;">
<div class="osh-hero-content" style="max-width: 800px;">
{{if .BlogPost.FeaturedImage}}
<div style="margin: 0 auto 32px; border-radius: 12px; overflow: hidden;">
<img src="{{.BlogPost.FeaturedImage.DownloadURL}}" alt="{{.BlogPost.Title}}" style="width: 100%; max-height: 400px; object-fit: cover; display: block;">
</div>
{{end}}
<h1 style="font-size: 36px; margin-bottom: 8px;">{{.BlogPost.Title}}</h1>
{{if .BlogPost.Subtitle}}<p style="font-size: 1.2rem; color: var(--osh-muted); margin-bottom: 24px;">{{.BlogPost.Subtitle}}</p>{{end}}
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 48px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace;">
{{if .BlogPost.Author}}<span>{{.BlogPost.Author.DisplayName}}</span><span>&middot;</span>{{end}}
<span>{{DateUtils.AbsoluteShort .BlogPost.CreatedUnix}}</span>
{{if .BlogTags}}<span>&middot;</span>{{range .BlogTags}}<span style="background: var(--osh-glow); padding: 2px 8px; border-radius: 4px; font-size: 12px;">{{.}}</span> {{end}}{{end}}
</div>
<div class="markup osh-blog-content" style="color: var(--osh-text); line-height: 1.8; font-size: 18px; text-align: left;">
{{.BlogRenderedContent}}
</div>
<div style="margin-top: 48px; padding-top: 24px; border-top: 1px solid rgba(255,255,255,0.06);">
<a href="{{.BlogBaseURL}}" class="osh-btn-secondary" data-cta="secondary" style="text-decoration: none;">
{{svg "octicon-arrow-left" 16}} Back to Blog
</a>
</div>
</div>
</section>
{{else if .PageIsBlogList}}
<!-- Blog List View -->
<section class="osh-features" style="padding-top: 120px;">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Blog</div>
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}All Posts{{end}}</h2>
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
</div>
<div class="osh-features-grid">
{{range .BlogListPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="osh-feature-card osh-reveal" style="text-decoration: none; color: inherit; display: flex; flex-direction: column;">
{{if .FeaturedImage}}
<div style="margin: -32px -32px 20px -32px; overflow: hidden; border-radius: 12px 12px 0 0;">
<img src="{{.FeaturedImage.DownloadURL}}" alt="{{.Title}}" style="width: 100%; height: 180px; object-fit: cover; display: block;">
</div>
{{end}}
<h3 class="osh-feature-title">{{.Title}}</h3>
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}
<p class="osh-feature-desc">{{.Subtitle}}</p>
{{end}}
<div style="margin-top: auto; padding-top: 16px; font-size: 12px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace;">
{{if .Author}}{{.Author.DisplayName}} &middot; {{end}}{{DateUtils.AbsoluteShort .CreatedUnix}}
</div>
</a>
{{end}}
</div>
{{if gt .BlogListTotal 9}}
<div style="text-align: center; margin-top: 48px;">
{{template "base/paginate" .}}
</div>
{{end}}
</div>
</section>
{{else}}
<!-- Hero Section -->
<section class="osh-hero" id="hero">
<div class="osh-hero-gradient"></div>
<div class="osh-hero-content">
<div class="osh-badge osh-reveal visible">
<span class="osh-badge-dot"></span>
{{if .LatestRelease}}v{{.LatestReleaseTag}} released{{else}}Open Source{{end}}
</div>
<h1 class="osh-reveal visible osh-reveal-delay-1">{{if .Config.Hero.Headline}}{{.Config.Hero.Headline}}{{else}}{{.Repository.Name}}{{end}}</h1>
<p class="osh-hero-sub osh-reveal visible osh-reveal-delay-2">
{{if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}
</p>
<div class="osh-hero-ctas osh-reveal visible osh-reveal-delay-3">
{{if .Config.Hero.PrimaryCTA.Label}}
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="osh-btn-primary" data-cta="primary">
{{.Config.Hero.PrimaryCTA.Label}}
{{svg "octicon-arrow-right" 16}}
</a>
{{end}}
{{if .Config.Hero.SecondaryCTA.Label}}
<a href="{{.Config.Hero.SecondaryCTA.URL}}" class="osh-btn-secondary" data-cta="secondary">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
{{.Config.Hero.SecondaryCTA.Label}}
</a>
{{end}}
</div>
{{if or $.GooglePlayID $.AppStoreID}}
<div style="display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 16px;" class="osh-reveal visible osh-reveal-delay-4">
{{if $.GooglePlayID}}
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M3.609 1.814L13.792 12 3.61 22.186a.996.996 0 0 1-.61-.92V2.734a1 1 0 0 1 .609-.92zm10.89 10.893l2.302 2.302-10.937 6.333 8.635-8.635zm3.199-1.4l2.834 1.64a1 1 0 0 1 0 1.726l-2.834 1.64-2.635-2.636 2.635-2.37zM5.864 2.658L16.8 9.99l-2.302 2.302-8.635-8.635z"/></svg>
Google Play
</a>
{{end}}
{{if $.AppStoreID}}
<a href="https://apps.apple.com/app/{{$.AppStoreID}}" target="_blank" rel="noopener" class="osh-download-item" style="display: inline-flex; align-items: center; gap: 10px;">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
App Store
</a>
{{end}}
</div>
{{end}}
{{if .Config.Hero.CodeExample}}
<div class="osh-code-block osh-reveal visible osh-reveal-delay-4">
<span class="osh-code-prompt">$</span>
<code id="install-cmd">{{.Config.Hero.CodeExample}}</code>
<button class="osh-copy-btn" onclick="navigator.clipboard.writeText(document.getElementById('install-cmd').textContent)">
{{svg "octicon-copy" 14}}
</button>
</div>
{{end}}
</div>
</section>
<!-- Stats Section -->
{{if or .Config.Stats (gt .NumStars 0)}}
<section class="osh-stats" id="stats">
<div class="osh-stats-inner osh-reveal">
{{if .Config.Stats}}
{{range .Config.Stats}}
<div class="osh-stat-item">
<div class="osh-stat-value">{{.Value}}</div>
<div class="osh-stat-label">{{.Label}}</div>
</div>
{{end}}
{{else}}
<div class="osh-stat-item">
<div class="osh-stat-value">{{.NumStars}}</div>
<div class="osh-stat-label">Stars</div>
</div>
<div class="osh-stat-item">
<div class="osh-stat-value">{{.NumForks}}</div>
<div class="osh-stat-label">Forks</div>
</div>
{{if .LatestRelease}}
<div class="osh-stat-item">
<div class="osh-stat-value">v{{.LatestReleaseTag}}</div>
<div class="osh-stat-label">Latest</div>
</div>
{{end}}
{{end}}
</div>
</section>
{{end}}
<!-- Downloads Section -->
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
<section class="osh-downloads" id="downloads">
<div class="osh-downloads-inner osh-reveal">
<h2>Download v{{.LatestReleaseTag}}</h2>
<p>Get the latest release</p>
{{$windowsFiles := newSlice}}{{$macosFiles := newSlice}}{{$linuxFiles := newSlice}}{{$androidFiles := newSlice}}{{$iosFiles := newSlice}}{{$otherFiles := newSlice}}
{{range $att := .LatestRelease.Attachments}}
{{$name := StringUtils.ToLower $att.Name}}
{{if or (StringUtils.Contains $name "android") (StringUtils.HasSuffix $name ".apk") (StringUtils.HasSuffix $name ".aab") (StringUtils.HasSuffix $name ".xapk")}}
{{$androidFiles = Append $androidFiles $att}}
{{else if or (StringUtils.Contains $name "ios") (StringUtils.Contains $name "iphone") (StringUtils.Contains $name "ipad") (StringUtils.HasSuffix $name ".ipa")}}
{{$iosFiles = Append $iosFiles $att}}
{{else if or (StringUtils.Contains $name "windows") (StringUtils.Contains $name "win64") (StringUtils.Contains $name "win32") (StringUtils.Contains $name "-win.") (StringUtils.Contains $name "_win.") (StringUtils.Contains $name "-win-") (StringUtils.Contains $name "_win_") (StringUtils.HasSuffix $name ".exe") (StringUtils.HasSuffix $name ".msi") (StringUtils.HasSuffix $name ".msix") (StringUtils.HasSuffix $name ".msixbundle") (StringUtils.HasSuffix $name ".appx") (StringUtils.HasSuffix $name ".appxbundle")}}
{{$windowsFiles = Append $windowsFiles $att}}
{{else if or (StringUtils.Contains $name "darwin") (StringUtils.Contains $name "macos") (StringUtils.Contains $name "-mac.") (StringUtils.Contains $name "_mac.") (StringUtils.Contains $name "-mac-") (StringUtils.Contains $name "_mac_") (StringUtils.Contains $name "osx") (StringUtils.HasSuffix $name ".dmg") (StringUtils.HasSuffix $name ".pkg")}}
{{$macosFiles = Append $macosFiles $att}}
{{else if or (StringUtils.Contains $name "linux") (StringUtils.Contains $name "-lin.") (StringUtils.Contains $name "_lin.") (StringUtils.Contains $name "-lin-") (StringUtils.Contains $name "_lin_") (StringUtils.HasSuffix $name ".deb") (StringUtils.HasSuffix $name ".rpm") (StringUtils.HasSuffix $name ".appimage") (StringUtils.HasSuffix $name ".flatpak") (StringUtils.HasSuffix $name ".snap")}}
{{$linuxFiles = Append $linuxFiles $att}}
{{else}}
{{$otherFiles = Append $otherFiles $att}}
{{end}}
{{end}}
{{if $windowsFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-device-desktop" 16}} Windows</h4>
<div class="osh-downloads-grid">
{{range $windowsFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{if $macosFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-device-desktop" 16}} macOS</h4>
<div class="osh-downloads-grid">
{{range $macosFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{if $linuxFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-terminal" 16}} Linux</h4>
<div class="osh-downloads-grid">
{{range $linuxFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{if not $.HideMobileReleases}}
{{if $androidFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} Android</h4>
<div class="osh-downloads-grid">
{{range $androidFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{if $iosFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-device-mobile" 16}} iOS</h4>
<div class="osh-downloads-grid">
{{range $iosFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{end}}
{{if $otherFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace; letter-spacing: 0.05em; text-transform: uppercase;">{{svg "octicon-file" 16}} Other</h4>
<div class="osh-downloads-grid">
{{range $otherFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="osh-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="osh-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
</div>
</section>
{{end}}
<!-- Value Props Section -->
{{if .Config.ValueProps}}
<section class="osh-features" id="value-props">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Why choose us</div>
<h2>{{if .Config.Brand.Name}}Why {{.Config.Brand.Name}}?{{else}}Why Choose Us{{end}}</h2>
<p>Everything you need to get started quickly.</p>
</div>
<div class="osh-features-grid">
{{range .Config.ValueProps}}
<div class="osh-feature-card osh-reveal">
<div class="osh-feature-icon">
{{svg (printf "octicon-%s" (or .Icon "check")) 20}}
</div>
<h3 class="osh-feature-title">{{.Title}}</h3>
<p class="osh-feature-desc">{{.Description}}</p>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- Features Section -->
{{if .Config.Features}}
<section class="osh-features" id="features" style="padding-top: 40px;">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Capabilities</div>
<h2>Features</h2>
<p>Powerful capabilities at your fingertips.</p>
</div>
<div class="osh-features-grid">
{{range .Config.Features}}
<div class="osh-feature-card osh-reveal">
<div class="osh-feature-icon">
{{svg (printf "octicon-%s" (or .Icon "zap")) 20}}
</div>
<h3 class="osh-feature-title">{{.Title}}</h3>
<p class="osh-feature-desc">{{.Description}}</p>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- Social Proof -->
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
<section class="osh-social-proof" id="social-proof">
<div class="osh-social-proof-inner">
{{if .Config.SocialProof.Logos}}
<div class="osh-logos osh-reveal">
{{range .Config.SocialProof.Logos}}
<div class="osh-logo-item">{{.}}</div>
{{end}}
</div>
{{end}}
{{if .Config.SocialProof.Testimonials}}
<div class="osh-testimonials-container">
{{range .Config.SocialProof.Testimonials}}
<div class="osh-testimonial osh-reveal" style="display: none;">
<p class="osh-testimonial-quote">"{{.Quote}}"</p>
<div>
<div class="osh-testimonial-author">{{.Author}}</div>
<div class="osh-testimonial-role">{{.Role}}</div>
</div>
</div>
{{end}}
</div>
<script>
(function() {
var testimonials = document.querySelectorAll(".osh-testimonial");
if (testimonials.length > 0) {
var idx = Math.floor(Math.random() * testimonials.length);
testimonials[idx].style.display = "block";
}
})();
</script>
{{end}}
</div>
</section>
{{end}}
<!-- Pricing Section -->
{{if .Config.Pricing.Plans}}
<section class="osh-pricing" id="pricing">
<div class="osh-pricing-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Pricing</div>
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Pricing{{end}}</h2>
<p>{{if .Config.Pricing.Subheadline}}{{.Config.Pricing.Subheadline}}{{else}}Choose the plan that works for you{{end}}</p>
</div>
<div class="osh-pricing-grid">
{{range .Config.Pricing.Plans}}
<div class="osh-pricing-card{{if .Featured}} featured{{end}} osh-reveal">
{{if .Featured}}<span class="osh-pricing-badge">Popular</span>{{end}}
<div class="osh-pricing-name">{{.Name}}</div>
<div class="osh-pricing-price">{{.Price}}</div>
<div class="osh-pricing-period">{{.Period}}</div>
{{if .Features}}
<ul class="osh-pricing-features">
{{range .Features}}<li>{{.}}</li>{{end}}
</ul>
{{end}}
<a href="#" class="osh-pricing-cta">{{if .CTA}}{{.CTA}}{{else}}Get Started{{end}}</a>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- CTA Section -->
{{if .Config.CTASection.Headline}}
<section class="osh-cta-section" id="cta">
<div class="osh-cta-inner osh-reveal">
<h2>{{.Config.CTASection.Headline}}</h2>
{{if .Config.CTASection.Subheadline}}
<p>{{.Config.CTASection.Subheadline}}</p>
{{end}}
<a href="{{if .Config.CTASection.Button.URL}}{{.Config.CTASection.Button.URL}}{{else}}{{.RepoURL}}{{end}}" class="osh-btn-primary" data-cta="primary" style="padding: 16px 32px; font-size: 15px;">
{{if .Config.CTASection.Button.Label}}{{.Config.CTASection.Button.Label}}{{else}}Get Started{{end}}
{{svg "octicon-arrow-right" 16}}
</a>
</div>
</section>
{{end}}
<!-- Blog Section -->
{{if and .Config.Blog.Enabled .BlogPosts}}
<section class="osh-features" id="blog" style="border-top: 1px solid rgba(255,255,255,0.04);">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Blog</div>
<h2>{{if .Config.Blog.Headline}}{{.Config.Blog.Headline}}{{else}}Latest Posts{{end}}</h2>
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
</div>
<div class="osh-features-grid">
{{range .BlogPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="osh-feature-card osh-reveal" style="text-decoration: none; color: inherit; display: flex; flex-direction: column;">
{{if .FeaturedImage}}
<div style="margin: -32px -32px 20px -32px; overflow: hidden; border-radius: 12px 12px 0 0;">
<img src="{{.FeaturedImage.DownloadURL}}" alt="{{.Title}}" style="width: 100%; height: 180px; object-fit: cover; display: block;">
</div>
{{end}}
<h3 class="osh-feature-title">{{.Title}}</h3>
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}
<p class="osh-feature-desc">{{.Subtitle}}</p>
{{end}}
<div style="margin-top: auto; padding-top: 16px; font-size: 12px; color: var(--osh-muted); font-family: 'IBM Plex Mono', monospace;">
{{if .Author}}{{.Author.DisplayName}}{{end}} · {{DateUtils.AbsoluteShort .CreatedUnix}}
</div>
</a>
{{end}}
</div>
{{if .Config.Blog.CTAButton.Label}}
<div style="text-align: center; margin-top: 48px;" class="osh-reveal">
<a href="{{if .Config.Blog.CTAButton.URL}}{{.Config.Blog.CTAButton.URL}}{{else}}{{.BlogBaseURL}}{{end}}" class="osh-btn-secondary" data-cta="secondary">
{{.Config.Blog.CTAButton.Label}}
{{svg "octicon-arrow-right" 16}}
</a>
</div>
{{end}}
</div>
</section>
{{end}}
<!-- Gallery Section -->
{{if and .Config.Gallery.Enabled .GalleryImages}}
<section class="osh-features" id="gallery" style="border-top: 1px solid rgba(255,255,255,0.04);">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Gallery</div>
<h2>{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</h2>
{{if .Config.Gallery.Subheadline}}<p>{{.Config.Gallery.Subheadline}}</p>{{end}}
</div>
<div style="display: grid; grid-template-columns: repeat({{if .Config.Gallery.Columns}}{{.Config.Gallery.Columns}}{{else}}3{{end}}, 1fr); gap: 16px;">
{{range .GalleryImages}}
<div class="osh-feature-card osh-reveal" style="padding: 0; overflow: hidden;">
<a href="{{.URL}}" class="pages-gallery-trigger" data-src="{{.URL}}" data-caption="{{if .Caption}}{{.Caption}}{{else}}{{.Name}}{{end}}" style="display: block; cursor: pointer;">
<img src="{{.URL}}" alt="{{if .Caption}}{{.Caption}}{{else}}{{.Name}}{{end}}" style="width: 100%; height: 220px; object-fit: cover; display: block;">
</a>
{{if .Caption}}
<div style="padding: 16px 20px; font-size: 13px; color: var(--osh-muted);">{{.Caption}}</div>
{{end}}
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- Comparison Section -->
{{if and .Config.Comparison.Enabled .Config.Comparison.HasData}}
<section class="osh-features" id="comparison" style="border-top: 1px solid rgba(255,255,255,0.04);">
<div class="osh-features-inner">
<div class="osh-section-header osh-reveal">
<div class="osh-section-label">Compare</div>
<h2>{{if .Config.Comparison.Headline}}{{.Config.Comparison.Headline}}{{else}}How We Compare{{end}}</h2>
{{if .Config.Comparison.Subheadline}}<p>{{.Config.Comparison.Subheadline}}</p>{{end}}
</div>
<div class="osh-reveal" style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
<thead>
<tr>
<th style="text-align: left; padding: 16px 20px; border-bottom: 2px solid var(--osh-dim); color: var(--osh-muted); font-weight: 500; min-width: 200px;"></th>
{{range .Config.Comparison.Columns}}
<th style="text-align: center; padding: 16px 20px; border-bottom: 2px solid var(--osh-dim); font-weight: 600; font-size: 15px;{{if .Highlight}} color: var(--osh-accent);{{else}} color: var(--osh-text);{{end}}">
{{.Name}}
{{if .Highlight}}<div style="width: 40px; height: 3px; background: var(--osh-accent); margin: 8px auto 0; border-radius: 2px;"></div>{{end}}
</th>
{{end}}
</tr>
</thead>
<tbody>
{{range .Config.Comparison.Groups}}
{{if .Name}}
<tr>
<td colspan="4" style="padding: 20px 20px 8px; font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--osh-muted); border-bottom: 1px solid var(--osh-dim);">{{.Name}}</td>
</tr>
{{end}}
{{range .Features}}
<tr style="border-bottom: 1px solid rgba(255,255,255,0.04);">
<td style="padding: 14px 20px; color: var(--osh-text);">{{.Name}}</td>
{{range .Values}}
<td style="text-align: center; padding: 14px 20px;">
{{if eq . "true"}}<span style="color: var(--osh-accent);">{{svg "octicon-check-circle-fill" 18}}</span>
{{else if eq . "false"}}<span style="color: var(--osh-dim);">{{svg "octicon-x-circle" 18}}</span>
{{else}}<span style="color: var(--osh-text); font-weight: 500;">{{.}}</span>
{{end}}
</td>
{{end}}
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
</div>
</section>
{{end}}
{{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}}
<!-- Footer -->
<footer class="osh-footer">
<div class="osh-footer-brand">
<div class="osh-footer-logo">{{svg "octicon-zap" 12}}</div>
<span class="osh-footer-text">{{if .Config.Footer.Copyright}}{{.Config.Footer.Copyright}}{{else}}&copy; <script>document.write(new Date().getFullYear())</script> {{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}{{end}}</span>
</div>
{{if .Config.Footer.Social}}
<div class="osh-footer-social">
{{range .Config.Footer.Social}}
<a href="{{.URL}}" title="{{.Platform}}">
{{if eq .Platform "twitter"}}{{svg "octicon-mention" 16}}
{{else if eq .Platform "bluesky"}}{{svg "octicon-cloud" 16}}
{{else if eq .Platform "github"}}{{svg "octicon-mark-github" 16}}
{{else if eq .Platform "discord"}}{{svg "octicon-comment-discussion" 16}}
{{else if eq .Platform "linkedin"}}{{svg "octicon-briefcase" 16}}
{{else if eq .Platform "youtube"}}{{svg "octicon-video" 16}}
{{else if eq .Platform "instagram"}}{{svg "octicon-device-camera" 16}}
{{else if eq .Platform "facebook"}}{{svg "octicon-people" 16}}
{{else if eq .Platform "substack"}}{{svg "octicon-note" 16}}
{{else if eq .Platform "threads"}}{{svg "octicon-share" 16}}
{{else if eq .Platform "tiktok"}}{{svg "octicon-play" 16}}
{{else if eq .Platform "reddit"}}{{svg "octicon-hash" 16}}
{{else if eq .Platform "mastodon"}}{{svg "octicon-megaphone" 16}}
{{else if eq .Platform "twitch"}}{{svg "octicon-broadcast" 16}}
{{else if eq .Platform "rss"}}{{svg "octicon-rss" 16}}
{{else}}{{svg "octicon-link-external" 16}}{{end}}
</a>
{{end}}
</div>
{{end}}
<div class="osh-footer-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="osh-footer-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="osh-footer-link">Repository</a>{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="osh-footer-link">Documentation</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="osh-footer-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="osh-footer-link">Issues</a>{{end}}
</div>
</footer>
</div>
<!-- Scroll reveal observer -->
<script>
(function() {
var reveals = document.querySelectorAll('.osh-reveal:not(.visible)');
if (!reveals.length) return;
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
reveals.forEach(function(el) { observer.observe(el); });
})();
</script>
{{template "pages/base_footer" .}}