2
0
Files
gitcaddy-server/templates/pages/bold-marketing.tmpl
logikonline 573aa49a22
Some checks failed
Build and Release / Unit Tests (push) Successful in 3m32s
Build and Release / Create Release (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 8m1s
Build and Release / Lint (push) Failing after 8m13s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been skipped
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been skipped
Build and Release / Build Binary (linux/arm64) (push) Has been skipped
fix(blog): allow public access to blog featured images
Allow public access to blog post featured images even when repository is private, if the post is published and blog is enabled. This supports public landing pages with blog sections that display featured images. Adds IsPublishedBlogFeaturedImage query and isBlogFeaturedImage check in attachment serving. Also removes redundant SafeHTML filter from blog content templates (already HTML-safe).
2026-03-15 23:05:58 -04:00

1577 lines
44 KiB
Handlebars

{{template "pages/base_head" .}}
<style>
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=Space+Mono:wght@400;700&display=swap');
:root {
--nb-primary: {{if .Config.Theme.PrimaryColor}}{{.Config.Theme.PrimaryColor}}{{else}}#ff2d55{{end}};
--nb-accent: {{if .Config.Theme.AccentColor}}{{.Config.Theme.AccentColor}}{{else}}#00f0ff{{end}};
--nb-neon-yellow: #e4ff1a;
--nb-bg: #0a0a0a;
--nb-surface: #111111;
--nb-text: #ffffff;
--nb-muted: rgba(255, 255, 255, 0.55);
--nb-subtle: rgba(255, 255, 255, 0.25);
--nb-border: rgba(255, 255, 255, 0.06);
--nb-border-hard: rgba(255, 255, 255, 0.12);
}
/* Scroll reveal */
.nb-reveal {
opacity: 0;
transform: translateY(24px);
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.nb-reveal.visible {
opacity: 1;
transform: translateY(0);
}
.nb-reveal-delay-1 { transition-delay: 0.1s; }
.nb-reveal-delay-2 { transition-delay: 0.2s; }
.nb-reveal-delay-3 { transition-delay: 0.3s; }
.nb-page {
min-height: 100vh;
background: var(--nb-bg);
color: var(--nb-text);
font-family: 'Syne', -apple-system, sans-serif;
line-height: 1.6;
overflow-x: hidden;
position: relative;
}
/* Grid lines background */
.nb-page::before {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(var(--nb-border) 1px, transparent 1px),
linear-gradient(90deg, var(--nb-border) 1px, transparent 1px);
background-size: 80px 80px;
pointer-events: none;
z-index: 0;
}
/* Noise texture */
.nb-page::after {
content: '';
position: fixed;
inset: 0;
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
}
/* Neon glow utility */
.nb-glow-text {
text-shadow: 0 0 20px var(--nb-accent), 0 0 40px color-mix(in srgb, var(--nb-accent) 30%, transparent);
color: var(--nb-accent);
}
.nb-glow-primary {
text-shadow: 0 0 20px var(--nb-primary), 0 0 40px color-mix(in srgb, var(--nb-primary) 30%, transparent);
color: var(--nb-primary);
}
/* Glitch hover effect */
@keyframes nb-glitch {
0% { transform: translate(0); }
20% { transform: translate(-2px, 2px); }
40% { transform: translate(-2px, -2px); }
60% { transform: translate(2px, 2px); }
80% { transform: translate(2px, -2px); }
100% { transform: translate(0); }
}
@keyframes nb-scan {
0% { top: -100%; }
100% { top: 100%; }
}
/* --- Navigation --- */
.nb-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 16px 48px;
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(10, 10, 10, 0.85);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
z-index: 100;
border-bottom: 1px solid var(--nb-border-hard);
}
.nb-nav-brand {
font-weight: 800;
font-size: 22px;
letter-spacing: -0.03em;
text-decoration: none;
color: var(--nb-text);
text-transform: uppercase;
}
.nb-nav-links {
display: flex;
align-items: center;
gap: 32px;
}
.nb-nav-link {
color: var(--nb-muted);
text-decoration: none;
font-family: 'Space Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
transition: all 0.2s ease;
position: relative;
}
.nb-nav-link:hover {
color: var(--nb-accent);
}
.nb-nav-link:hover::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
right: 0;
height: 2px;
background: var(--nb-accent);
box-shadow: 0 0 8px var(--nb-accent);
}
.nb-nav-repo {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--nb-muted);
text-decoration: none;
font-family: 'Space Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
transition: color 0.2s ease;
}
.nb-nav-repo:hover {
color: var(--nb-accent);
}
/* Mobile toggle */
.nb-mobile-toggle {
display: none;
background: transparent;
border: 2px solid var(--nb-accent);
padding: 8px 16px;
cursor: pointer;
color: var(--nb-accent);
font-family: 'Space Mono', monospace;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.nb-mobile-menu {
display: none;
position: fixed;
top: 60px;
left: 0;
right: 0;
background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 2px solid var(--nb-accent);
padding: 24px 48px;
z-index: 99;
flex-direction: column;
gap: 16px;
}
.nb-mobile-menu.open {
display: flex;
}
/* --- Buttons --- */
.nb-btn-primary {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 18px 36px;
background: var(--nb-primary);
color: white;
font-weight: 700;
font-size: 15px;
letter-spacing: 0.04em;
text-transform: uppercase;
text-decoration: none;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
border: none;
cursor: pointer;
position: relative;
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
}
.nb-btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 32px color-mix(in srgb, var(--nb-primary) 40%, transparent), 0 0 60px color-mix(in srgb, var(--nb-primary) 15%, transparent);
}
.nb-btn-primary.small {
padding: 12px 24px;
font-size: 12px;
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
}
.nb-btn-secondary {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 18px 36px;
background: transparent;
color: white;
font-weight: 700;
font-size: 15px;
letter-spacing: 0.04em;
text-transform: uppercase;
text-decoration: none;
border: 2px solid var(--nb-border-hard);
transition: all 0.3s ease;
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
}
.nb-btn-secondary:hover {
border-color: var(--nb-accent);
color: var(--nb-accent);
transform: translateY(-3px);
box-shadow: 0 0 20px color-mix(in srgb, var(--nb-accent) 15%, transparent);
}
.nb-btn-cta {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 20px 44px;
background: var(--nb-text);
color: var(--nb-bg);
font-weight: 800;
font-size: 16px;
letter-spacing: 0.04em;
text-transform: uppercase;
text-decoration: none;
transition: all 0.3s ease;
position: relative;
z-index: 1;
clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 14px 100%, 0 calc(100% - 14px));
}
.nb-btn-cta:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 12px 40px rgba(255, 255, 255, 0.25);
}
/* --- Hero --- */
.nb-hero {
position: relative;
z-index: 1;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 160px 48px 120px;
text-align: center;
}
/* Hero accent shapes */
.nb-hero-accent {
position: absolute;
z-index: 0;
pointer-events: none;
}
.nb-hero-accent-1 {
top: 15%;
left: 5%;
width: 200px;
height: 200px;
border: 2px solid color-mix(in srgb, var(--nb-primary) 15%, transparent);
transform: rotate(45deg);
}
.nb-hero-accent-2 {
bottom: 20%;
right: 8%;
width: 150px;
height: 150px;
border: 2px solid color-mix(in srgb, var(--nb-accent) 12%, transparent);
border-radius: 50%;
}
.nb-hero-accent-3 {
top: 30%;
right: 15%;
font-family: 'Space Mono', monospace;
font-size: 120px;
font-weight: 700;
color: rgba(255, 255, 255, 0.02);
line-height: 1;
letter-spacing: -0.05em;
}
.nb-hero-content {
position: relative;
z-index: 1;
max-width: 1000px;
}
.nb-hero-label {
font-family: 'Space Mono', monospace;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--nb-accent);
margin-bottom: 28px;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.nb-hero-label::before,
.nb-hero-label::after {
content: '';
width: 32px;
height: 1px;
background: var(--nb-accent);
box-shadow: 0 0 6px var(--nb-accent);
}
.nb-hero h1 {
font-size: clamp(48px, 8vw, 96px);
font-weight: 800;
line-height: 1.0;
margin-bottom: 28px;
letter-spacing: -0.04em;
text-transform: uppercase;
}
.nb-hero-sub {
font-size: clamp(16px, 2vw, 22px);
color: var(--nb-muted);
line-height: 1.7;
margin-bottom: 48px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
font-weight: 400;
}
.nb-hero-buttons {
display: flex;
gap: 16px;
justify-content: center;
flex-wrap: wrap;
}
/* --- Stats --- */
.nb-stats {
position: relative;
z-index: 1;
padding: 80px 48px;
border-top: 1px solid var(--nb-border-hard);
border-bottom: 1px solid var(--nb-border-hard);
}
.nb-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0;
max-width: 1000px;
margin: 0 auto;
}
.nb-stat-card {
text-align: center;
padding: 40px 24px;
border-right: 1px solid var(--nb-border-hard);
position: relative;
}
.nb-stat-card:last-child {
border-right: none;
}
.nb-stat-value {
font-size: 56px;
font-weight: 800;
margin-bottom: 4px;
letter-spacing: -0.03em;
color: var(--nb-accent);
text-shadow: 0 0 30px color-mix(in srgb, var(--nb-accent) 20%, transparent);
}
.nb-stat-label {
font-family: 'Space Mono', monospace;
font-size: 11px;
color: var(--nb-subtle);
text-transform: uppercase;
letter-spacing: 0.15em;
font-weight: 700;
}
/* --- Downloads --- */
.nb-downloads {
position: relative;
z-index: 1;
padding: 80px 48px;
border-top: 1px solid var(--nb-border-hard);
border-bottom: 1px solid var(--nb-border-hard);
}
.nb-downloads-inner {
max-width: 900px;
margin: 0 auto;
}
.nb-downloads-header {
margin-bottom: 28px;
text-align: center;
}
.nb-downloads-header h2 {
font-size: 32px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
.nb-downloads-header p {
font-family: 'Space Mono', monospace;
font-size: 12px;
color: var(--nb-subtle);
text-transform: uppercase;
letter-spacing: 0.15em;
font-weight: 700;
}
.nb-downloads-grid {
display: flex;
flex-direction: column;
gap: 2px;
}
.nb-download-item {
display: flex;
align-items: center;
gap: 16px;
padding: 18px 24px;
background: var(--nb-surface);
color: var(--nb-text);
text-decoration: none;
font-family: 'Space Mono', monospace;
font-size: 13px;
font-weight: 700;
transition: all 0.3s ease;
border-left: 3px solid transparent;
}
.nb-download-item:hover {
background: #161616;
border-left-color: var(--nb-accent);
transform: translateX(4px);
}
.nb-download-size {
margin-left: auto;
font-size: 11px;
color: var(--nb-subtle);
letter-spacing: 0.04em;
}
/* --- Trust bar --- */
.nb-trust-bar {
position: relative;
z-index: 1;
padding: 48px;
}
.nb-trust-label {
text-align: center;
font-family: 'Space Mono', monospace;
font-size: 11px;
color: var(--nb-subtle);
margin-bottom: 28px;
text-transform: uppercase;
letter-spacing: 0.2em;
font-weight: 700;
}
.nb-trust-logos {
display: flex;
justify-content: center;
gap: 56px;
flex-wrap: wrap;
}
.nb-trust-logo {
font-family: 'Syne', sans-serif;
font-size: 16px;
font-weight: 700;
color: var(--nb-subtle);
text-transform: uppercase;
letter-spacing: 0.05em;
transition: all 0.3s ease;
}
.nb-trust-logo:hover {
color: var(--nb-accent);
text-shadow: 0 0 12px color-mix(in srgb, var(--nb-accent) 30%, transparent);
}
/* --- Section header --- */
.nb-section-label {
font-family: 'Space Mono', monospace;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--nb-primary);
text-shadow: 0 0 12px color-mix(in srgb, var(--nb-primary) 30%, transparent);
margin-bottom: 16px;
}
.nb-section-header {
text-align: center;
margin-bottom: 72px;
}
.nb-section-header h2 {
font-size: clamp(36px, 5vw, 60px);
font-weight: 800;
margin-bottom: 16px;
letter-spacing: -0.03em;
text-transform: uppercase;
}
.nb-section-header p {
font-size: 18px;
color: var(--nb-muted);
font-weight: 400;
}
/* --- Value props --- */
.nb-value-props {
position: relative;
z-index: 1;
padding: 120px 48px;
}
.nb-value-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2px;
max-width: 1200px;
margin: 0 auto;
background: var(--nb-border-hard);
}
.nb-value-tile {
padding: 44px;
background: var(--nb-surface);
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
position: relative;
overflow: hidden;
}
.nb-value-tile::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 0;
background: var(--nb-accent);
box-shadow: 0 0 12px var(--nb-accent);
transition: height 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.nb-value-tile:hover {
background: #161616;
}
.nb-value-tile:hover::before {
height: 100%;
}
.nb-value-icon {
width: 56px;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--nb-accent) 6%, transparent);
border: 1px solid color-mix(in srgb, var(--nb-accent) 15%, transparent);
color: var(--nb-accent);
margin-bottom: 20px;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
}
.nb-value-title {
font-size: 24px;
font-weight: 700;
margin-bottom: 10px;
letter-spacing: -0.01em;
}
.nb-value-desc {
font-size: 15px;
color: var(--nb-muted);
line-height: 1.7;
}
/* --- Features --- */
.nb-features {
position: relative;
z-index: 1;
padding: 100px 48px;
border-top: 1px solid var(--nb-border-hard);
}
.nb-feature-list {
display: flex;
flex-direction: column;
gap: 2px;
max-width: 900px;
margin: 0 auto;
}
.nb-feature-row {
display: flex;
align-items: center;
gap: 20px;
padding: 20px 28px;
background: var(--nb-surface);
transition: all 0.3s ease;
border-left: 3px solid transparent;
}
.nb-feature-row:hover {
background: #161616;
border-left-color: var(--nb-neon-yellow);
transform: translateX(4px);
}
.nb-feature-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
color: var(--nb-neon-yellow);
flex-shrink: 0;
}
.nb-feature-title {
font-weight: 700;
font-size: 16px;
letter-spacing: -0.01em;
}
.nb-feature-desc {
font-size: 14px;
color: var(--nb-muted);
margin-left: auto;
max-width: 400px;
text-align: right;
}
/* --- Pricing --- */
.nb-pricing {
position: relative;
z-index: 1;
padding: 120px 48px;
border-top: 1px solid var(--nb-border-hard);
}
.nb-pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2px;
max-width: 1100px;
margin: 0 auto;
background: var(--nb-border-hard);
}
.nb-pricing-card {
padding: 44px;
background: var(--nb-surface);
position: relative;
transition: all 0.3s ease;
}
.nb-pricing-card:hover {
background: #161616;
}
.nb-pricing-card.featured {
background: #161616;
}
.nb-pricing-card.featured::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--nb-primary) 0%, var(--nb-accent) 100%);
box-shadow: 0 0 20px color-mix(in srgb, var(--nb-primary) 30%, transparent);
}
.nb-pricing-badge {
display: inline-block;
font-family: 'Space Mono', monospace;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--nb-bg);
background: var(--nb-neon-yellow);
padding: 4px 12px;
margin-bottom: 16px;
}
.nb-pricing-name {
font-family: 'Space Mono', monospace;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--nb-muted);
margin-bottom: 12px;
}
.nb-pricing-price {
font-size: 52px;
font-weight: 800;
margin-bottom: 4px;
letter-spacing: -0.03em;
}
.nb-pricing-card.featured .nb-pricing-price {
color: var(--nb-accent);
text-shadow: 0 0 20px color-mix(in srgb, var(--nb-accent) 20%, transparent);
}
.nb-pricing-period {
font-family: 'Space Mono', monospace;
font-size: 12px;
color: var(--nb-subtle);
margin-bottom: 28px;
letter-spacing: 0.05em;
}
.nb-pricing-features {
list-style: none;
padding: 0;
margin: 0 0 28px;
}
.nb-pricing-features li {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 0;
font-size: 14px;
color: var(--nb-muted);
border-bottom: 1px solid var(--nb-border);
}
.nb-pricing-features li:last-child {
border-bottom: none;
}
.nb-pricing-features li::before {
content: "//";
font-family: 'Space Mono', monospace;
color: var(--nb-accent);
font-weight: 700;
font-size: 12px;
}
.nb-pricing-cta {
display: block;
width: 100%;
padding: 14px 28px;
text-align: center;
background: transparent;
border: 2px solid var(--nb-border-hard);
font-family: 'Syne', sans-serif;
font-weight: 700;
font-size: 14px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: white;
text-decoration: none;
transition: all 0.3s ease;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
}
.nb-pricing-cta:hover {
border-color: var(--nb-accent);
color: var(--nb-accent);
box-shadow: 0 0 16px color-mix(in srgb, var(--nb-accent) 15%, transparent);
}
.nb-pricing-card.featured .nb-pricing-cta {
background: var(--nb-primary);
border-color: var(--nb-primary);
}
.nb-pricing-card.featured .nb-pricing-cta:hover {
box-shadow: 0 0 24px color-mix(in srgb, var(--nb-primary) 40%, transparent);
transform: translateY(-2px);
}
/* --- Testimonial --- */
.nb-testimonial-section {
position: relative;
z-index: 1;
padding: 100px 48px;
}
.nb-testimonial-block {
position: relative;
padding: 72px;
background: var(--nb-surface);
border: 1px solid var(--nb-border-hard);
max-width: 1000px;
margin: 0 auto;
}
.nb-testimonial-block::before {
content: '';
position: absolute;
top: -1px;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--nb-primary), var(--nb-neon-yellow), var(--nb-accent));
}
.nb-quote-mark {
font-family: 'Syne', sans-serif;
font-size: 160px;
font-weight: 800;
line-height: 0.7;
color: color-mix(in srgb, var(--nb-accent) 6%, transparent);
position: absolute;
top: 40px;
left: 48px;
}
.nb-testimonial-quote {
font-size: clamp(22px, 3vw, 34px);
font-weight: 500;
line-height: 1.4;
margin-bottom: 36px;
position: relative;
z-index: 1;
letter-spacing: -0.01em;
}
.nb-testimonial-author {
display: flex;
align-items: center;
gap: 16px;
position: relative;
z-index: 1;
}
.nb-testimonial-avatar {
width: 56px;
height: 56px;
background: linear-gradient(135deg, var(--nb-primary) 0%, var(--nb-accent) 100%);
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
overflow: hidden;
}
.nb-testimonial-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.nb-testimonial-name {
font-weight: 700;
font-size: 16px;
letter-spacing: -0.01em;
}
.nb-testimonial-role {
font-family: 'Space Mono', monospace;
font-size: 12px;
color: var(--nb-subtle);
letter-spacing: 0.04em;
}
/* --- CTA --- */
.nb-cta-section {
position: relative;
z-index: 1;
padding: 100px 48px;
}
.nb-cta-banner {
position: relative;
padding: 100px 80px;
text-align: center;
overflow: hidden;
max-width: 1100px;
margin: 0 auto;
background: var(--nb-surface);
border: 1px solid var(--nb-border-hard);
}
.nb-cta-banner::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 30% 50%, color-mix(in srgb, var(--nb-primary) 8%, transparent) 0%, transparent 50%),
radial-gradient(ellipse at 70% 50%, color-mix(in srgb, var(--nb-accent) 8%, transparent) 0%, transparent 50%);
pointer-events: none;
}
.nb-cta-banner::after {
content: '';
position: absolute;
top: -1px;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--nb-primary), var(--nb-neon-yellow), var(--nb-accent));
}
.nb-cta-banner h2 {
font-size: clamp(32px, 5vw, 64px);
font-weight: 800;
margin-bottom: 16px;
position: relative;
z-index: 1;
letter-spacing: -0.03em;
text-transform: uppercase;
}
.nb-cta-banner p {
font-size: 20px;
color: var(--nb-muted);
margin-bottom: 36px;
position: relative;
z-index: 1;
}
/* --- Footer --- */
.nb-footer {
position: relative;
z-index: 1;
padding: 40px 48px;
border-top: 1px solid var(--nb-border-hard);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 24px;
}
.nb-footer-brand {
display: flex;
align-items: center;
gap: 12px;
}
.nb-footer-name {
font-weight: 800;
font-size: 16px;
text-transform: uppercase;
letter-spacing: -0.01em;
}
.nb-footer-copyright {
font-family: 'Space Mono', monospace;
color: var(--nb-subtle);
font-size: 11px;
letter-spacing: 0.04em;
}
.nb-footer-links {
display: flex;
gap: 28px;
flex-wrap: wrap;
}
.nb-footer-link {
color: var(--nb-subtle);
text-decoration: none;
font-family: 'Space Mono', monospace;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
transition: all 0.2s ease;
}
.nb-footer-link:hover {
color: var(--nb-accent);
}
.nb-footer-social {
display: flex;
gap: 16px;
}
.nb-footer-social a {
color: var(--nb-subtle);
transition: all 0.2s ease;
}
.nb-footer-social a:hover {
color: var(--nb-accent);
filter: drop-shadow(0 0 6px color-mix(in srgb, var(--nb-accent) 40%, transparent));
}
/* --- Responsive --- */
@media (max-width: 1024px) {
.nb-hero h1 { font-size: 64px; }
.nb-section-header h2 { font-size: 44px; }
.nb-cta-banner h2 { font-size: 44px; }
.nb-testimonial-quote { font-size: 26px; }
.nb-testimonial-block { padding: 56px; }
.nb-feature-desc { display: none; }
.nb-hero-accent { display: none; }
}
@media (max-width: 768px) {
.nb-nav { padding: 14px 24px; }
.nb-nav-links { display: none; }
.nb-mobile-toggle { display: block; }
.nb-mobile-menu { padding: 24px; }
.nb-hero { padding: 120px 24px 80px; }
.nb-hero h1 { font-size: 40px; }
.nb-hero-sub { font-size: 16px; }
.nb-hero-buttons { flex-direction: column; align-items: center; }
.nb-btn-primary, .nb-btn-secondary { width: 100%; justify-content: center; padding: 16px 28px; }
.nb-stats { padding: 48px 24px; }
.nb-stats-grid { grid-template-columns: 1fr 1fr; }
.nb-stat-card { border-right: none; border-bottom: 1px solid var(--nb-border-hard); padding: 28px 16px; }
.nb-stat-value { font-size: 40px; }
.nb-trust-logos { gap: 28px; }
.nb-value-props { padding: 80px 24px; }
.nb-value-grid { grid-template-columns: 1fr; }
.nb-value-tile { padding: 32px; }
.nb-features { padding: 64px 24px; }
.nb-feature-row { padding: 16px 20px; }
.nb-pricing { padding: 80px 24px; }
.nb-pricing-grid { grid-template-columns: 1fr; }
.nb-pricing-card { padding: 32px; }
.nb-pricing-price { font-size: 40px; }
.nb-testimonial-section { padding: 64px 24px; }
.nb-testimonial-block { padding: 36px 24px; }
.nb-quote-mark { font-size: 80px; top: 16px; left: 16px; }
.nb-testimonial-quote { font-size: 20px; }
.nb-cta-section { padding: 64px 24px; }
.nb-cta-banner { padding: 56px 24px; }
.nb-cta-banner h2 { font-size: 32px; }
.nb-cta-banner p { font-size: 16px; }
.nb-downloads { padding: 48px 24px; }
.nb-footer { flex-direction: column; text-align: center; padding: 32px 24px; }
.nb-section-header h2 { font-size: 32px; }
}
/* Blog content markdown styles */
.nb-blog-content h1, .nb-blog-content h2, .nb-blog-content h3, .nb-blog-content h4 {
color: var(--nb-text); font-family: 'Syne', sans-serif; margin: 1.5em 0 0.5em; text-transform: uppercase; letter-spacing: 0.02em;
}
.nb-blog-content h2 { font-size: 1.5em; border-bottom: 2px solid var(--nb-border-hard); padding-bottom: 8px; text-transform: none; }
.nb-blog-content h3 { font-size: 1.25em; text-transform: none; }
.nb-blog-content a { color: var(--nb-accent); text-decoration: underline; }
.nb-blog-content a:hover { color: var(--nb-primary); }
.nb-blog-content code { background: var(--nb-surface); padding: 2px 6px; border-radius: 2px; font-family: 'Space Mono', monospace; font-size: 0.9em; }
.nb-blog-content pre { background: var(--nb-surface); border: 1px solid var(--nb-border-hard); border-radius: 0; padding: 16px; overflow-x: auto; margin: 16px 0; clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%); }
.nb-blog-content pre code { background: none; padding: 0; }
.nb-blog-content blockquote { border-left: 3px solid var(--nb-primary); padding-left: 16px; color: var(--nb-muted); margin: 16px 0; font-style: italic; }
.nb-blog-content img { max-width: 100%; margin: 16px 0; }
.nb-blog-content ul, .nb-blog-content ol { padding-left: 24px; margin: 12px 0; }
.nb-blog-content li { margin: 4px 0; }
.nb-blog-content table { border-collapse: collapse; width: 100%; margin: 16px 0; }
.nb-blog-content th, .nb-blog-content td { border: 1px solid var(--nb-border-hard); padding: 8px 12px; text-align: left; }
.nb-blog-content th { background: var(--nb-surface); font-weight: 700; font-family: 'Syne', sans-serif; text-transform: uppercase; font-size: 0.85em; }
.nb-blog-content hr { border: none; border-top: 2px solid var(--nb-border-hard); margin: 24px 0; }
</style>
<div class="nb-page">
<!-- Navigation -->
<nav class="nb-nav">
<a href="/" class="nb-nav-brand">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}</a>
<div class="nb-nav-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="nb-nav-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">Docs</a>{{end}}
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">API</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">Issues</a>{{end}}
{{if .Config.ValueProps}}<a href="#value-props" class="nb-nav-link">Why</a>{{end}}
{{if .Config.Features}}<a href="#features" class="nb-nav-link">Features</a>{{end}}
{{if .Config.Pricing.Plans}}<a href="#pricing" class="nb-nav-link">Pricing</a>{{end}}
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}#blog{{end}}" class="nb-nav-link">Blog</a>{{end}}
{{if .Config.Gallery.Enabled}}<a href="#gallery" class="nb-nav-link">Gallery</a>{{end}}
{{if .Config.Navigation.ShowRepository}}
<a href="{{.RepoURL}}" class="nb-nav-repo">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
Repo
</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}}
{{if .Config.Hero.PrimaryCTA.Label}}
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="nb-btn-primary small">
{{.Config.Hero.PrimaryCTA.Label}}
</a>
{{end}}
</div>
<button class="nb-mobile-toggle" onclick="document.getElementById('nb-mobile-nav').classList.toggle('open')">Menu</button>
</nav>
<div class="nb-mobile-menu" id="nb-mobile-nav">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="nb-nav-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-nav-link">Docs</a>{{end}}
{{if .Config.Navigation.ShowAPI}}<a href="{{.RepoURL}}/swagger" class="nb-nav-link">API</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-nav-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-nav-link">Issues</a>{{end}}
{{if .Config.ValueProps}}<a href="#value-props" class="nb-nav-link">Why</a>{{end}}
{{if .Config.Features}}<a href="#features" class="nb-nav-link">Features</a>{{end}}
{{if .Config.Pricing.Plans}}<a href="#pricing" class="nb-nav-link">Pricing</a>{{end}}
{{if .Config.Blog.Enabled}}<a href="{{if .BlogBaseURL}}{{.BlogBaseURL}}{{else}}#blog{{end}}" class="nb-nav-link">Blog</a>{{end}}
{{if .Config.Gallery.Enabled}}<a href="#gallery" class="nb-nav-link">Gallery</a>{{end}}
{{if .Config.Navigation.ShowRepository}}
<a href="{{.RepoURL}}" class="nb-nav-repo">
<img src="/assets/img/gitcaddy-icon.svg" width="16" height="16" alt="GitCaddy">
Repository
</a>
{{end}}
</div>
{{if .PageIsBlogDetail}}
<!-- Blog Detail View -->
<section class="nb-hero" style="padding-top: 120px; min-height: auto;">
<div style="max-width: 800px; margin: 0 auto; padding: 0 24px;">
{{if .BlogPost.FeaturedImage}}
<div style="margin-bottom: 32px; overflow: hidden; clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px));">
<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: 'Syne', sans-serif; font-size: 36px; font-weight: 800; text-transform: uppercase; margin-bottom: 8px;">{{.BlogPost.Title}}</h1>
{{if .BlogPost.Subtitle}}<p style="font-size: 1.2rem; color: var(--nb-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(--nb-muted); font-family: 'Space Mono', monospace;">
{{if .BlogPost.Author}}<span>{{.BlogPost.Author.Name}}</span><span>&middot;</span>{{end}}
<span>{{DateUtils.AbsoluteShort .BlogPost.CreatedUnix}}</span>
{{if .BlogTags}}<span>&middot;</span>{{range .BlogTags}}<span style="background: var(--nb-surface); border: 1px solid var(--nb-border-hard); padding: 2px 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;">{{.}}</span> {{end}}{{end}}
</div>
<div class="markup nb-blog-content" style="color: var(--nb-text); line-height: 1.8; font-size: 16px; text-align: left;">
{{.BlogRenderedContent}}
</div>
<div style="margin-top: 48px; padding-top: 24px; border-top: 2px solid var(--nb-border-hard);">
<a href="{{.BlogBaseURL}}" class="nb-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="nb-features" style="padding-top: 120px;">
<div class="nb-features-inner">
<div class="nb-section-header nb-reveal">
<div class="nb-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="nb-feature-list">
{{range .BlogListPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="nb-feature-row nb-reveal" style="text-decoration: none; color: inherit;">
{{if .FeaturedImage}}
<div style="width: 120px; min-width: 120px; height: 80px; overflow: hidden; clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);">
<img src="{{.FeaturedImage.DownloadURL}}" alt="{{.Title}}" style="width: 100%; height: 100%; object-fit: cover; display: block;">
</div>
{{end}}
<div class="nb-feature-content" style="flex: 1;">
<h3 class="nb-feature-name">{{.Title}}</h3>
<p class="nb-feature-desc">
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}{{.Subtitle}} &middot; {{end}}
{{if .Author}}{{.Author.Name}} &middot; {{end}}{{DateUtils.AbsoluteShort .CreatedUnix}}
</p>
</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="nb-hero">
<div class="nb-hero-accent nb-hero-accent-1"></div>
<div class="nb-hero-accent nb-hero-accent-2"></div>
<div class="nb-hero-accent nb-hero-accent-3">*</div>
<div class="nb-hero-content">
<div class="nb-hero-label nb-reveal">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}Open Source{{end}}</div>
<h1 class="nb-reveal nb-reveal-delay-1">
{{if .Config.Hero.Headline}}<span class="nb-glow-text">{{.Config.Hero.Headline}}</span>{{else}}<span class="nb-glow-text">{{.Repository.Name}}</span>{{end}}
</h1>
<p class="nb-hero-sub nb-reveal nb-reveal-delay-2">{{if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}</p>
<div class="nb-hero-buttons nb-reveal nb-reveal-delay-3">
{{if .Config.Hero.PrimaryCTA.Label}}
<a href="{{.Config.Hero.PrimaryCTA.URL}}" class="nb-btn-primary" data-cta="primary">
{{.Config.Hero.PrimaryCTA.Label}}
{{svg "octicon-arrow-right" 18}}
</a>
{{end}}
{{if .Config.Hero.SecondaryCTA.Label}}
<a href="{{.Config.Hero.SecondaryCTA.URL}}" class="nb-btn-secondary" data-cta="secondary">
{{svg "octicon-play" 18}}
{{.Config.Hero.SecondaryCTA.Label}}
</a>
{{end}}
</div>
</div>
</section>
{{if .Config.Stats}}
<!-- Stats Section -->
<section class="nb-stats">
<div class="nb-stats-grid">
{{range .Config.Stats}}
<div class="nb-stat-card nb-reveal">
<div class="nb-stat-value">{{.Value}}</div>
<div class="nb-stat-label">{{.Label}}</div>
</div>
{{end}}
</div>
</section>
{{end}}
{{if and .PublicReleases .LatestRelease .LatestRelease.Attachments}}
<section class="nb-downloads">
<div class="nb-downloads-inner">
<div class="nb-downloads-header nb-reveal">
<div class="nb-section-label">Download</div>
<h2><span class="nb-glow-text">v{{.LatestReleaseTag}}</span></h2>
<p>Get the latest release</p>
</div>
<div class="nb-downloads-grid">
{{range .LatestRelease.Attachments}}
<a href="{{$.RepoURL}}/releases/download/{{$.LatestRelease.TagName}}/{{.Name}}" class="nb-download-item nb-reveal">
{{svg "octicon-download" 16}}
{{.Name}}
<span class="nb-download-size">{{FileSize .Size}}</span>
</a>
{{end}}
</div>
</div>
</section>
{{end}}
{{if .Config.SocialProof.Logos}}
<!-- Trust Bar -->
<section class="nb-trust-bar">
<p class="nb-trust-label nb-reveal">Trusted by the best</p>
<div class="nb-trust-logos nb-reveal nb-reveal-delay-1">
{{range .Config.SocialProof.Logos}}
<span class="nb-trust-logo">{{.}}</span>
{{end}}
</div>
</section>
{{end}}
{{if .Config.ValueProps}}
<!-- Value Props Section -->
<section class="nb-value-props" id="value-props">
<div class="nb-section-header nb-reveal">
<div class="nb-section-label">Why choose this</div>
<h2>Unlock your <span class="nb-glow-primary">potential</span></h2>
<p>Everything you need to create without limits</p>
</div>
<div class="nb-value-grid">
{{range .Config.ValueProps}}
<div class="nb-value-tile nb-reveal">
<div class="nb-value-icon">
{{svg (printf "octicon-%s" (or .Icon "zap")) 28}}
</div>
<h3 class="nb-value-title">{{.Title}}</h3>
<p class="nb-value-desc">{{.Description}}</p>
</div>
{{end}}
</div>
</section>
{{end}}
{{if .Config.Features}}
<!-- Features Section -->
<section class="nb-features" id="features">
<div class="nb-section-header nb-reveal">
<div class="nb-section-label">Capabilities</div>
<h2>Packed with <span class="nb-glow-text">power</span></h2>
</div>
<div class="nb-feature-list">
{{range .Config.Features}}
<div class="nb-feature-row nb-reveal">
<div class="nb-feature-icon">
{{svg (printf "octicon-%s" (or .Icon "zap")) 22}}
</div>
<div class="nb-feature-title">{{.Title}}</div>
<div class="nb-feature-desc">{{.Description}}</div>
</div>
{{end}}
</div>
</section>
{{end}}
{{if .Config.Pricing.Plans}}
<!-- Pricing Section -->
<section class="nb-pricing" id="pricing">
<div class="nb-section-header nb-reveal">
<div class="nb-section-label">Investment</div>
<h2>{{if .Config.Pricing.Headline}}{{.Config.Pricing.Headline}}{{else}}Simple <span class="nb-glow-text">Pricing</span>{{end}}</h2>
<p>Choose the plan that works for you</p>
</div>
<div class="nb-pricing-grid">
{{range .Config.Pricing.Plans}}
<div class="nb-pricing-card{{if .Featured}} featured{{end}} nb-reveal">
{{if .Featured}}<span class="nb-pricing-badge">Popular</span>{{end}}
<div class="nb-pricing-name">{{.Name}}</div>
<div class="nb-pricing-price">{{.Price}}</div>
<div class="nb-pricing-period">{{.Period}}</div>
{{if .Features}}
<ul class="nb-pricing-features">
{{range .Features}}
<li>{{.}}</li>
{{end}}
</ul>
{{end}}
<a href="#" class="nb-pricing-cta">{{if .CTA}}{{.CTA}}{{else}}Get Started{{end}}</a>
</div>
{{end}}
</div>
</section>
{{end}}
<!-- Testimonials Section -->
{{if .Config.SocialProof.Testimonials}}
<section class="nb-testimonial-section">
<div class="nb-testimonial-block nb-reveal">
<span class="nb-quote-mark">&ldquo;</span>
<div class="nb-testimonials-container">
{{range .Config.SocialProof.Testimonials}}
<div class="nb-testimonial-item" style="display: none;">
<blockquote class="nb-testimonial-quote">{{.Quote}}</blockquote>
<div class="nb-testimonial-author">
<div class="nb-testimonial-avatar">
{{if .Avatar}}<img src="{{.Avatar}}" alt="{{.Author}}">{{end}}
</div>
<div>
<div class="nb-testimonial-name">{{.Author}}</div>
<div class="nb-testimonial-role">{{.Role}}</div>
</div>
</div>
</div>
{{end}}
</div>
</div>
</section>
<script>
(function() {
var items = document.querySelectorAll(".nb-testimonial-item");
if (items.length > 0) {
var idx = Math.floor(Math.random() * items.length);
items[idx].style.display = "block";
}
})();
</script>
{{end}}
{{if .Config.CTASection.Headline}}
<!-- CTA Banner -->
<section class="nb-cta-section">
<div class="nb-cta-banner nb-reveal">
<h2>{{.Config.CTASection.Headline}}</h2>
{{if .Config.CTASection.Subheadline}}
<p>{{.Config.CTASection.Subheadline}}</p>
{{end}}
{{if .Config.CTASection.Button.Label}}
<a href="{{.Config.CTASection.Button.URL}}" class="nb-btn-cta" data-cta="cta-section">
{{.Config.CTASection.Button.Label}}
{{svg "octicon-arrow-right" 20}}
</a>
{{end}}
</div>
</section>
{{end}}
<!-- Blog Section -->
{{if and .Config.Blog.Enabled .BlogPosts}}
<section class="nb-features" id="blog" style="border-top: 1px solid var(--nb-border-hard);">
<div class="nb-section-header nb-reveal">
<div class="nb-section-label">Blog</div>
<h2>{{if .Config.Blog.Headline}}<span class="nb-glow-text">{{.Config.Blog.Headline}}</span>{{else}}Latest <span class="nb-glow-text">Posts</span>{{end}}</h2>
{{if .Config.Blog.Subheadline}}<p>{{.Config.Blog.Subheadline}}</p>{{end}}
</div>
<div class="nb-feature-list">
{{range .BlogPosts}}
<a href="{{$.BlogBaseURL}}/{{.ID}}" class="nb-feature-row nb-reveal" style="text-decoration: none; color: inherit;">
{{if .FeaturedImage}}
<div style="flex-shrink: 0; width: 80px; height: 56px; overflow: hidden; clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));">
<img src="{{.FeaturedImage.DownloadURL}}" alt="{{.Title}}" style="width: 100%; height: 100%; object-fit: cover;">
</div>
{{else}}
<div class="nb-feature-icon">
{{svg "octicon-note" 22}}
</div>
{{end}}
<div class="nb-feature-title">{{.Title}}</div>
<div class="nb-feature-desc">
{{if and $.Config.Blog.ShowExcerpt .Subtitle}}{{.Subtitle}}{{else}}{{if .Author}}{{.Author.Name}}{{end}} · {{DateUtils.AbsoluteShort .CreatedUnix}}{{end}}
</div>
</a>
{{end}}
</div>
{{if .Config.Blog.CTAButton.Label}}
<div style="text-align: center; margin-top: 48px;" class="nb-reveal">
<a href="{{if .Config.Blog.CTAButton.URL}}{{.Config.Blog.CTAButton.URL}}{{else}}{{.BlogBaseURL}}{{end}}" class="nb-btn-secondary" data-cta="secondary">
{{.Config.Blog.CTAButton.Label}}
{{svg "octicon-arrow-right" 18}}
</a>
</div>
{{end}}
</section>
{{end}}
<!-- Gallery Section -->
{{if and .Config.Gallery.Enabled .GalleryImages}}
<section class="nb-features" id="gallery" style="border-top: 1px solid var(--nb-border-hard);">
<div class="nb-section-header nb-reveal">
<div class="nb-section-label">Gallery</div>
<h2><span class="nb-glow-text">{{if .Config.Gallery.Headline}}{{.Config.Gallery.Headline}}{{else}}Gallery{{end}}</span></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: 4px;">
{{range .GalleryImages}}
<div class="nb-reveal" style="overflow: hidden; clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));">
<a href="{{.URL}}" target="_blank" style="display: block; position: relative;">
<img src="{{.URL}}" alt="{{if .Caption}}{{.Caption}}{{else}}{{.Name}}{{end}}" style="width: 100%; height: 220px; object-fit: cover; display: block;">
{{if .Caption}}
<div style="position: absolute; bottom: 0; left: 0; right: 0; padding: 12px 16px; background: linear-gradient(transparent, rgba(0,0,0,0.7)); font-size: 13px; color: #fff;">{{.Caption}}</div>
{{end}}
</a>
</div>
{{end}}
</div>
</section>
{{end}}
{{end}}{{/* end PageIsBlogDetail / PageIsBlogList / else */}}
<!-- Footer -->
<footer class="nb-footer">
<div class="nb-footer-brand">
{{if .Config.Footer.Copyright}}
<span class="nb-footer-copyright">{{.Config.Footer.Copyright}}</span>
{{else}}
<span class="nb-footer-name">{{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}</span>
<span class="nb-footer-copyright">&copy; <script>document.write(new Date().getFullYear())</script></span>
{{end}}
</div>
{{if .Config.Footer.Social}}
<div class="nb-footer-social">
{{range .Config.Footer.Social}}
<a href="{{.URL}}" title="{{.Platform}}">
{{if eq .Platform "twitter"}}{{svg "octicon-mention" 18}}
{{else if eq .Platform "bluesky"}}{{svg "octicon-cloud" 18}}
{{else if eq .Platform "github"}}{{svg "octicon-mark-github" 18}}
{{else if eq .Platform "discord"}}{{svg "octicon-comment-discussion" 18}}
{{else if eq .Platform "linkedin"}}{{svg "octicon-briefcase" 18}}
{{else if eq .Platform "youtube"}}{{svg "octicon-video" 18}}
{{else if eq .Platform "instagram"}}{{svg "octicon-device-camera" 18}}
{{else if eq .Platform "facebook"}}{{svg "octicon-people" 18}}
{{else if eq .Platform "substack"}}{{svg "octicon-note" 18}}
{{else if eq .Platform "threads"}}{{svg "octicon-share" 18}}
{{else if eq .Platform "tiktok"}}{{svg "octicon-play" 18}}
{{else if eq .Platform "reddit"}}{{svg "octicon-hash" 18}}
{{else if eq .Platform "mastodon"}}{{svg "octicon-megaphone" 18}}
{{else if eq .Platform "twitch"}}{{svg "octicon-broadcast" 18}}
{{else if eq .Platform "rss"}}{{svg "octicon-rss" 18}}
{{else}}{{svg "octicon-link-external" 18}}{{end}}
</a>
{{end}}
</div>
{{end}}
<div class="nb-footer-links">
{{range .Config.Footer.Links}}
<a href="{{.URL}}" class="nb-footer-link">{{.Label}}</a>
{{end}}
{{if .Config.Navigation.ShowRepository}}<a href="{{.RepoURL}}" class="nb-footer-link">Repository</a>{{end}}
{{if .Config.Navigation.ShowDocs}}<a href="{{.RepoURL}}/wiki" class="nb-footer-link">Docs</a>{{end}}
{{if .Config.Navigation.ShowReleases}}<a href="{{.RepoURL}}/releases" class="nb-footer-link">Releases</a>{{end}}
{{if .Config.Navigation.ShowIssues}}<a href="{{.RepoURL}}/issues" class="nb-footer-link">Issues</a>{{end}}
</div>
</footer>
</div>
<script>
/* Scroll reveal */
(function() {
var reveals = document.querySelectorAll('.nb-reveal');
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.08, rootMargin: '0px 0px -40px 0px' });
reveals.forEach(function(el) { observer.observe(el); });
})();
</script>
{{template "pages/base_footer" .}}