Converti chill-theme en dossier classique
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user