2
0
Files
gitcaddy-server/templates/pages/documentation-first.tmpl
logikonline f3eba7dd34 feat(pages): add 5 new landing page templates
Add Documentation First, Developer Tool, Visual Showcase, CLI Terminal, and Architecture Deep Dive templates. Brings total templates to 9. Each template has unique design language and target audience: Documentation First for docs-heavy projects, Developer Tool for technical products, Visual Showcase for design/media projects, CLI Terminal for command-line tools, Architecture Deep Dive for technical deep-dives. Updates template display names for clarity.
2026-03-16 22:18:53 -04:00

1452 lines
44 KiB
Handlebars

{{template "pages/base_head" .}}
<style>
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=Source+Sans+3:wght@300;400;500;600;700&display=swap');
:root {
--df-bg: #faf8f5;
--df-surface: #ffffff;
--df-elevated: #f5f2ed;
--df-text: #2c2c2c;
--df-muted: #6b6560;
--df-dim: #a9a29a;
--df-border: #e8e2d9;
--df-accent: {{if .Config.Theme.PrimaryColor}}{{.Config.Theme.PrimaryColor}}{{else}}#0d7377{{end}};
--df-accent-dark: {{if .Config.Theme.AccentColor}}{{.Config.Theme.AccentColor}}{{else}}#0a5c5f{{end}};
--df-accent-light: color-mix(in srgb, var(--df-accent) 10%, transparent);
--df-accent-medium: color-mix(in srgb, var(--df-accent) 20%, transparent);
--df-warm-shadow: 0 1px 3px rgba(44, 44, 44, 0.06), 0 4px 16px rgba(44, 44, 44, 0.04);
}
html {
scroll-behavior: smooth;
}
html, body.pages-body {
overflow-x: hidden;
background: var(--df-bg) !important;
}
/* Paper-grain noise overlay */
.df-page::before {
content: "";
position: fixed;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 9999;
opacity: 0.5;
}
.df-page {
position: relative;
min-height: 100vh;
background: var(--df-bg);
color: var(--df-text);
font-family: 'Source Sans 3', -apple-system, sans-serif;
-webkit-font-smoothing: antialiased;
}
/* Reveal animation system */
.df-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);
}
.df-reveal.visible {
opacity: 1;
transform: translateY(0);
}
.df-reveal-delay-1 { transition-delay: 0.1s; }
.df-reveal-delay-2 { transition-delay: 0.2s; }
.df-reveal-delay-3 { transition-delay: 0.3s; }
.df-reveal-delay-4 { transition-delay: 0.4s; }
/* Navigation */
.df-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(250, 248, 245, 0.92);
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
border-bottom: 1px solid var(--df-border);
z-index: 100;
transition: background 0.3s ease;
}
.df-nav-brand {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: inherit;
}
.df-nav-logo {
width: 30px;
height: 30px;
background: var(--df-accent);
border-radius: 7px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
transition: box-shadow 0.3s ease;
}
.df-nav-brand:hover .df-nav-logo {
box-shadow: 0 2px 12px var(--df-accent-medium);
}
.df-nav-name {
font-family: 'Lora', serif;
font-weight: 600;
font-size: 17px;
letter-spacing: -0.01em;
color: var(--df-text);
}
.df-nav-links {
display: flex;
align-items: center;
gap: 4px;
}
.df-nav-link {
color: var(--df-muted);
text-decoration: none;
font-size: 14px;
font-weight: 500;
padding: 8px 14px;
border-radius: 6px;
transition: all 0.2s ease;
}
.df-nav-link:hover {
color: var(--df-text);
background: var(--df-accent-light);
}
.df-nav-cta {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: var(--df-elevated);
border: 1px solid var(--df-border);
color: var(--df-text);
font-size: 13px;
font-weight: 500;
border-radius: 6px;
text-decoration: none;
transition: all 0.2s ease;
}
.df-nav-cta:hover {
background: var(--df-border);
border-color: var(--df-dim);
}
/* Mobile menu toggle */
.df-menu-toggle {
display: none;
background: none;
border: none;
color: var(--df-muted);
cursor: pointer;
padding: 8px;
}
/* Buttons */
.df-btn-primary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
background: var(--df-accent);
color: #fff;
font-weight: 600;
font-size: 15px;
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 1px 3px rgba(13, 115, 119, 0.2);
}
.df-btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(13, 115, 119, 0.25);
background: var(--df-accent-dark);
}
.df-btn-secondary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
background: transparent;
color: var(--df-accent);
font-weight: 600;
font-size: 15px;
border-radius: 8px;
text-decoration: none;
border: 2px solid var(--df-accent);
transition: all 0.25s ease;
}
.df-btn-secondary:hover {
background: var(--df-accent-light);
border-color: var(--df-accent-dark);
}
/* Hero Section */
.df-hero {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 160px 40px 120px;
text-align: center;
}
.df-hero-content {
position: relative;
z-index: 1;
max-width: 800px;
}
/* Warm badge */
.df-badge {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 18px;
background: var(--df-accent-light);
border: 1px solid var(--df-accent-medium);
border-radius: 100px;
font-family: 'Source Sans 3', sans-serif;
font-size: 13px;
font-weight: 600;
color: var(--df-accent);
margin-bottom: 36px;
letter-spacing: 0.02em;
}
.df-badge-dot {
width: 7px;
height: 7px;
background: var(--df-accent);
border-radius: 50%;
animation: df-pulse 2.5s ease-in-out infinite;
}
@keyframes df-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.df-hero h1 {
font-family: 'Lora', serif;
font-size: clamp(40px, 6vw, 64px);
font-weight: 700;
line-height: 1.12;
margin-bottom: 24px;
letter-spacing: -0.02em;
color: var(--df-text);
}
.df-hero-sub {
font-size: clamp(17px, 2vw, 20px);
color: var(--df-muted);
line-height: 1.7;
margin-bottom: 44px;
max-width: 560px;
margin-left: auto;
margin-right: auto;
font-weight: 400;
}
.df-hero-ctas {
display: flex;
gap: 14px;
justify-content: center;
margin-bottom: 52px;
}
/* Code block */
.df-code-block {
display: inline-flex;
align-items: center;
gap: 16px;
padding: 16px 24px;
background: var(--df-surface);
border: 1px solid var(--df-border);
border-radius: 10px;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 14px;
max-width: 480px;
margin: 0 auto;
position: relative;
overflow: hidden;
box-shadow: var(--df-warm-shadow);
}
.df-code-block::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--df-accent);
border-radius: 3px 0 0 3px;
}
.df-code-prompt {
color: var(--df-accent);
font-weight: 600;
user-select: none;
}
.df-code-block code {
color: var(--df-text);
flex: 1;
text-align: left;
}
.df-copy-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: var(--df-elevated);
border: 1px solid var(--df-border);
border-radius: 6px;
color: var(--df-muted);
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
}
.df-copy-btn:hover {
background: var(--df-border);
color: var(--df-text);
}
/* Stats section */
.df-stats {
padding: 64px 40px;
border-top: 1px solid var(--df-border);
border-bottom: 1px solid var(--df-border);
background: var(--df-surface);
}
.df-stats-inner {
display: flex;
justify-content: center;
align-items: center;
max-width: 800px;
margin: 0 auto;
flex-wrap: wrap;
gap: 0;
}
.df-stat-item {
text-align: center;
padding: 0 40px;
position: relative;
}
.df-stat-item:not(:last-child)::after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 40px;
background: var(--df-border);
}
.df-stat-value {
font-family: 'Lora', serif;
font-size: 32px;
font-weight: 700;
color: var(--df-text);
letter-spacing: -0.02em;
}
.df-stat-label {
font-size: 13px;
color: var(--df-muted);
margin-top: 6px;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 600;
}
/* Downloads section */
.df-downloads {
padding: 80px 40px;
position: relative;
}
.df-downloads-inner {
max-width: 800px;
margin: 0 auto;
text-align: center;
}
.df-downloads h2 {
font-family: 'Lora', serif;
font-size: clamp(24px, 3vw, 32px);
font-weight: 700;
margin-bottom: 8px;
letter-spacing: -0.02em;
color: var(--df-text);
}
.df-downloads p {
color: var(--df-muted);
margin-bottom: 24px;
font-size: 16px;
}
.df-downloads-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.df-download-item {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: var(--df-surface);
border: 1px solid var(--df-border);
border-radius: 8px;
color: var(--df-text);
text-decoration: none;
font-size: 14px;
transition: all 0.2s;
box-shadow: var(--df-warm-shadow);
}
.df-download-item:hover {
border-color: var(--df-accent);
box-shadow: 0 2px 12px var(--df-accent-medium);
}
.df-download-size {
font-size: 12px;
color: var(--df-dim);
}
/* Features / Value Props sections */
.df-features {
padding: 120px 40px;
position: relative;
}
.df-features-inner {
max-width: 1100px;
margin: 0 auto;
}
.df-section-label {
font-family: 'Source Sans 3', sans-serif;
font-size: 13px;
font-weight: 700;
color: var(--df-accent);
text-transform: uppercase;
letter-spacing: 0.12em;
margin-bottom: 16px;
text-align: center;
}
.df-section-header {
text-align: center;
margin-bottom: 64px;
}
.df-section-header h2 {
font-family: 'Lora', serif;
font-size: clamp(28px, 4vw, 42px);
font-weight: 700;
margin-bottom: 16px;
letter-spacing: -0.02em;
color: var(--df-text);
}
.df-section-header p {
font-size: 17px;
color: var(--df-muted);
max-width: 480px;
margin: 0 auto;
font-weight: 400;
line-height: 1.6;
}
.df-features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.df-feature-card {
padding: 32px;
background: var(--df-surface);
border: 1px solid var(--df-border);
border-radius: 12px;
transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1);
position: relative;
overflow: hidden;
box-shadow: var(--df-warm-shadow);
}
.df-feature-card:hover {
border-color: var(--df-accent);
transform: translateY(-4px);
box-shadow: 0 8px 32px rgba(44, 44, 44, 0.08);
}
.df-feature-icon {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
background: var(--df-accent-light);
border: 1px solid var(--df-accent-medium);
border-radius: 10px;
color: var(--df-accent);
margin-bottom: 20px;
}
.df-feature-title {
font-family: 'Lora', serif;
font-size: 18px;
font-weight: 600;
color: var(--df-text);
margin-bottom: 8px;
}
.df-feature-desc {
font-size: 15px;
color: var(--df-muted);
line-height: 1.65;
font-weight: 400;
}
/* Social proof */
.df-social-proof {
padding: 100px 40px;
background: var(--df-surface);
border-top: 1px solid var(--df-border);
border-bottom: 1px solid var(--df-border);
}
.df-social-proof-inner {
max-width: 1100px;
margin: 0 auto;
}
.df-logos {
display: flex;
justify-content: center;
gap: 48px;
margin-bottom: 64px;
flex-wrap: wrap;
}
.df-logo-item {
padding: 10px 20px;
font-family: 'Lora', serif;
font-size: 15px;
font-weight: 600;
color: var(--df-dim);
letter-spacing: 0.01em;
transition: color 0.3s ease;
}
.df-logo-item:hover {
color: var(--df-muted);
}
.df-testimonial {
background: var(--df-bg);
border: 1px solid var(--df-border);
border-radius: 16px;
padding: 48px;
max-width: 680px;
margin: 0 auto;
position: relative;
box-shadow: var(--df-warm-shadow);
}
.df-testimonial-quote {
font-family: 'Lora', serif;
font-size: 20px;
line-height: 1.7;
color: var(--df-text);
margin-bottom: 28px;
font-weight: 400;
font-style: italic;
}
.df-testimonial-author {
font-weight: 600;
color: var(--df-text);
font-size: 15px;
}
.df-testimonial-role {
font-size: 13px;
color: var(--df-muted);
}
/* Pricing */
.df-pricing {
padding: 120px 40px;
}
.df-pricing-inner {
max-width: 1100px;
margin: 0 auto;
}
.df-pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 64px;
}
.df-pricing-card {
background: var(--df-surface);
border: 1px solid var(--df-border);
border-radius: 14px;
padding: 36px;
position: relative;
transition: all 0.35s cubic-bezier(0.16, 1, 0.3, 1);
box-shadow: var(--df-warm-shadow);
}
.df-pricing-card:hover {
border-color: var(--df-dim);
transform: translateY(-4px);
box-shadow: 0 8px 32px rgba(44, 44, 44, 0.08);
}
.df-pricing-card.featured {
border-color: var(--df-accent);
background: linear-gradient(180deg, color-mix(in srgb, var(--df-accent) 3%, var(--df-surface)) 0%, var(--df-surface) 100%);
box-shadow: 0 4px 24px rgba(13, 115, 119, 0.1);
}
.df-pricing-badge {
position: absolute;
top: -11px;
left: 50%;
transform: translateX(-50%);
padding: 5px 14px;
background: var(--df-accent);
color: #fff;
font-size: 11px;
font-weight: 700;
border-radius: 6px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.df-pricing-name {
font-family: 'Lora', serif;
font-size: 20px;
font-weight: 600;
color: var(--df-text);
margin-bottom: 8px;
}
.df-pricing-price {
font-family: 'Lora', serif;
font-size: 44px;
font-weight: 700;
color: var(--df-text);
line-height: 1;
margin-bottom: 4px;
letter-spacing: -0.03em;
}
.df-pricing-period {
font-size: 14px;
color: var(--df-muted);
margin-bottom: 28px;
}
.df-pricing-features {
list-style: none;
padding: 0;
margin: 0 0 32px 0;
}
.df-pricing-features li {
padding: 10px 0;
border-bottom: 1px solid var(--df-border);
font-size: 14px;
color: var(--df-muted);
display: flex;
align-items: center;
gap: 10px;
}
.df-pricing-features li::before {
content: "\2713";
color: var(--df-accent);
font-weight: 700;
font-size: 14px;
flex-shrink: 0;
}
.df-pricing-features li:last-child {
border-bottom: none;
}
.df-pricing-cta {
display: block;
width: 100%;
padding: 13px 24px;
text-align: center;
background: var(--df-elevated);
color: var(--df-text);
font-weight: 600;
font-size: 14px;
border-radius: 8px;
text-decoration: none;
border: 1px solid var(--df-border);
transition: all 0.25s ease;
}
.df-pricing-cta:hover {
background: var(--df-border);
border-color: var(--df-dim);
}
.df-pricing-card.featured .df-pricing-cta {
background: var(--df-accent);
color: #fff;
border: none;
}
.df-pricing-card.featured .df-pricing-cta:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(13, 115, 119, 0.25);
background: var(--df-accent-dark);
}
/* CTA section */
.df-cta-section {
padding: 120px 40px;
text-align: center;
position: relative;
background: var(--df-surface);
border-top: 1px solid var(--df-border);
}
.df-cta-inner {
max-width: 600px;
margin: 0 auto;
position: relative;
z-index: 1;
}
.df-cta-section h2 {
font-family: 'Lora', serif;
font-size: clamp(28px, 4vw, 44px);
font-weight: 700;
margin-bottom: 16px;
letter-spacing: -0.02em;
color: var(--df-text);
}
.df-cta-section p {
font-size: 17px;
color: var(--df-muted);
margin-bottom: 36px;
font-weight: 400;
line-height: 1.6;
}
/* Blog content markdown styles */
.df-blog-content h1, .df-blog-content h2, .df-blog-content h3, .df-blog-content h4 {
color: var(--df-text); font-family: 'Lora', serif; margin: 1.5em 0 0.5em;
}
.df-blog-content h2 { font-size: 1.5em; border-bottom: 1px solid var(--df-border); padding-bottom: 8px; }
.df-blog-content h3 { font-size: 1.25em; }
.df-blog-content a { color: var(--df-accent); text-decoration: underline; }
.df-blog-content a:hover { opacity: 0.8; }
.df-blog-content code { background: var(--df-elevated); padding: 2px 6px; border-radius: 4px; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; font-size: 0.9em; }
.df-blog-content pre { background: var(--df-elevated); border: 1px solid var(--df-border); border-radius: 8px; padding: 16px; overflow-x: auto; margin: 16px 0; }
.df-blog-content pre code { background: none; padding: 0; }
.df-blog-content blockquote { border-left: 3px solid var(--df-accent); padding-left: 16px; color: var(--df-muted); margin: 16px 0; font-style: italic; }
.df-blog-content img { max-width: 100%; border-radius: 8px; margin: 16px 0; }
.df-blog-content ul, .df-blog-content ol { padding-left: 24px; margin: 12px 0; }
.df-blog-content li { margin: 4px 0; }
.df-blog-content table { border-collapse: collapse; width: 100%; margin: 16px 0; }
.df-blog-content th, .df-blog-content td { border: 1px solid var(--df-border); padding: 8px 12px; text-align: left; }
.df-blog-content th { background: var(--df-elevated); font-weight: 600; }
.df-blog-content hr { border: none; border-top: 1px solid var(--df-border); margin: 24px 0; }
/* Footer */
.df-footer {
padding: 32px 40px;
border-top: 1px solid var(--df-border);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 24px;
background: var(--df-surface);
}
.df-footer-brand {
display: flex;
align-items: center;
gap: 8px;
}
.df-footer-logo {
width: 22px;
height: 22px;
background: var(--df-accent-light);
border: 1px solid var(--df-accent-medium);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
color: var(--df-accent);
}
.df-footer-text {
font-size: 13px;
color: var(--df-dim);
}
.df-footer-links {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.df-footer-link {
color: var(--df-muted);
text-decoration: none;
font-size: 13px;
transition: color 0.2s ease;
}
.df-footer-link:hover {
color: var(--df-text);
}
.df-footer-social {
display: flex;
gap: 12px;
}
.df-footer-social a {
color: var(--df-dim);
transition: color 0.2s ease;
padding: 4px;
}
.df-footer-social a:hover {
color: var(--df-accent);
}
/* Responsive */
@media (max-width: 768px) {
.df-nav { padding: 0 20px; }
.df-nav-links { display: none; }
.df-menu-toggle { display: flex; }
.df-hero { padding: 120px 24px 80px; }
.df-hero-ctas { flex-direction: column; align-items: center; }
.df-btn-primary, .df-btn-secondary { width: 100%; justify-content: center; }
.df-features, .df-social-proof, .df-cta-section, .df-pricing { padding: 80px 24px; }
.df-stats { padding: 48px 24px; }
.df-stat-item { padding: 16px 24px; }
.df-stat-item:not(:last-child)::after { display: none; }
.df-stats-inner { flex-direction: column; }
.df-features-grid { grid-template-columns: 1fr; }
.df-footer { flex-direction: column; text-align: center; padding: 24px; }
.df-testimonial { padding: 32px 24px; }
.df-code-block { font-size: 12px; }
}
@media (max-width: 480px) {
.df-hero h1 { font-size: 32px; }
.df-section-header h2 { font-size: 28px; }
.df-pricing-price { font-size: 36px; }
}
</style>
<div class="df-page">
<!-- Navigation -->
<nav class="df-nav">
<a href="/" class="df-nav-brand">
{{if .LogoURL}}
<img src="{{.LogoURL}}" alt="{{.Config.Brand.Name}}" style="height: 30px; border-radius: 4px;">
{{else}}
<div class="df-nav-logo">
{{svg "octicon-book" 14}}
</div>
{{end}}
<span class="df-nav-name">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}</span>
</a>
<div class="df-nav-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="df-nav-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="df-nav-link">Docs</a>{{end}}
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="df-nav-link">API</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-nav-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-nav-link">Issues</a>{{end}}
{{if .Config.ValueProps}}<a href="#value-props" class="df-nav-link">Value Props</a>{{end}}
{{if .Config.Features}}<a href="#features" class="df-nav-link">Features</a>{{end}}
{{if .Config.Pricing.Plans}}<a href="#pricing" class="df-nav-link">Pricing</a>{{end}}
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}#blog{{end}}" class="df-nav-link">Blog</a>{{end}}
{{if .Config.Gallery.Enabled}}<a href="#gallery" class="df-nav-link">Gallery</a>{{end}}
{{if .Config.Navigation.ShowRepository}}
<a href="{{.RepoURL}}" class="df-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="df-menu-toggle" onclick="this.parentElement.querySelector('.df-nav-links').style.display=this.parentElement.querySelector('.df-nav-links').style.display==='flex'?'none':'flex'">
{{svg "octicon-three-bars" 20}}
</button>
</nav>
{{if .PageIsBlogDetail}}
<!-- Blog Detail View -->
<section class="df-hero" style="padding-top: 120px; min-height: auto;">
<div class="df-hero-content" style="max-width: 800px;">
{{if .BlogPost.FeaturedImage}}
<div style="margin: 0 auto 32px; border-radius: 12px; overflow: hidden; border: 1px solid var(--df-border);">
<img src="{{.BlogPost.FeaturedImage.DownloadURL}}" alt="{{.BlogPost.Title}}" style="width: 100%; max-height: 400px; object-fit: cover; display: block;">
</div>
{{end}}
<h1 style="font-family: 'Lora', serif; font-size: 36px; margin-bottom: 8px;">{{.BlogPost.Title}}</h1>
{{if .BlogPost.Subtitle}}<p style="font-size: 1.2rem; color: var(--df-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(--df-muted);">
{{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(--df-accent-light); border: 1px solid var(--df-accent-medium); padding: 2px 10px; border-radius: 100px; font-size: 12px; color: var(--df-accent);">{{.}}</span> {{end}}{{end}}
</div>
<div class="markup df-blog-content" style="color: var(--df-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 var(--df-border);">
<a href="{{.BlogBaseURL}}" class="df-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="df-features" style="padding-top: 120px;">
<div class="df-features-inner">
<div class="df-section-header df-reveal">
<div class="df-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="df-features-grid">
{{range .BlogListPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="df-feature-card df-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="df-feature-title">{{.Title}}</h3>
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}
<p class="df-feature-desc">{{.Subtitle}}</p>
{{end}}
<div style="margin-top: auto; padding-top: 16px; font-size: 13px; color: var(--df-muted);">
{{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="df-hero">
<div class="df-hero-content">
<div class="df-badge df-reveal visible">
<span class="df-badge-dot"></span>
{{if .LatestRelease}}v{{.LatestReleaseTag}} released{{else}}Open Source{{end}}
</div>
<h1 class="df-reveal visible df-reveal-delay-1">{{if .Config.Hero.Headline}}{{.Config.Hero.Headline}}{{else}}{{.Repository.Name}}{{end}}</h1>
<p class="df-hero-sub df-reveal visible df-reveal-delay-2">
{{if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}
</p>
<div class="df-hero-ctas df-reveal visible df-reveal-delay-3">
{{if .Config.Hero.PrimaryCTA.Label}}
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="df-btn-primary" data-cta="primary">
{{.Config.Hero.PrimaryCTA.Label}}
{{svg "octicon-arrow-right" 16}}
</a>
{{else}}
<a href="{{.RepoURL}}" class="df-btn-primary" data-cta="primary">
Get Started
{{svg "octicon-arrow-right" 16}}
</a>
{{end}}
{{if .Config.Hero.SecondaryCTA.Label}}
<a href="{{.Config.Hero.SecondaryCTA.URL}}" class="df-btn-secondary" data-cta="secondary">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
{{.Config.Hero.SecondaryCTA.Label}}
</a>
{{else}}
<a href="{{.RepoURL}}" class="df-btn-secondary" data-cta="secondary">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
View Source
</a>
{{end}}
</div>
{{if .Config.Hero.CodeExample}}
<div class="df-code-block df-reveal visible df-reveal-delay-4">
<span class="df-code-prompt">$</span>
<code id="install-cmd">{{.Config.Hero.CodeExample}}</code>
<button class="df-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="df-stats">
<div class="df-stats-inner df-reveal">
{{if .Config.Stats}}
{{range .Config.Stats}}
<div class="df-stat-item">
<div class="df-stat-value">{{.Value}}</div>
<div class="df-stat-label">{{.Label}}</div>
</div>
{{end}}
{{else}}
<div class="df-stat-item">
<div class="df-stat-value">{{.NumStars}}</div>
<div class="df-stat-label">Stars</div>
</div>
<div class="df-stat-item">
<div class="df-stat-value">{{.NumForks}}</div>
<div class="df-stat-label">Forks</div>
</div>
{{if .LatestRelease}}
<div class="df-stat-item">
<div class="df-stat-value">v{{.LatestReleaseTag}}</div>
<div class="df-stat-label">Latest</div>
</div>
{{end}}
{{end}}
</div>
</section>
{{end}}
<!-- Downloads Section -->
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
<section class="df-downloads">
<div class="df-downloads-inner df-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: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-desktop" 16}} Windows</h4>
<div class="df-downloads-grid">
{{range $windowsFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-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: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-desktop" 16}} macOS</h4>
<div class="df-downloads-grid">
{{range $macosFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-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: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-terminal" 16}} Linux</h4>
<div class="df-downloads-grid">
{{range $linuxFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-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: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-mobile" 16}} Android</h4>
<div class="df-downloads-grid">
{{range $androidFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-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: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-mobile" 16}} iOS</h4>
<div class="df-downloads-grid">
{{range $iosFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
{{end}}
{{if or $.GooglePlayID $.AppStoreID}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-device-mobile" 16}} App Stores</h4>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
{{if $.GooglePlayID}}
<a href="https://play.google.com/store/apps/details?id={{$.GooglePlayID}}" target="_blank" rel="noopener" class="df-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="df-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>
</div>
{{end}}
{{if $otherFiles}}
<div style="margin-bottom: 24px;">
<h4 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; color: var(--df-muted); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;">{{svg "octicon-file" 16}} Other</h4>
<div class="df-downloads-grid">
{{range $otherFiles}}<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="df-download-item">{{svg "octicon-download" 16}} {{.Name}} <span class="df-download-size">{{FileSize .Size}}</span></a>{{end}}
</div>
</div>
{{end}}
</div>
</section>
{{end}}
<!-- Value Props Section -->
{{if .Config.ValueProps}}
<section class="df-features" id="value-props">
<div class="df-features-inner">
<div class="df-section-header df-reveal">
<div class="df-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="df-features-grid">
{{range .Config.ValueProps}}
<div class="df-feature-card df-reveal">
<div class="df-feature-icon">
{{svg (printf "octicon-%s" (or .Icon "check")) 20}}
</div>
<h3 class="df-feature-title">{{.Title}}</h3>
<p class="df-feature-desc">{{.Description}}</p>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- Features Section -->
{{if .Config.Features}}
<section class="df-features" id="features" style="padding-top: 40px;">
<div class="df-features-inner">
<div class="df-section-header df-reveal">
<div class="df-section-label">Capabilities</div>
<h2>Features</h2>
<p>Powerful capabilities at your fingertips.</p>
</div>
<div class="df-features-grid">
{{range .Config.Features}}
<div class="df-feature-card df-reveal">
<div class="df-feature-icon">
{{svg (printf "octicon-%s" (or .Icon "zap")) 20}}
</div>
<h3 class="df-feature-title">{{.Title}}</h3>
<p class="df-feature-desc">{{.Description}}</p>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- Social Proof -->
{{if or .Config.SocialProof.Logos .Config.SocialProof.Testimonials}}
<section class="df-social-proof">
<div class="df-social-proof-inner">
{{if .Config.SocialProof.Logos}}
<div class="df-logos df-reveal">
{{range .Config.SocialProof.Logos}}
<div class="df-logo-item">{{.}}</div>
{{end}}
</div>
{{end}}
{{if .Config.SocialProof.Testimonials}}
<div class="df-testimonials-container">
{{range .Config.SocialProof.Testimonials}}
<div class="df-testimonial df-reveal" style="display: none;">
<p class="df-testimonial-quote">"{{.Quote}}"</p>
<div>
<div class="df-testimonial-author">{{.Author}}</div>
<div class="df-testimonial-role">{{.Role}}</div>
</div>
</div>
{{end}}
</div>
<script>
(function() {
var testimonials = document.querySelectorAll(".df-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="df-pricing" id="pricing">
<div class="df-pricing-inner">
<div class="df-section-header df-reveal">
<div class="df-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="df-pricing-grid">
{{range .Config.Pricing.Plans}}
<div class="df-pricing-card{{if .Featured}} featured{{end}} df-reveal">
{{if .Featured}}<span class="df-pricing-badge">Popular</span>{{end}}
<div class="df-pricing-name">{{.Name}}</div>
<div class="df-pricing-price">{{.Price}}</div>
<div class="df-pricing-period">{{.Period}}</div>
{{if .Features}}
<ul class="df-pricing-features">
{{range .Features}}<li>{{.}}</li>{{end}}
</ul>
{{end}}
<a href="#" class="df-pricing-cta">{{if .CTA}}{{.CTA}}{{else}}Get Started{{end}}</a>
</div>
{{end}}
</div>
</div>
</section>
{{end}}
<!-- CTA Section -->
{{if .Config.CTASection.Headline}}
<section class="df-cta-section">
<div class="df-cta-inner df-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="df-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="df-features" id="blog" style="border-top: 1px solid var(--df-border);">
<div class="df-features-inner">
<div class="df-section-header df-reveal">
<div class="df-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="df-features-grid">
{{range .BlogPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="df-feature-card df-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="df-feature-title">{{.Title}}</h3>
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}
<p class="df-feature-desc">{{.Subtitle}}</p>
{{end}}
<div style="margin-top: auto; padding-top: 16px; font-size: 13px; color: var(--df-muted);">
{{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="df-reveal">
<a href="{{if .Config.Blog.CTAButton.URL}}{{.Config.Blog.CTAButton.URL}}{{else}}{{.BlogBaseURL}}{{end}}" class="df-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="df-features" id="gallery" style="border-top: 1px solid var(--df-border);">
<div class="df-features-inner">
<div class="df-section-header df-reveal">
<div class="df-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="df-feature-card df-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(--df-muted);">{{.Caption}}</div>
{{end}}
</div>
{{end}}
</div>
</div>
</section>
{{end}}
{{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}}
<!-- Footer -->
<footer class="df-footer">
<div class="df-footer-brand">
<div class="df-footer-logo">{{svg "octicon-book" 12}}</div>
<span class="df-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="df-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="df-footer-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="df-footer-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="df-footer-link">Repository</a>{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="df-footer-link">Documentation</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="df-footer-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="df-footer-link">Issues</a>{{end}}
</div>
</footer>
</div>
<!-- Scroll reveal observer -->
<script>
(function() {
var reveals = document.querySelectorAll('.df-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" .}}