Converti chill-theme en dossier classique

This commit is contained in:
Boris Waaub
2026-01-28 09:40:37 +01:00
parent 577786f22d
commit cc60b64505
118 changed files with 11868 additions and 1 deletions
@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode | default "en" }}" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Google Tag Manager -->
{{ partial "google-tag-manager" . }}
<!-- Google Analytics -->
{{ partial "google-analytics" . }}
<!-- Title -->
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
<!-- Meta Tags -->
<meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
<meta name="author" content="{{ .Site.Params.author | default .Site.Title }}">
<meta name="robots" content="{{ with .Params.robots }}{{ . }}{{ else }}index, follow{{ end }}">
<!-- Open Graph -->
<meta property="og:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}">
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
<meta property="og:type" content="{{ if .IsHome }}website{{ else }}article{{ end }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
{{ with .Params.image }}
<meta property="og:image" content="{{ . | absURL }}">
{{ else }}
{{ with .Site.Params.image }}
<meta property="og:image" content="{{ . | absURL }}">
{{ end }}
{{ end }}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}">
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
{{ with .Site.Params.twitter }}
<meta name="twitter:site" content="@{{ . }}">
{{ end }}
{{ with .Params.image }}
<meta name="twitter:image" content="{{ . | absURL }}">
{{ else }}
{{ with .Site.Params.image }}
<meta property="twitter:image" content="{{ . | absURL }}">
{{ end }}
{{ end }}
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="{{ "images/favicon.ico" | relURL }}">
<!-- Canonical URL -->
<link rel="canonical" href="{{ .Permalink }}">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@600;700;800&display=swap" rel="stylesheet">
<!-- Main CSS -->
<link rel="stylesheet" href="{{ "css/style.css" | relURL }}">
<!-- Additional Meta Tags from Front Matter -->
{{ with .Params.customMeta }}
{{ range . }}
<meta {{ range $key, $value := . }} {{ $key }}="{{ $value }}"{{ end }}>
{{ end }}
{{ end }}
<!-- Custom Head Content -->
{{ partial "custom-head" . }}
</head>
<body class="min-h-screen flex flex-col">
<!-- Google Tag Manager (noscript) -->
{{ if and hugo.IsProduction .Site.Params.googleTagManager }}
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ .Site.Params.googleTagManager }}"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
{{ end }}
<!-- End Google Tag Manager (noscript) -->
<!-- Header -->
<div class="fixed top-0 left-0 right-0 z-50">
{{ partial "header" . }}
</div>
<!-- Main Content -->
<div class="pt-20">
{{ block "main" . }}{{ end }}
</div>
<!-- Footer -->
{{ partial "footer" . }}
<!-- Language Preference Script -->
{{ if .Site.IsMultiLingual }}
{{ if .Site.Params.visitorapi_pid }}
<script>
// Inject VisitorAPI project ID from Hugo config
window.HUGO_VISITOR_API_PID = "{{ .Site.Params.visitorapi_pid }}";
</script>
{{ end }}
<script src="{{ "js/language-preference.js" | relURL }}"></script>
{{ end }}
<!-- Mobile Menu Script -->
<script>
const mobileMenuButton = document.getElementById('mobile-menu-button');
if (mobileMenuButton) {
mobileMenuButton.addEventListener('click', function() {
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenu) {
mobileMenu.classList.toggle('hidden');
}
});
}
</script>
</body>
</html>
@@ -0,0 +1,115 @@
{{ define "main" }}
<div class="bg-gray-50">
<!-- Hero Section -->
<div class="bg-gradient-to-r from-primary-600 to-primary-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24">
<div class="text-center">
<h1 class="text-4xl font-bold text-white mb-6">{{ .Title }}</h1>
<p class="text-xl text-white max-w-2xl mx-auto leading-relaxed">{{ .Description }}</p>
</div>
</div>
</div>
<!-- Team Culture Section -->
{{ with .Params.culture_section }}
<div class="bg-white py-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-gray-50 rounded-xl shadow-sm p-8 lg:p-12">
<div class="grid lg:grid-cols-2 gap-12 items-center">
<div>
<h2 class="text-3xl font-bold text-gray-900 mb-6">{{ .title }}</h2>
<div class="space-y-6">
{{ range .values }}
<div class="flex items-start">
<div class="flex-shrink-0">
<div class="flex items-center justify-center h-12 w-12 rounded-md bg-primary-600 text-white">
{{ .icon }}
</div>
</div>
<div class="ml-4">
<h3 class="text-xl font-semibold text-gray-900">{{ .title }}</h3>
<p class="mt-2 text-gray-600">{{ .description }}</p>
</div>
</div>
{{ end }}
</div>
</div>
<div class="relative">
<img src="{{ .image }}" alt="{{ .image_alt }}" class="rounded-lg shadow-xl">
<div class="absolute -bottom-4 -right-4 bg-white rounded-lg shadow-lg px-6 py-4">
<p class="text-gray-900 font-semibold">{{ .image_caption }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
{{ end }}
<!-- Benefits Section -->
{{ with .Params.benefits_section }}
<div class="bg-white py-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 class="text-3xl font-bold text-gray-900 text-center mb-12">{{ .title }}</h2>
<div class="grid md:grid-cols-3 gap-8">
{{ range .benefits }}
<div class="bg-gray-50 rounded-lg p-6 hover:shadow-md transition-shadow duration-200">
<div class="text-2xl mb-4">{{ .icon }}</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">{{ .title }}</h3>
<p class="text-gray-600">{{ .description }}</p>
</div>
{{ end }}
</div>
</div>
</div>
{{ end }}
<!-- Open Positions Section -->
{{ with .Params.positions_section }}
<div class="bg-white py-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-gray-50 rounded-xl shadow-sm p-8 lg:p-12">
<div class="text-center mb-12">
<h2 class="text-3xl font-bold text-gray-900 mb-4">{{ .title }}</h2>
<p class="text-lg text-gray-600 max-w-2xl mx-auto">Join our team and help shape the future of SaaS. We're always looking for talented individuals who share our passion for innovation.</p>
</div>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{{ range where $.Site.RegularPages "Section" "jobs" }}
<a href="{{ .RelPermalink }}" class="group">
<div class="h-full bg-white rounded-lg shadow-sm group-hover:shadow-md transition-all duration-200 overflow-hidden border border-gray-100 group-hover:border-primary-100">
<div class="p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-semibold text-gray-900 group-hover:text-primary-600 transition-colors duration-200">{{ .Title }}</h3>
<span class="flex items-center justify-center w-8 h-8 rounded-full bg-primary-50 text-primary-600 group-hover:bg-primary-100 transition-colors duration-200">
</span>
</div>
<div class="flex items-center text-gray-600 text-sm mb-4 space-x-4">
{{ with .Params.location }}
<span class="flex items-center">
<span class="mr-2">📍</span>
{{ . }}
</span>
{{ end }}
{{ with .Params.type }}
<span class="flex items-center">
<span class="mr-2">💼</span>
{{ . }}
</span>
{{ end }}
</div>
<p class="text-gray-600 mb-4 line-clamp-2">{{ .Description | default .Summary }}</p>
<div class="text-primary-600 font-medium group-hover:text-primary-700 transition-colors duration-200">
{{ $.Params.positions_section.view_position_text }} →
</div>
</div>
</div>
</a>
{{ end }}
</div>
</div>
</div>
</div>
{{ end }}
</div>
{{ end }}
@@ -0,0 +1,6 @@
{{ define "main" }}
<article class="post">
<!-- Hero section will be rendered by the hero shortcode -->
{{ .Content }}
</article>
{{ end }}
@@ -0,0 +1,76 @@
{{ define "main" }}
<article class="feature-page" style="--badge-color: {{ .Params.badgeColor }}">
<!-- Hero Section -->
<div class="relative isolate overflow-hidden">
<style>
.badge {
background-color: color-mix(in srgb, var(--badge-color) 10%, transparent);
color: var(--badge-color);
}
.gradient-bg {
background-image: linear-gradient(180deg, color-mix(in srgb, var(--badge-color) 5%, white), white);
}
</style>
<div class="absolute inset-x-0 top-0 h-96 gradient-bg opacity-75"></div>
<div class="relative pt-24 pb-16 sm:pt-32">
<div class="mx-auto max-w-7xl px-6 lg:px-8">
<div class="mx-auto max-w-2xl text-center">
{{ with .Params.badge }}
<div class="mb-6">
<span class="badge inline-flex items-center rounded-full px-4 py-1.5 text-sm font-medium">
{{ . }}
</span>
</div>
{{ end }}
<h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-6xl">{{ .Title }}</h1>
<p class="mt-6 text-lg leading-8 text-gray-600">{{ .Description }}</p>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="mx-auto max-w-7xl px-6 lg:px-8">
<!-- Key Features Grid -->
{{ if .Params.features }}
<div class="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none">
<div class="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 lg:max-w-none lg:grid-cols-2 xl:grid-cols-4">
{{ range .Params.features }}
<div class="flex flex-col bg-white rounded-2xl shadow-sm ring-1 ring-gray-200 p-8">
<dt class="text-lg font-semibold leading-7 text-gray-900">
{{ .title }}
</dt>
<dd class="mt-4 flex flex-auto flex-col text-base leading-7 text-gray-600">
<p class="flex-auto">{{ .description }}</p>
</dd>
</div>
{{ end }}
</div>
</div>
{{ end }}
<!-- Content Section -->
<div class="prose prose-lg mx-auto mt-16 pb-24">
{{ .Content }}
</div>
<!-- Demo Section -->
{{ if .Params.demo }}
<div class="bg-gray-50 -mx-6 px-6 py-24 sm:py-32">
<div class="mx-auto max-w-2xl lg:text-center">
<h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">{{ i18n "seeItInAction" }}</h2>
<p class="mt-6 text-lg leading-8 text-gray-600">{{ .Params.demo.description }}</p>
</div>
<div class="mt-16 flex justify-center">
<div class="relative rounded-xl bg-white p-8 shadow-2xl ring-1 ring-gray-200">
<img src="{{ .Params.demo.image }}" alt="Demo" class="rounded-lg">
</div>
</div>
</div>
{{ end }}
</div>
<!-- Global CTA -->
{{ partial "components/cta.html" . }}
</article>
{{ end }}
@@ -0,0 +1,51 @@
{{ define "main" }}
<div class="bg-gray-50 min-h-screen">
<!-- Job Header -->
<div class="bg-gradient-to-r from-primary-600 to-primary-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
<div class="text-center">
<h1 class="text-3xl font-bold text-white mb-6">{{ .Title }}</h1>
<div class="flex items-center justify-center text-white space-x-8 text-lg">
{{ with .Params.location }}
<span class="flex items-center">
<span class="mr-2">📍</span>
{{ . }}
</span>
{{ end }}
{{ with .Params.type }}
<span class="flex items-center">
<span class="mr-2">💼</span>
{{ . }}
</span>
{{ end }}
{{ with .Params.salary }}
<span class="flex items-center">
<span class="mr-2">💰</span>
{{ . }}
</span>
{{ end }}
</div>
</div>
</div>
</div>
<!-- Job Content -->
<div class="bg-white py-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-gray-50 rounded-xl shadow-sm p-8 lg:p-12">
<div class="prose prose-lg max-w-none">
<div class="text-gray-600 text-lg mb-8">{{ .Description }}</div>
{{ .Content }}
</div>
</div>
<div class="mt-8 text-center">
<a href="/careers" class="inline-flex items-center text-primary-600 hover:text-primary-700">
<span class="mr-2"></span>
View All Positions
</a>
</div>
</div>
</div>
</div>
{{ end }}
@@ -0,0 +1,70 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-12">
<div class="max-w-7xl mx-auto">
{{ if .IsHome }}
<h1 class="text-4xl font-bold mb-8">{{ .Site.Title }}</h1>
{{ else if eq .Section "blog" }}
<h1 class="text-4xl font-bold mb-8">{{ .Site.Params.blog.title }}</h1>
{{ with .Site.Params.blog.subtitle }}
<div class="text-xl text-gray-600 mb-8">{{ . }}</div>
{{ end }}
{{ else }}
<h1 class="text-4xl font-bold mb-8">{{ .Title }}</h1>
{{ with .Description }}
<div class="text-xl text-gray-600 mb-8">{{ . }}</div>
{{ end }}
{{ end }}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{{ $paginator := .Paginate .Pages }}
{{ range $paginator.Pages }}
{{ partial "post-card.html" . }}
{{ end }}
</div>
{{ $paginator := .Paginate .Pages }}
{{ if gt $paginator.TotalPages 1 }}
<nav class="mt-12 flex justify-between items-center">
{{ if $paginator.HasPrev }}
<a href="{{ $paginator.Prev.URL }}"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors duration-200">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
{{ i18n "previous" }}
</a>
{{ else }}
<div></div>
{{ end }}
<div class="flex space-x-2">
{{ range $paginator.Pagers }}
{{ if eq . $paginator }}
<span class="px-4 py-2 bg-primary-600 text-white rounded-lg">
{{ .PageNumber }}
</span>
{{ else }}
<a href="{{ .URL }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors duration-200">
{{ .PageNumber }}
</a>
{{ end }}
{{ end }}
</div>
{{ if $paginator.HasNext }}
<a href="{{ $paginator.Next.URL }}"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors duration-200">
{{ i18n "next" }}
<svg class="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
{{ else }}
<div></div>
{{ end }}
</nav>
{{ end }}
</div>
</div>
{{ end }}
@@ -0,0 +1,12 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-8 mb-12 flex-1">
<h1 class="text-4xl font-bold text-center mb-5">{{ .Title }}</h1>
{{ with .Params.description }}
<p class="text-center font-light text-gray-500 sm:text-xl">{{ . }}</p>
{{ end }}
<div class="!max-w-none">
{{ .Content }}
</div>
</div>
{{ end }}
@@ -0,0 +1,8 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-8 mb-12 flex-1">
<h1 class="text-4xl font-bold text-center mb-12">{{ .Title }}</h1>
<div class="prose prose-lg !max-w-none">
{{ .Content }}
</div>
</div>
{{ end }}
@@ -0,0 +1,86 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-12">
<!-- Two Column Layout for Content and Sidebar -->
<div class="flex flex-col lg:flex-row gap-8 mb-12">
<!-- Main Content -->
<article class="flex-1">
<header class="mb-8">
{{ with .Params.categories }}
<div class="mb-4">
{{ range . }}
<a href="{{ "/categories/" | relLangURL }}{{ . | urlize }}"
class="inline-block px-3 py-1 text-sm font-medium text-primary-600 bg-primary-50 rounded-full hover:bg-primary-100 mr-2">
{{ . }}
</a>
{{ end }}
</div>
{{ end }}
<h1 class="text-4xl font-bold mb-4">{{ .Title }}</h1>
{{ partial "post-meta.html" . }}
</header>
{{ with .Params.featured_image }}
<div class="mb-8">
<img src="{{ . }}"
alt="{{ $.Title }}"
class="w-full h-auto rounded-lg"
loading="lazy">
</div>
{{ end }}
<div class="prose prose-lg max-w-none">
{{ .Content }}
</div>
</article>
<!-- Sidebar -->
<aside class="lg:w-80 xl:w-96">
{{ partial "sidebar.html" . }}
</aside>
</div>
<!-- Full-width Navigation Section -->
<nav class="border-t border-gray-200 mt-12 pt-8">
<div class="flex justify-between items-center">
{{ with .PrevInSection }}
<a href="{{ .RelPermalink }}"
class="group flex items-center">
<svg class="w-5 h-5 mr-2 text-gray-600 group-hover:text-primary-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
<div>
<div class="text-sm text-gray-600">{{ i18n "previousPost" $ }}</div>
<div class="font-medium group-hover:text-primary-600">{{ .Title }}</div>
</div>
</a>
{{ else }}
<div></div>
{{ end }}
{{ with .NextInSection }}
<a href="{{ .RelPermalink }}"
class="group flex items-center text-right">
<div>
<div class="text-sm text-gray-600">{{ i18n "nextPost" $ }}</div>
<div class="font-medium group-hover:text-primary-600">{{ .Title }}</div>
</div>
<svg class="w-5 h-5 ml-2 text-gray-600 group-hover:text-primary-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
{{ else }}
<div></div>
{{ end }}
</div>
</nav>
<!-- Full-width CTA Section -->
{{ if and .Site.Params.blog.cta.enable .Site.Params.cta.enable }}
<div class="mt-16">
{{ partial "components/cta.html" . }}
</div>
{{ end }}
</div>
{{ end }}
@@ -0,0 +1,20 @@
{{ define "main" }}
<div class="pt-2">
<div class="container mx-auto px-4 py-6">
<div class="flex flex-col md:flex-row gap-6 items-start">
<!-- Sidebar -->
<div class="w-full md:w-1/4 lg:w-1/5 pt-1">
{{ partial "docs-sidebar.html" . }}
</div>
<!-- Main Content -->
<main class="w-full md:w-3/4 lg:w-4/5 pt-2">
<article class="prose max-w-none -mt-2 pt-6">
{{ .Content }}
</article>
</main>
</div>
</div>
</div>
{{ end }}
+3
View File
@@ -0,0 +1,3 @@
{{ define "main" }}
{{ .Content }}
{{ end }}
@@ -0,0 +1,28 @@
{{ if .Site.Params.cta.enable }}
<section class="cta-section">
<div class="container">
{{ $angle := .Site.Params.cta.gradient_angle | default 45 }}
{{ $from := .Site.Params.cta.gradient_from }}
{{ $to := .Site.Params.cta.gradient_to }}
<div class="relative rounded-lg overflow-hidden bg-primary-600 cta-gradient"
style="--gradient-angle: {{ $angle }}; --gradient-from: {{ $from }}; --gradient-to: {{ $to }}">
<div class="relative text-center max-w-3xl mx-auto px-6 py-10">
<h2 class="text-3xl md:text-4xl font-bold text-white mb-6">{{ .Site.Params.cta.title }}</h2>
<p class="text-xl text-primary-100 mb-8">{{ .Site.Params.cta.description }}</p>
<div class="flex flex-col sm:flex-row justify-center gap-4">
{{ with .Site.Params.cta.primary_button }}
<a href="{{ .url }}" class="btn bg-white text-primary-600 hover:bg-gray-100">
{{ .text }}
</a>
{{ end }}
{{ with .Site.Params.cta.secondary_button }}
<a href="{{ .url }}" class="btn border-2 border-white text-white hover:bg-primary-700">
{{ .text }}
</a>
{{ end }}
</div>
</div>
</div>
</div>
</section>
{{ end }}
@@ -0,0 +1,46 @@
{{ $lang := .Language.Lang }}
<div class="bg-primary-50 p-6 rounded-lg">
<h3 class="text-lg font-bold text-gray-900 mb-2">
{{ if .title }}
{{ .title }}
{{ else }}
{{ if eq $lang "zh-cn" }}订阅新闻通讯{{ else }}Subscribe to Newsletter{{ end }}
{{ end }}
</h3>
<p class="text-sm text-gray-600 mb-4">
{{ if .description }}
{{ .description }}
{{ else }}
{{ if eq $lang "zh-cn" }}将最新文章直接发送到您的收件箱{{ else }}Get the latest posts delivered right to your inbox{{ end }}
{{ end }}
</p>
<form action="{{ .action | default "#" }}" method="POST" class="space-y-3">
{{ with .hidden }}
{{ range $name, $value := . }}
<input type="hidden" name="{{ $name }}" value="{{ $value }}">
{{ end }}
{{ end }}
<div>
<input
type="email"
name="{{ .emailName | default "email" }}"
placeholder="{{ if .placeholder }}{{ .placeholder }}{{ else }}{{ if eq $lang "zh-cn" }}输入您的邮箱{{ else }}Enter your email{{ end }}{{ end }}"
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
>
</div>
<button
type="submit"
class="w-full px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors duration-200"
>
{{ if .buttonText }}{{ .buttonText }}{{ else }}{{ if eq $lang "zh-cn" }}订阅{{ else }}Subscribe{{ end }}{{ end }}
</button>
{{ with .disclaimer }}
<p class="text-xs text-gray-500 mt-2">{{ . }}</p>
{{ end }}
</form>
</div>
@@ -0,0 +1,18 @@
<!-- Custom Head Content -->
<!--
This partial is intentionally left empty in the theme.
Users can override this partial in their own site by creating:
layouts/partials/custom-head.html
Example usage - Add any custom code to the <head> section:
- Custom tracking scripts (Hotjar, Mixpanel, etc.)
- Meta tags for verification (Google Search Console, Pinterest, etc.)
- Custom CSS or JavaScript libraries
- Any other HTML that should be in the <head>
Example:
<script>
// Your custom tracking code
</script>
-->
@@ -0,0 +1,22 @@
<div class="docs-sidebar bg-white shadow-sm rounded-lg p-4 sticky top-20 mt-2">
<h3 class="text-lg font-semibold text-gray-900 mb-3 mt-1">{{ i18n "documentation" }}</h3>
<nav class="docs-nav">
<ul class="space-y-1">
{{ range (where .Site.Pages "Section" "docs").ByWeight }}
{{ if and .Title (not .IsHome) (ne .Kind "section") }}
<li>
<a href="{{ .RelPermalink }}"
class="block px-4 py-2 rounded-md transition-colors duration-200
{{ if eq .RelPermalink $.RelPermalink }}
bg-indigo-50 text-indigo-600 font-medium
{{ else }}
text-gray-700 hover:bg-gray-50 hover:text-gray-900
{{ end }}">
{{ .Title }}
</a>
</li>
{{ end }}
{{ end }}
</ul>
</nav>
</div>
@@ -0,0 +1,151 @@
<footer class="footer py-12 ">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
<div class="flex flex-col md:flex-row justify-between space-y-4 md:space-y-0">
<!-- Logo and Social Media -->
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<a href="{{ .Site.BaseURL }}" class="inline-block">
{{ with .Site.Params.logo }}
<img src="{{ . | relURL }}" alt="{{ $.Site.Title }}" class="h-6">
{{ end }}
</a>
<span class="text-xl font-bold text-gray-900">{{ .Site.Title }}</span>
</div>
<div class="flex space-x-3 p-2">
{{ with .Site.Params.social.linkedin }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">LinkedIn</span>
<img src="/images/social/linkedin.svg" alt="LinkedIn" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.bluesky }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Bluesky</span>
<img src="/images/social/bluesky.svg" alt="Bluesky" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.twitter }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Twitter (X)</span>
<img src="/images/social/twitter.svg" alt="Twitter" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.youtube }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">YouTube</span>
<img src="/images/social/youtube.svg" alt="YouTube" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.facebook }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Facebook</span>
<img src="/images/social/facebook.svg" alt="Facebook" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.instagram }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Instagram</span>
<img src="/images/social/instagram.svg" alt="Instagram" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.github }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">GitHub</span>
<img src="/images/social/github.svg" alt="GitHub" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.telegram }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Telegram</span>
<img src="/images/social/telegram.svg" alt="Telegram" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.discord }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Discord</span>
<img src="/images/social/discord.svg" alt="Discord" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.slack }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Slack</span>
<img src="/images/social/slack.svg" alt="Slack" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.medium }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Medium</span>
<img src="/images/social/medium.svg" alt="Medium" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.dribbble }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Dribbble</span>
<img src="/images/social/dribbble.svg" alt="Dribbble" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.behance }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">Behance</span>
<img src="/images/social/behance.svg" alt="Behance" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.mastodon }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">mastodon</span>
<img src="/images/social/mastodon.svg" alt="mastodon" class="h-5 w-5">
</a>
{{ end }}
{{ with .Site.Params.social.gitlab }}
<a href="{{ . }}" class="text-gray-600 hover:text-gray-900" target="_blank" rel="noopener noreferrer">
<span class="sr-only">gitlab</span>
<img src="/images/social/gitlab.svg" alt="gitlab" class="h-5 w-5">
</a>
{{ end }}
</div>
</div>
<!-- Column 1 -->
<div class="flex-1">
<h3 class="text-sm font-semibold uppercase tracking-wider text-gray-900 mb-4">{{ .Site.Params.footer.column_1_title }}</h3>
<ul class="space-y-2">
{{ range .Site.Menus.footer_column_1 }}
<li><a href="{{ .URL }}" class="text-gray-600 hover:text-primary-600">{{ .Name }}</a></li>
{{ end }}
</ul>
</div>
<!-- Column 2 -->
<div class="flex-1">
<h3 class="text-sm font-semibold uppercase tracking-wider text-gray-900 mb-4">{{ .Site.Params.footer.column_2_title }}</h3>
<ul class="space-y-2">
{{ range .Site.Menus.footer_column_2 }}
<li><a href="{{ .URL }}" class="text-gray-600 hover:text-primary-600">{{ .Name }}</a></li>
{{ end }}
</ul>
</div>
<!-- Column 3 -->
<div class="flex-1">
<h3 class="text-sm font-semibold uppercase tracking-wider text-gray-900 mb-4">{{ .Site.Params.footer.column_3_title }}</h3>
<ul class="space-y-2">
{{ range .Site.Menus.footer_column_3 }}
<li><a href="{{ .URL }}" class="text-gray-600 hover:text-primary-600">{{ .Name }}</a></li>
{{ end }}
</ul>
</div>
</div>
<!-- Bottom -->
<div class="mt-12 pt-8 border-t border-gray-600">
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<p class="text-gray-600 text-sm">
© {{ now.Format "2006" }} {{ .Site.Title }}. All rights reserved.
</p>
<a href="https://www.champs-libres.coop/" class="text-sm text-gray-600 hover:text-primary-600" target="_blank" rel="noopener noreferrer">
Designed by Champs-Libres
</a>
</div>
</div>
</div>
</footer>
@@ -0,0 +1,10 @@
{{ if and hugo.IsProduction .Site.Params.googleAnalytics }}
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ .Site.Params.googleAnalytics }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ .Site.Params.googleAnalytics }}');
</script>
{{ end }}
@@ -0,0 +1,9 @@
{{ if and hugo.IsProduction .Site.Params.googleTagManager }}
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','{{ .Site.Params.googleTagManager }}');</script>
<!-- End Google Tag Manager -->
{{ end }}
@@ -0,0 +1,160 @@
{{ $headerConfig := .Site.Params.header }}
{{ $dropdownConfig := $headerConfig.menu.dropdown }}
<div class="mobile-menu-wrapper">
<input type="checkbox" id="nav-toggle" class="nav-toggle">
<header class="fixed w-full top-0 z-50 {{ with $headerConfig.background }}{{ . }}{{ else }}bg-white/80 backdrop-blur-sm{{ end }} {{ with $headerConfig.border }}{{ . }}{{ else }}border-b border-gray-100{{ end }}">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
<nav class="flex items-center justify-between h-20">
<!-- Logo and Title -->
<a href="{{ "/" | relLangURL }}" class="flex items-center space-x-4">
{{ with $headerConfig.logo }}
<img src="{{ .src | relURL }}" alt="{{ $.Site.Title }}" class="{{ with .class }}{{ . }}{{ else }}h-12{{ end }}">
{{ else }}
<span class="text-3xl font-bold text-gray-900">{{ .Site.Title }}</span>
{{ end }}
{{ if .Site.Title }}
<span class="text-3xl font-semibold text-gray-800">{{ .Site.Title }}</span>
{{ end }}
</a>
<!-- Navigation -->
<div class="hidden md:flex items-center {{ with $headerConfig.menu.spacing }}{{ . }}{{ else }}space-x-8{{ end }}">
{{ range .Site.Menus.main }}
{{ if .Params.has_submenu }}
<div class="relative group">
<button class="flex items-center {{ with $headerConfig.menu.linkClass }}{{ . }}{{ else }}text-base text-gray-900 hover:text-primary-600 font-bold transition duration-200{{ end }}">
{{ .Name }}
<svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="absolute left-0 mt-2 {{ $dropdownConfig.width | default "w-72" }} opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 ease-in-out">
<div class="{{ $dropdownConfig.container_padding | default "py-6" }} {{ $dropdownConfig.background | default "bg-white" }} {{ $dropdownConfig.border | default "border border-gray-100" }} {{ $dropdownConfig.shadow | default "shadow-xl" }} {{ $dropdownConfig.radius | default "rounded-lg" }}">
{{ range .Params.submenu }}
<a href="{{ .url }}" class="block {{ $dropdownConfig.item_padding | default "px-8 py-3" }} {{ $dropdownConfig.text_size | default "text-sm" }} {{ $dropdownConfig.text_color | default "text-gray-700" }} {{ $dropdownConfig.hover_background | default "hover:bg-gray-50" }}">{{ .name }}</a>
{{ end }}
</div>
</div>
</div>
{{ else }}
<a href="{{ .URL }}" class="{{ with $headerConfig.menu.linkClass }}{{ . }}{{ else }}text-base text-gray-900 hover:text-primary-600 font-bold transition duration-200{{ end }}">{{ .Name }}</a>
{{ end }}
{{ end }}
<!-- Language Switcher -->
{{ partial "language-switcher" . }}
</div>
<!-- CTA Buttons -->
{{ if not $headerConfig.hideButtons }}
<div class="hidden md:flex items-center space-x-4">
{{ with $headerConfig.buttons.signIn }}
<a href="{{ .url | default "#" }}" class="{{ with .class }}{{ . }}{{ else }}inline-flex items-center justify-center px-6 py-3 rounded-lg font-bold transition duration-200 ease-in-out border-2 border-gray-200 hover:border-primary-600 hover:text-primary-600{{ end }}">
{{ .text | default "Sign in" }}
</a>
{{ end }}
{{ with $headerConfig.buttons.getStarted }}
<a href="{{ .url | default "#" }}" class="{{ with .class }}{{ . }}{{ else }}inline-flex items-center justify-center px-6 py-3 rounded-lg font-bold transition duration-200 ease-in-out bg-primary-600 text-white hover:bg-primary-700 hover:scale-105{{ end }}">
{{ .text | default "Get Started" }}
</a>
{{ end }}
</div>
{{ end }}
<!-- Mobile Menu Toggle -->
<div class="md:hidden">
<label for="nav-toggle" class="p-2 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 transition-colors cursor-pointer">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</label>
</div>
</nav>
</div>
<!-- Mobile Menu -->
<div class="nav-content md:hidden w-full fixed left-0 right-0 top-20 bg-white border-t border-gray-100 shadow-lg max-h-[calc(100vh-5rem)] overflow-y-auto">
<div class="w-full px-6 py-4">
{{ range .Site.Menus.main }}
{{ if .Params.has_submenu }}
<div class="py-2">
<div class="text-xl text-gray-900 font-bold mb-2">{{ .Name }}</div>
<div class="pl-4">
{{ range .Params.submenu }}
<a href="{{ .url }}" class="block text-gray-700 hover:text-primary-600 py-2">{{ .name }}</a>
{{ end }}
</div>
</div>
{{ else }}
<a href="{{ .URL }}" class="{{ with $headerConfig.menu.mobileLinkClass }}{{ . }}{{ else }}block text-xl text-gray-900 hover:text-primary-600 font-bold transition duration-200 py-2{{ end }}">{{ .Name }}</a>
{{ end }}
{{ end }}
<!-- Mobile Language Switcher -->
{{ if .Site.IsMultiLingual }}
<div class="py-4 border-t border-gray-200 mt-4">
<div class="text-lg text-gray-600 font-semibold mb-2">{{ i18n "language" }}</div>
{{ range .Site.Languages }}
{{ if eq $.Site.Language.Lang .Lang }}
<span class="block py-2 text-primary-600 font-bold">{{ .LanguageName }}</span>
{{ else }}
{{ $langURL := printf "/%s/" .Lang }}
<a href="{{ $langURL }}" class="language-switch-link block py-2 text-gray-700 hover:text-primary-600" data-lang="{{ .Lang }}">
{{ .LanguageName }}
</a>
{{ end }}
{{ end }}
</div>
{{ end }}
{{ if not $headerConfig.hideButtons }}
<div class="pt-4 space-y-4">
{{ with $headerConfig.buttons.signIn }}
<a href="{{ .url | default "#" }}" class="{{ with .mobileClass }}{{ . }}{{ else }}block text-center px-6 py-3 rounded-lg font-bold transition duration-200 ease-in-out border-2 border-gray-200 hover:border-primary-600 hover:text-primary-600{{ end }}">
{{ .text | default "Sign in" }}
</a>
{{ end }}
{{ with $headerConfig.buttons.getStarted }}
<a href="{{ .url | default "#" }}" class="{{ with .mobileClass }}{{ . }}{{ else }}block text-center px-6 py-3 rounded-lg font-bold transition duration-200 ease-in-out bg-primary-600 text-white hover:bg-primary-700 hover:scale-105{{ end }}">
{{ .text | default "Get Started" }}
</a>
{{ end }}
</div>
{{ end }}
</div>
</div>
</header>
<style>
.mobile-menu-wrapper {
position: relative;
}
.nav-toggle {
display: none;
}
.nav-content {
display: none;
-webkit-overflow-scrolling: touch;
}
.nav-toggle:checked ~ header .nav-content {
display: block;
}
</style>
<script>
(function() {
var navToggle = document.getElementById('nav-toggle');
if (navToggle) {
navToggle.addEventListener('change', function() {
if (this.checked) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
});
}
})();
</script>
</div>
@@ -0,0 +1,22 @@
{{- $iconClass := printf "w-%s h-%s" (.size | default "6") (.size | default "6") -}}
{{- $color := .color | default "#000000" -}}
<svg class="{{ $iconClass }}" style="color: {{ $color }};" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{{- if eq .name "chart" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
{{- else if eq .name "attribution" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
{{- else if eq .name "roi" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
{{- else if eq .name "check" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
{{- else if eq .name "analytics" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 8v8m-4-5v5m-4-2v2m-2 4h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
{{- else if eq .name "funnel" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h18M5 8h14M7 12h10M9 16h6M11 20h2"></path>
{{- else if eq .name "target" -}}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
{{- else if eq .name "custom" -}}
{{ .path | safeHTML }}
{{- end -}}
</svg>
@@ -0,0 +1,24 @@
{{ if .Site.IsMultiLingual }}
{{ $headerConfig := .Site.Params.header }}
{{ $dropdownConfig := $headerConfig.menu.dropdown }}
<div class="relative group">
<button class="flex items-center {{ with $headerConfig.menu.linkClass }}{{ . }}{{ else }}text-base text-gray-900 hover:text-primary-600 font-bold transition duration-200{{ end }}">
{{ .Site.Language.LanguageName }}
<svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="absolute right-0 mt-2 {{ $dropdownConfig.width | default "w-72" }} opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 ease-in-out">
<div class="{{ $dropdownConfig.container_padding | default "py-6" }} {{ $dropdownConfig.background | default "bg-white" }} {{ $dropdownConfig.border | default "border border-gray-100" }} {{ $dropdownConfig.shadow | default "shadow-xl" }} {{ $dropdownConfig.radius | default "rounded-lg" }}">
{{ range .Site.Languages }}
{{ if ne $.Site.Language.Lang .Lang }}
{{ $langURL := printf "/%s/" .Lang }}
<a href="{{ $langURL }}" class="language-switch-link block {{ $dropdownConfig.item_padding | default "px-8 py-3" }} {{ $dropdownConfig.text_size | default "text-sm" }} {{ $dropdownConfig.text_color | default "text-gray-700" }} {{ $dropdownConfig.hover_background | default "hover:bg-gray-50" }}" data-lang="{{ .Lang }}">
{{ .LanguageName }}
</a>
{{ end }}
{{ end }}
</div>
</div>
</div>
{{ end }}
@@ -0,0 +1,80 @@
<article class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
{{ with .Params.featured_image }}
<a href="{{ $.RelPermalink }}" class="block aspect-w-16 aspect-h-9 overflow-hidden">
<img
src="{{ . }}"
alt="{{ $.Title }}"
class="object-cover w-full h-full transform hover:scale-105 transition-transform duration-300"
loading="lazy"
>
</a>
{{ end }}
<div class="p-6">
<!-- Category -->
{{ with .Params.categories }}
<div class="mb-4">
{{ range first 1 . }}
<a href="{{ "/categories/" | relLangURL }}{{ . | urlize }}"
class="inline-block px-3 py-1 text-sm font-medium text-primary-600 bg-primary-50 rounded-full hover:bg-primary-100">
{{ . }}
</a>
{{ end }}
</div>
{{ end }}
<!-- Title -->
<h2 class="text-2xl font-bold mb-3 hover:text-primary-600 transition-colors duration-200">
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
</h2>
<!-- Description -->
<p class="text-gray-600 mb-4 line-clamp-2">
{{ with .Description }}
{{ . }}
{{ else }}
{{ .Summary | truncate 160 }}
{{ end }}
</p>
<!-- Author and Date in a cleaner layout -->
<div class="flex items-center justify-between text-sm text-gray-500 mb-4">
{{ with .Params.author }}
<div class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
<span>{{ . }}</span>
</div>
{{ end }}
<div class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "Jan 2, 2006" }}
</time>
</div>
</div>
<!-- Read More link and Reading Time -->
<div class="flex justify-between items-center">
<a href="{{ .RelPermalink }}"
class="inline-flex items-center text-primary-600 hover:text-primary-700 font-medium">
{{ i18n "readMore" }}
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
<!-- Reading Time -->
<span class="text-sm text-gray-500 flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>
<span>{{ .ReadingTime }} {{ i18n "readTime" }}</span>
</span>
</div>
</div>
</article>
@@ -0,0 +1,45 @@
<div class="flex flex-col space-y-4">
<!-- Author and Date -->
<div class="flex items-center justify-between text-sm text-gray-500">
{{ with .Params.author }}
<div class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
<span>{{ . }}</span>
</div>
{{ end }}
<div class="flex items-center space-x-6">
<div class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>
<span>{{ .ReadingTime }} {{ i18n "minRead" . }}</span>
</div>
<div class="flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format (i18n "dateFormat" .) }}
</time>
</div>
</div>
</div>
<!-- Tags -->
{{ with .Params.tags }}
<div class="flex items-center flex-wrap gap-2">
{{ range . }}
<a href="{{ "/tags/" | relLangURL }}{{ . | urlize }}"
class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-full text-sm text-gray-700 transition-colors duration-200">
#{{ . }}
</a>
{{ end }}
</div>
{{ end }}
</div>
@@ -0,0 +1,52 @@
{{/* Feature Partial (utilisé par le carousel) */}}
{{ $title := .title }}
{{ $titleBtn := .titleBtn}}
{{ $description := .description }}
{{ $badge := .badge }}
{{ $badgeColor := .badgeColor | default "chill-blue" }}
{{ $image := .image }}
{{ $buttonText := .buttonText | default "Learn More" }}
{{ $buttonLink := .buttonLink | default "#" }}
{{ $imagePosition := .imagePosition | default "right" }}
{{ $features := split (.features) "," }}
<div class="grid lg:grid-cols-2 gap-12 items-center badge-{{ $badgeColor }}">
{{ if eq $imagePosition "left" }}
<div class="order-2 lg:order-1">
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
<div class="space-y-6 {{ if eq $imagePosition "left" }}order-1 lg:order-2{{ end }}">
<style>
.badge {
background-color: color-mix(in srgb, var(--badge-color) 30%, transparent);
color: #222;
}
.badge-icon {
color: var(--badge-color);
}
</style>
<div class="badge inline-block px-4 py-2 rounded-full font-medium">{{ $badge }}</div>
<h3 class="text-2xl md:text-3xl font-bold">{{ $title }}</h3>
<p class="text-lg text-gray-600">{{ $description }}</p>
<ul class="space-y-4">
{{ range $features }}
<li class="flex items-center space-x-3">
<svg class="badge-icon w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>{{ . | default "" }}</span>
</li>
{{ end }}
</ul>
<a href="{{ $buttonLink }}" class="btn-primary inline-block">{{ $buttonText }}</a>
</div>
{{ if ne $imagePosition "left" }}
<div>
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
</div>
@@ -0,0 +1,88 @@
<div class="space-y-8">
<!-- Subscribe Form -->
{{ if .Site.Params.blog.sidebar.subscribe.enable }}
{{ $params := dict
"Site" .Site
"Language" .Site.Language
"title" .Site.Params.blog.sidebar.subscribe.title
"description" .Site.Params.blog.sidebar.subscribe.description
"action" .Site.Params.blog.sidebar.subscribe.action
"emailName" .Site.Params.blog.sidebar.subscribe.emailName
"buttonText" .Site.Params.blog.sidebar.subscribe.buttonText
"placeholder" .Site.Params.blog.sidebar.subscribe.placeholder
"disclaimer" .Site.Params.blog.sidebar.subscribe.disclaimer
"hidden" .Site.Params.blog.sidebar.subscribe.hidden
}}
{{ partial "components/subscribe-form" $params }}
{{ end }}
<!-- Recent Articles -->
{{ if .Site.Params.blog.sidebar.recent.enable }}
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-bold mb-4">{{ .Site.Params.blog.sidebar.recent.title | default (i18n "recentArticles" .) }}</h3>
<div class="space-y-4">
{{ $recentCount := .Site.Params.blog.sidebar.recent.count | default 5 }}
{{ range first $recentCount (where .Site.RegularPages "Type" "blog") }}
<div class="group">
<a href="{{ .RelPermalink }}" class="block">
{{ with .Params.featured_image }}
<div class="aspect-w-16 aspect-h-9 mb-3 overflow-hidden rounded-lg">
<img
src="{{ . }}"
alt="{{ $.Title }}"
class="object-cover w-full h-full transform group-hover:scale-105 transition-transform duration-300"
loading="lazy"
>
</div>
{{ end }}
<h4 class="font-medium text-gray-900 group-hover:text-primary-600 transition-colors duration-200 line-clamp-2">
{{ .Title }}
</h4>
<div class="flex items-center text-sm text-gray-500 mt-2">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "Jan 2, 2006" }}
</time>
</div>
</a>
</div>
{{ end }}
</div>
</div>
{{ end }}
<!-- Categories -->
{{ if .Site.Params.blog.sidebar.categories.enable }}
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-bold mb-4">{{ .Site.Params.blog.sidebar.categories.title | default (i18n "categories" .) }}</h3>
<div class="flex flex-wrap gap-2">
{{ range $name, $taxonomy := .Site.Taxonomies.categories }}
<a href="{{ "/categories/" | relLangURL }}{{ $name | urlize }}"
class="inline-block px-3 py-1 text-sm font-medium text-primary-600 bg-primary-50 rounded-full hover:bg-primary-100">
{{ $name }}
<span class="text-gray-500 ml-1">({{ $taxonomy.Count }})</span>
</a>
{{ end }}
</div>
</div>
{{ end }}
<!-- Tags Cloud -->
{{ if .Site.Params.blog.sidebar.tags.enable }}
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-bold mb-4">{{ .Site.Params.blog.sidebar.tags.title | default (i18n "popularTags" .) }}</h3>
<div class="flex flex-wrap gap-2">
{{ $tagCount := .Site.Params.blog.sidebar.tags.count | default 20 }}
{{ range first $tagCount .Site.Taxonomies.tags.ByCount }}
<a href="{{ "/tags/" | relLangURL }}{{ .Name | urlize }}"
class="text-sm text-gray-600 hover:text-primary-600">
#{{ .Name }}
<span class="text-gray-500">({{ .Count }})</span>
</a>
{{ end }}
</div>
</div>
{{ end }}
</div>
@@ -0,0 +1,25 @@
{{ $title := .Get "title" }}
{{ $subtitle := .Get "subtitle" }}
<div class="text-center mb-16">
<h2 class="text-3xl font-bold mb-4">{{ $title }}</h2>
<p class="text-xl text-gray-600">{{ $subtitle }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
{{ range $index := (seq 1 10) }}
{{ $benefit := printf "benefit%d" $index }}
{{ with $.Get $benefit }}
{{ $parts := split . "|" }}
{{ if ge (len $parts) 4 }}
<div class="p-6 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow">
<div class="w-12 h-12 rounded-lg flex items-center justify-center mb-4" style="background-color: {{ index $parts 1 }}15;">
{{ partial "icons" (dict "name" (index $parts 0) "color" (index $parts 1) "size" "6") }}
</div>
<h3 class="text-xl font-bold mb-2">{{ index $parts 2 }}</h3>
<p class="text-gray-600">{{ index $parts 3 }}</p>
</div>
{{ end }}
{{ end }}
{{ end }}
</div>
@@ -0,0 +1,15 @@
<div class="not-prose">
<div class="bg-white rounded-2xl shadow-xl overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-2">
<div class="p-8 md:p-12">
<div class="inline-block px-4 py-2 rounded-full text-sm font-semibold mb-6" style="background-color: {{ .Get 0 }}15; color: {{ .Get 0 }};">{{ .Get 1 }}</div>
<div class="text-3xl font-bold mb-4 !mt-0">{{ .Get 2 }}</div>
<div class="text-gray-600 mb-6">{{ .Get 3 }}</div>
<a href="{{ .Get 5 }}" class="inline-block px-6 py-3 text-white font-bold rounded-lg transition-colors hover:opacity-90 !no-underline !text-white" style="background-color: {{ .Get 0 }};">{{ .Get 4 }}</a>
</div>
<div class="p-8 md:p-12 flex items-center justify-center" style="background: linear-gradient(135deg, {{ .Get 0 }}03, {{ .Get 0 }}30);">
<img src="{{ .Get 6 }}" alt="{{ .Get 7 }}" class="max-w-[200px] !my-0 !rounded-none !shadow-none">
</div>
</div>
</div>
</div>
@@ -0,0 +1,82 @@
<section class="border-y border-gray-100 overflow-hidden">
<div class="container mx-auto">
<div class="py-12">
<p class="text-center text-3xl md:text-2xl font-bold mb-6">{{ .Get "title" | default (i18n "trustedByCompanies") }}</p>
<div class="logo-scroll">
<div class="logos-slide{{ if ne (.Get "animate" | default "true") "false" }} animate{{ end }}">
{{ range .Page.Params.client_logos }}
<img src="{{ .logo | relURL }}" class="h-20" alt="{{ .name }}" />
{{ end }}
{{ if ne (.Get "animate" | default "true") "false" }}
{{ range .Page.Params.client_logos }}
<img src="{{ .logo | relURL }}" class="h-20" alt="{{ .name }}" />
{{ end }}
{{ end }}
</div>
</div>
</div>
</div>
</section>
<style>
.logo-scroll {
overflow: hidden;
padding: 20px 0;
white-space: nowrap;
position: relative;
}
.logo-scroll.static {
display: flex;
justify-content: center;
overflow: visible;
}
.logo-scroll.static .logos-slide {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 40px;
}
.logos-slide {
display: inline-block;
}
.logos-slide.animate {
animation: 30s slide infinite linear;
}
.logos-slide img {
max-height: 1.5rem;
margin: 0 40px;
display: inline-block;
vertical-align: middle;
filter: grayscale(100%);
opacity: 0.6;
}
.logo-scroll.static .logos-slide img {
margin: 0;
}
@keyframes slide {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-100% / 2));
}
}
.logo-scroll:hover .logos-slide.animate {
animation-play-state: paused;
}
/* Mobile-specific animation speed */
@media (max-width: 768px) {
.logos-slide.animate {
animation: 15s slide infinite linear;
}
}
</style>
@@ -0,0 +1,19 @@
{{ $lang := .Get 0 }}
{{ $code := .Inner }}
{{ $filename := .Get 1 }}
<div class="not-prose my-8 overflow-hidden rounded-lg bg-gray-900 shadow-lg">
{{ with $filename }}
<div class="flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700">
<div class="text-sm text-gray-200 font-mono">{{ . }}</div>
<div class="flex space-x-2">
<div class="w-3 h-3 rounded-full bg-red-500"></div>
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
<div class="w-3 h-3 rounded-full bg-green-500"></div>
</div>
</div>
{{ end }}
<div class="p-4 overflow-x-auto">
{{ highlight $code $lang "linenos=table,linenostart=1,hl_lines=,lineanchors=line" }}
</div>
</div>
@@ -0,0 +1,17 @@
{{ $params := dict
"title" (.Get "title")
"description" (.Get "description")
"primary_button" (dict
"text" (.Get "primary_button_text")
"url" (.Get "primary_button_url")
)
"secondary_button" (dict
"text" (.Get "secondary_button_text")
"url" (.Get "secondary_button_url")
)
"gradient_from" (.Get "gradient-from")
"gradient_to" (.Get "gradient-to")
"gradient_angle" (.Get "gradient-angle")
}}
{{ partial "components/cta" (dict "Site" .Site "Params" (dict "cta" $params "enable" true)) }}
@@ -0,0 +1,31 @@
{{ $data := .Inner | transform.Unmarshal }}
<section class="bg-white">
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900">{{ $data.title | default "Frequently Asked Questions" }}</h2>
{{ with $data.description }}
<p class="mb-5 font-light text-gray-500 sm:text-xl">{{ . }}</p>
{{ end }}
</div>
<div class="space-y-6">
{{ range $data.questions }}
<div class="border rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-md transition-shadow duration-200">
<button class="w-full flex justify-between items-center p-6 text-left hover:bg-gray-50 transition-colors duration-200 focus:outline-none"
onclick="this.parentElement.querySelector('.faq-content').classList.toggle('hidden');
this.querySelector('svg').classList.toggle('rotate-180')">
<span class="text-lg font-medium text-gray-900">{{ .question }}</span>
<svg class="w-5 h-5 text-gray-500 transform transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="faq-content hidden border-t">
<div class="p-6 prose prose-sm sm:prose lg:prose-lg max-w-none">
{{ .answer | markdownify }}
</div>
</div>
</div>
{{ end }}
</div>
</div>
</section>
@@ -0,0 +1,51 @@
{{/* Feature Shortcode */}}
{{ $title := .Get "title" }}
{{ $description := .Get "description" }}
{{ $badge := .Get "badge" }}
{{ $badgeColor := .Get "badgeColor" | default "#5573df" }}
{{ $image := .Get "image" }}
{{ $buttonText := .Get "buttonText" | default "Learn More" }}
{{ $buttonLink := .Get "buttonLink" | default "#" }}
{{ $imagePosition := .Get "imagePosition" | default "right" }}
{{ $features := split (.Get "features") "," }}
<div class="grid lg:grid-cols-2 gap-12 items-center" style="--badge-color: {{ $badgeColor }}">
{{ if eq $imagePosition "left" }}
<div class="order-2 lg:order-1">
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
<div class="space-y-6 {{ if eq $imagePosition "left" }}order-1 lg:order-2{{ end }}">
<style>
.badge {
background-color: color-mix(in srgb, var(--badge-color) 10%, transparent);
color: var(--badge-color);
}
.badge-icon {
color: var(--badge-color);
}
</style>
<div class="badge inline-block px-4 py-2 rounded-full font-medium">{{ $badge }}</div>
<h3 class="text-2xl md:text-3xl font-bold">{{ $title }}</h3>
<p class="text-lg text-gray-600">{{ $description }}</p>
<ul class="space-y-4">
{{ range $features }}
<li class="flex items-center space-x-3">
<svg class="badge-icon w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span>{{ . | default "" }}</span>
</li>
{{ end }}
</ul>
<a href="{{ $buttonLink }}" class="btn-primary inline-block">{{ $buttonText }}</a>
</div>
{{ if ne $imagePosition "left" }}
<div>
<img src="{{ $image | relURL }}" alt="{{ $title }}" class="rounded-xl shadow-elevation">
</div>
{{ end }}
</div>
@@ -0,0 +1,81 @@
{{/*
Usage :
{{< features-carousel title="Vos fonctionnalités clés" >}}
{
"features": [
{
"title": "Performance",
"titleBtn": "Performance",
"description": "Leverage Hugo's blazing-fast build times and optimized output.",
"badge": "Performance",
"badgeColor": "#2563eb",
"image": "/images/feature-1.svg",
"buttonText": "Learn More",
"buttonLink": "/features/performance/",
"features": "Sub-second page loads,Optimized assets,Minimal JavaScript,CDN-ready output",
"imagePosition": "right"
},
]
}
{{< /features-carousel >}}
*/}}
{{ $title := .Get "title" | default "" }}
{{ $data := .Inner | transform.Unmarshal }}
{{ $background_color := .Get "background-color" }}
<section id="features-carousel" class="section{{ if not $background_color }} bg-gray-50{{ end }}" {{ if $background_color }}style="background-color:{{ $background_color }};"{{ end }}>
<div class="container">
{{ if $title }}
<h1 class="text-3xl font-bold mb-4 text-center">{{ $title }}</h1>
{{ end }}
<div class="features-carousel__nav"></div>
<div class="features-carousel__slides">
{{ range $i, $f := $data.features }}
<div class="feature" data-title-btn="{{ $f.titleBtn }}">
{{ partial "shortcodes/feature.html" (dict
"title" $f.title
"titleBtn" $f.titleBtn
"description" $f.description
"badge" $f.badge
"badgeColor" $f.badgeColor
"image" $f.image
"buttonText" $f.buttonText
"buttonLink" $f.buttonLink
"features" $f.features
"imagePosition" $f.imagePosition
) }}
</div>
{{ end }}
</div>
</div>
</section>
<script>
(function() {
const carousel = document.getElementById('features-carousel');
if (!carousel) return;
const slides = carousel.querySelectorAll('.feature');
const nav = carousel.querySelector('.features-carousel__nav');
let current = 0;
slides.forEach((slide, i) => {
const btn = document.createElement('button');
btn.innerText = slide.dataset.titleBtn || `Feature ${i+1}`;
btn.onclick = () => {
current = i;
showSlide(current);
};
nav.appendChild(btn);
});
function showSlide(idx) {
slides.forEach((slide, i) => {
slide.style.display = i === idx ? 'block' : 'none';
});
Array.from(nav.children).forEach((btn, i) => {
btn.classList.toggle('active', i === idx);
});
}
showSlide(current);
})();
</script>
@@ -0,0 +1,26 @@
{{ $title := .Get "title" }}
{{ $color := .Get "color" | default "#2563eb" }}
<div class="max-w-3xl mx-auto">
<h2 class="text-3xl font-bold text-center !mb-24">{{ $title }}</h2>
<div class="space-y-12">
{{ range $index := (seq 1 10) }}
{{ $feature := printf "feature%d" $index }}
{{ with $.Get $feature }}
{{ $parts := split . "|" }}
{{ if ge (len $parts) 2 }}
<div class="flex gap-6">
<div class="w-10 h-10 rounded-xl flex items-center justify-center flex-shrink-0" style="background-color: {{ $color }}15;">
{{ partial "icons" (dict "name" "check" "color" $color "size" "6") }}
</div>
<div>
<h3 class="text-2xl font-bold mb-3 !mt-0">{{ index $parts 0 }}</h3>
<p class="text-gray-600 text-lg leading-relaxed">{{ index $parts 1 }}</p>
</div>
</div>
{{ end }}
{{ end }}
{{ end }}
</div>
</div>
@@ -0,0 +1,17 @@
{{/* Features Section Wrapper Shortcode */}}
{{ $title := .Get "title" | default "Powerful Features for Modern Teams" }}
{{ $description := .Get "description" | default "Discover how our platform helps you understand and optimize every aspect of your user experience." }}
<section class="section">
<div class="container">
<div class="text-center max-w-3xl mx-auto mb-16">
<h2 class="text-3xl md:text-4xl font-bold mb-6">{{ $title }}</h2>
<p class="text-xl text-gray-600">{{ $description }}</p>
</div>
<!-- Feature Grid -->
<div class="space-y-32">
{{ .Inner }}
</div>
</div>
</section>
@@ -0,0 +1,18 @@
{{ $src := .Get "src" }}
{{ $alt := .Get "alt" }}
{{ $caption := .Get "caption" }}
{{ $class := .Get "class" | default "w-full" }}
<figure class="my-8">
<img
src="{{ $src }}"
alt="{{ $alt }}"
class="{{ $class }} rounded-lg shadow-lg"
loading="lazy"
>
{{ with $caption }}
<figcaption class="mt-2 text-center text-sm text-gray-600">
{{ . | markdownify }}
</figcaption>
{{ end }}
</figure>
@@ -0,0 +1,3 @@
<div class="not-prose">
<img src="{{ .Get 0 }}" alt="{{ .Get 1 }}" class="w-full no-prose-img">
</div>
@@ -0,0 +1,51 @@
{{ $headline := .Get "headline" }}
{{ $sub_headline := .Get "sub_headline" }}
{{ $primary_button_text := .Get "primary_button_text" }}
{{ $primary_button_url := .Get "primary_button_url" }}
{{ $secondary_button_text := .Get "secondary_button_text" }}
{{ $secondary_button_url := .Get "secondary_button_url" }}
{{ $hero_image := .Get "hero_image" }}
{{ $background_image := .Get "background_image" }}
{{ $custom_class := .Get "custom_class" }}
<section class="relative overflow-hidden {{ $custom_class }}" >
{{ if $background_image }}
<div class="absolute inset-0">
<img src="{{ $background_image | relURL }}" alt="Background" class="w-full h-full object-cover">
</div>
{{ end }}
<div class="container pt-16 pb-20 md:pt-24 md:pb-28">
<div class="grid lg:grid-cols-2 gap-12 items-center">
<div class="space-y-8">
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
{{ $headline | safeHTML }}
</h1>
<p class="text-xl text-gray-200">
{{ $sub_headline | safeHTML }}
</p>
<div class="flex flex-col sm:flex-row gap-4">
{{ if and $primary_button_text $primary_button_url }}
<a href="{{ $primary_button_url }}" class="btn-primary text-center">
{{ $primary_button_text }}
</a>
{{ end }}
{{ if and $secondary_button_text $secondary_button_url }}
<a href="{{ $secondary_button_url }}" class="btn-outline text-center">
{{ $secondary_button_text }}
</a>
{{ end }}
</div>
</div>
<div class="relative">
{{ if $hero_image }}
<div class="relative z-10">
<img src="{{ $hero_image | relURL }}" alt="Hero Image" class="rounded-xl shadow-elevation">
</div>
{{ end }}
<!-- Background decoration -->
<div class="absolute -top-20 -right-20 w-64 h-64 bg-primary-100 rounded-full filter blur-3xl opacity-50"></div>
<div class="absolute -bottom-20 -left-20 w-64 h-64 bg-secondary-100 rounded-full filter blur-3xl opacity-50"></div>
</div>
</div>
</div>
</section>
@@ -0,0 +1,7 @@
<div class="investor-logo p-6 flex items-center justify-center">
{{ if .Get "image" }}
<img src="{{ .Get "image" }}" alt="{{ .Get "name" }}" class="max-h-12 grayscale hover:grayscale-0 transition-all duration-300">
{{ else }}
<div class="text-xl font-bold text-gray-400">{{ .Get "name" }}</div>
{{ end }}
</div>
@@ -0,0 +1,63 @@
{{ $json := .Inner | transform.Unmarshal }}
<section class="py-16 bg-white">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{{ with $json.title }}
<div class="text-center">
<h2 class="text-3xl font-bold text-gray-900">{{ . }}</h2>
{{ with $json.description }}
<p class="mt-4 text-xl text-gray-600">{{ . }}</p>
{{ end }}
</div>
{{ end }}
<div class="mt-16">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
{{ range $json.plans }}
<div class="w-full">
<div class="h-full flex flex-col rounded-2xl border border-gray-200 bg-white shadow-sm hover:shadow-lg transition-shadow duration-300 relative">
{{ if .featured }}
<div class="absolute top-4 right-4 inline-block rounded-full bg-blue-100 px-4 py-1 text-sm font-semibold text-blue-800">
{{ i18n "popular" }}
</div>
{{ end }}
<div class="p-8 flex flex-col flex-grow">
<div class="mb-6">
<h3 class="text-2xl font-bold text-gray-900">{{ .name }}</h3>
<p class="mt-2 text-gray-500">{{ .description }}</p>
</div>
<div class="mb-8">
<p class="flex items-baseline">
<span class="text-4xl font-bold tracking-tight text-gray-900">{{ .price }}</span>
<span class="ml-2 text-gray-500">{{ i18n "perMonth" }}</span>
</p>
<p class="flex items-baseline">
<span class="font-bold tracking-tight text-gray-900">{{ .additional_price }}</span>
<span class="ml-2 text-gray-500">{{ i18n "perMonth" }}</span>
</p>
</div>
<ul class="space-y-4 text-gray-600 flex-grow">
{{ range .features }}
<li class="flex items-start">
<svg class="h-6 w-6 flex-shrink-0 text-blue-500" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/>
</svg>
<span class="ml-3">{{ . }}</span>
</li>
{{ end }}
</ul>
{{ with .additional_description }}
<div class="mt-4 text-sm text-gray-500">{{ . }}</div>
{{ end }}
<div class="mt-8">
<a href="{{ .button.url }}" class="btn btn-primary w-full">
{{ .button.text }}
</a>
</div>
</div>
</div>
</div>
{{ end }}
</div>
</div>
</div>
</section>
@@ -0,0 +1,60 @@
{{ $data := .Inner | unmarshal }}
<section class="bg-white">
<div class="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
{{ with $data.title }}
<div class="mx-auto max-w-screen-md text-center mb-8 lg:mb-12">
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900">{{ . }}</h2>
{{ with $data.description }}
<p class="mb-5 font-light text-gray-500 sm:text-xl">{{ . }}</p>
{{ end }}
</div>
{{ end }}
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
{{ range $index, $plan := $data.plans }}
<div class="relative flex flex-col p-6 {{ if $plan.featured }}bg-primary-600 text-white{{ else }}bg-white{{ end }} rounded-2xl shadow-xl transform hover:-translate-y-1 transition duration-300">
{{ if $plan.featured }}
<div class="absolute -top-4 left-1/2 transform -translate-x-1/2">
<span class="bg-yellow-400 text-gray-900 text-xs font-semibold px-4 py-1 rounded-full">{{ i18n "mostPopular" }}</span>
</div>
{{ end }}
<div class="mb-4">
<h3 class="text-2xl font-bold {{ if $plan.featured }}text-white{{ else }}text-gray-900{{ end }}">{{ $plan.name }}</h3>
<p class="mt-2 {{ if not $plan.featured }}text-gray-500{{ end }}">{{ $plan.description }}</p>
</div>
{{ if $plan.price }}
<div class="mb-6">
<div class="flex items-baseline">
<span class="text-5xl font-extrabold tracking-tight {{ if not $plan.featured }}text-gray-900{{ end }}">{{ $plan.price }}</span>
<span class="ml-1 {{ if not $plan.featured }}text-gray-500{{ end }}">{{ i18n $plan.price_unit }}</span>
</div>
<div class="flex items-baseline">
<span class="font-extrabold tracking-tight {{ if not $plan.featured }}text-gray-900{{ end }}">{{ $plan.additional_price }}</span>
</div>
</div>
{{ end }}
<ul class="mb-8 space-y-4 flex-grow">
{{ range $plan.features }}
<li class="flex items-center">
<svg class="w-5 h-5 {{ if $plan.featured }}text-white{{ else }}text-green-500{{ end }} mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="{{ if not $plan.featured }}text-gray-600{{ end }}">{{ . }}</span>
</li>
{{ end }}
</ul>
{{ with .additional_description }}
<div class="text-sm text-gray-500">{{ . }}</div>
{{ end }}
<a href="{{ $plan.button.url }}" class="mt-4 text-center w-full px-5 py-3 rounded-lg {{ if $plan.featured }}bg-white text-primary-600 hover:bg-gray-100{{ else }}bg-primary-600 text-white hover:bg-primary-700{{ end }} font-medium transition duration-300">
{{ $plan.button.text }}
</a>
</div>
{{ end }}
</div>
</div>
</section>
@@ -0,0 +1,5 @@
<section class="{{ .Get "class" }}">
<div class="container mx-auto px-4">
{{ .Inner }}
</div>
</section>
@@ -0,0 +1,4 @@
<div class="stat-card">
<div class="text-3xl md:text-4xl font-bold text-blue-600 mb-2">{{ .Get "number" }}</div>
<div class="text-gray-600 font-medium">{{ .Get "label" }}</div>
</div>
@@ -0,0 +1,22 @@
<div class="team-member text-center">
<div class="relative mb-4 rounded-lg overflow-hidden aspect-square">
{{ if .Get "image" }}
<img src="{{ .Get "image" }}" alt="{{ .Get "name" }}" class="w-full h-full object-cover">
{{ else }}
<div class="w-full h-full bg-gray-200 flex items-center justify-center">
<svg class="w-20 h-20 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 14c3.31 0 6-2.69 6-6s-2.69-6-6-6-6 2.69-6 6 2.69 6 6 6zm0 2c-4 0-12 2-12 6v2h24v-2c0-4-8-6-12-6z"/>
</svg>
</div>
{{ end }}
{{ if .Get "linkedin" }}
<a href="{{ .Get "linkedin" }}" target="_blank" rel="noopener" class="absolute bottom-2 right-2 bg-white rounded-full p-2 shadow-md hover:shadow-lg transition-shadow">
<svg class="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
</svg>
</a>
{{ end }}
</div>
<h3 class="text-xl font-bold mb-1">{{ .Get "name" }}</h3>
<p class="text-gray-600">{{ .Get "title" }}</p>
</div>
@@ -0,0 +1,183 @@
{{ $background_color := .Get "background-color" }}
<section class="section{{ if not $background_color }} bg-gray-50{{ end }}" {{ if $background_color }}style="background-color:{{ $background_color }};"{{ end }}>
<div class="container">
<div class="text-center max-w-3xl mx-auto mb-16">
<h2 class="text-3xl md:text-4xl font-bold mb-6">{{ .Get "title" | default (i18n "lovedByTeams") }}</h2>
<p class="text-xl text-gray-600">{{ .Get "description" | default (i18n "testimonialsDescription") }}</p>
</div>
<div class="testimonials-container overflow-hidden">
<div class="testimonials-track{{ if ne (.Get "animate" | default "true") "false" }} animate{{ end }}">
{{ range .Page.Params.testimonials }}
<div class="testimonial-card">
<div class="flex items-center space-x-4 mb-6">
<img src="{{ .avatar | relURL }}" alt="{{ .name }}" class="w-12 h-12 rounded-full">
<div>
<h4 class="font-bold">{{ .name }}</h4>
<p class="text-gray-600">{{ .title }}</p>
</div>
</div>
<p class="text-gray-600">{{ .quote }}</p>
</div>
{{ end }}
{{ if ne (.Get "animate" | default "true") "false" }}
{{ range .Page.Params.testimonials }}
<div class="testimonial-card" aria-hidden="true">
<div class="flex items-center space-x-4 mb-6">
<img src="{{ .avatar | relURL }}" alt="{{ .name }}" class="w-12 h-12 rounded-full">
<div>
<h4 class="font-bold">{{ .name }}</h4>
<p class="text-gray-600">{{ .title }}</p>
</div>
</div>
<p class="text-gray-600">{{ .quote }}</p>
</div>
{{ end }}
{{ end }}
</div>
</div>
</div>
</section>
{{ if ne (.Get "animate" | default "true") "false" }}
<script>
(function() {
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTestimonialsScroll);
} else {
initTestimonialsScroll();
}
function initTestimonialsScroll() {
const tracks = document.querySelectorAll('.testimonials-track.animate');
tracks.forEach(track => {
// Remove CSS animation and use JS for smoother control
track.style.animation = 'none';
const container = track.parentElement;
// Calculate the width of one set of testimonials (50% of track)
const trackWidth = track.scrollWidth;
const halfWidth = trackWidth / 2;
// Ensure track has valid dimensions before animating
if (trackWidth === 0 || halfWidth === 0) {
return;
}
let position = 0; // Current position in pixels
let isPaused = false;
let lastTime = performance.now();
// Check for mobile and set duration - responsive to window resize
function getDuration() {
return window.innerWidth <= 768 ? 15000 : 40000;
}
// Speed in pixels per millisecond (recalculated on resize)
let speed = halfWidth / getDuration();
function animate(currentTime) {
if (!isPaused) {
const delta = currentTime - lastTime;
position += speed * delta;
// Reset position seamlessly when we've moved one full set
if (position >= halfWidth) {
position = position - halfWidth;
}
track.style.transform = `translateX(-${position}px)`;
}
lastTime = currentTime;
requestAnimationFrame(animate);
}
// Start animation
requestAnimationFrame(animate);
// Pause on hover
container.addEventListener('mouseenter', () => {
isPaused = true;
});
container.addEventListener('mouseleave', () => {
isPaused = false;
lastTime = performance.now(); // Reset lastTime to avoid jump
});
// Handle window resize to adjust speed
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
speed = halfWidth / getDuration();
}, 250); // Debounce resize events
});
});
}
})();
</script>
{{ end }}
<style>
.testimonials-container {
padding: 20px 0;
position: relative;
}
.testimonials-container.static {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
overflow: visible;
}
.testimonials-track {
display: flex;
gap: 2rem;
width: fit-content;
}
.testimonials-track.animate {
animation: 40s testimonials-scroll infinite linear;
will-change: transform;
}
.testimonial-card {
flex: 0 0 auto;
width: 300px;
background: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.testimonials-container.static .testimonial-card {
width: 100%;
}
@keyframes testimonials-scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
.testimonials-container:hover .testimonials-track.animate {
animation-play-state: paused;
}
/* Mobile-specific animation speed */
@media (max-width: 768px) {
.testimonials-track.animate {
animation: 15s testimonials-scroll infinite linear;
will-change: transform;
}
}
</style>
@@ -0,0 +1,4 @@
<div class="bg-gray-50 p-6 rounded-lg mb-8">
<h2 class="text-xl font-semibold mb-4">{{ i18n "tableOfContents" }}</h2>
{{ .Page.TableOfContents }}
</div>
@@ -0,0 +1,21 @@
<div class="value-card bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div class="mb-4">
{{ $icon := .Get "icon" }}
{{ if eq $icon "lightbulb" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
</svg>
{{ else if eq $icon "users" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
</svg>
{{ else if eq $icon "eye" }}
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
{{ end }}
</div>
<h3 class="text-xl font-bold mb-2">{{ .Get "title" }}</h3>
<p class="text-gray-600">{{ .Get "description" }}</p>
</div>
@@ -0,0 +1,70 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-12">
<div class="max-w-4xl mx-auto">
<header class="mb-12">
<h1 class="text-4xl font-bold mb-4">
{{ .Data.Singular | title }}: {{ .Title }}
</h1>
<div class="text-xl text-gray-600">
{{ $count := len .Pages }}
{{ if eq $count 1 }}
1 post
{{ else }}
{{ $count }} posts
{{ end }}
</div>
</header>
<div class="grid gap-8">
{{ $paginator := .Paginate .Pages }}
{{ range $paginator.Pages }}
{{ partial "post-card.html" . }}
{{ end }}
</div>
{{ $paginator := .Paginate .Pages }}
{{ if gt $paginator.TotalPages 1 }}
<nav class="mt-12 flex justify-between items-center">
{{ if $paginator.HasPrev }}
<a href="{{ $paginator.Prev.URL }}"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors duration-200">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Previous
</a>
{{ else }}
<div></div>
{{ end }}
<div class="flex space-x-2">
{{ range $paginator.Pagers }}
{{ if eq . $paginator }}
<span class="px-4 py-2 bg-primary-600 text-white rounded-lg">
{{ .PageNumber }}
</span>
{{ else }}
<a href="{{ .URL }}"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors duration-200">
{{ .PageNumber }}
</a>
{{ end }}
{{ end }}
</div>
{{ if $paginator.HasNext }}
<a href="{{ $paginator.Next.URL }}"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors duration-200">
Next
<svg class="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</a>
{{ else }}
<div></div>
{{ end }}
</nav>
{{ end }}
</div>
</div>
{{ end }}
@@ -0,0 +1,39 @@
{{ define "main" }}
<div class="container mx-auto px-4 py-12">
<div class="max-w-4xl mx-auto">
<header class="mb-12">
<h1 class="text-4xl font-bold mb-4">
{{ .Title }}
</h1>
<div class="text-xl text-gray-600">
Browse all {{ .Data.Plural }}
</div>
</header>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{{ range .Data.Terms.ByCount }}
<a href="{{ .Page.RelPermalink }}"
class="group p-6 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200">
<h2 class="text-xl font-semibold mb-2 group-hover:text-primary-600">
{{ .Page.Title }}
</h2>
<div class="text-gray-600">
{{ .Count }}
{{ if eq .Count 1 }}
post
{{ else }}
posts
{{ end }}
</div>
</a>
{{ end }}
</div>
{{ if not .Data.Terms }}
<div class="text-center py-12 text-gray-600">
No {{ .Data.Plural }} found
</div>
{{ end }}
</div>
</div>
{{ end }}