feat(plugins-visuels): theme-guyane + landing-hero + landing-sections

Phase 2 visuals — la page d'accueil prend vie via 3 plugins activables :

- theme-guyane : palette tropicale (vert canopée, eau Maroni, ocre latérite,
  bois karbé, blanc cassé), tokens CSS, typographie display Cormorant Garamond,
  gradient ambient discret. Activé via body[data-theme=guyane].

- landing-hero : section plein écran avec illustration vectorielle SVG (carbet
  sur pilotis au crépuscule + fleuve + jungle), claim 'Le karbé qui dort vous
  attend', double CTA Découvrir / Proposer. Fallback = hero minimaliste actuel.

- landing-sections : 5 sections en cascade — 2 expériences (route+fleuve vs
  expédition fleuve), Comment ça marche (3 étapes), CE (registre coop sans
  commission), Témoignages (3 stubs), Footer riche avec navigation.

Illustrations 100% SVG inline (pas de dépendance image externe). Quand le
plugin image-gallery-seed sera activé (Phase 2.4), les photos remplaceront
progressivement les SVG. Aucune coupure sur le rendu actuel : tous les plugins
visuels sont disabled par défaut, le site garde son look minimaliste tant que
l'admin ne les a pas activés depuis /admin/plugins.
This commit is contained in:
Claude Integration 2026-05-30 23:19:24 +00:00
parent 4454f7331d
commit d19701e275
11 changed files with 691 additions and 30 deletions

View file

@ -10,10 +10,45 @@
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-serif: var(--font-serif);
/* === Theme Guyane (plugin theme-guyane) === */
/* Activé lorsque <body data-theme="guyane"> (gated par le plugin). */
--color-karbe-canopy-50: #f1f7f1;
--color-karbe-canopy-100: #dceadc;
--color-karbe-canopy-300: #82b58a;
--color-karbe-canopy-500: #2f7a3f; /* vert canopée Guyane */
--color-karbe-canopy-700: #1f5530;
--color-karbe-canopy-900: #103018;
--color-karbe-maroni-100: #e6e7d8; /* eau Maroni — vert-brun chargé latérite */
--color-karbe-maroni-300: #b4b690;
--color-karbe-maroni-500: #8a8a55;
--color-karbe-maroni-700: #5e5e32;
--color-karbe-laterite-300: #d99c6a; /* ocre latérite rougeâtre */
--color-karbe-laterite-500: #c46434;
--color-karbe-laterite-700: #8c3d18;
--color-karbe-wood-300: #c0a280;
--color-karbe-wood-500: #8d6b48; /* bois karbé */
--color-karbe-wood-700: #5a4329;
--color-karbe-bone: #f5f1e8; /* blanc cassé chaud */
--color-karbe-ink: #1a1a14;
}
body[data-theme="guyane"] {
--background: var(--color-karbe-bone);
--foreground: var(--color-karbe-ink);
font-family: var(--font-geist-sans), system-ui, sans-serif;
background-image:
radial-gradient(ellipse at top, rgba(47, 122, 63, 0.06) 0%, transparent 60%),
radial-gradient(ellipse at bottom, rgba(196, 100, 52, 0.04) 0%, transparent 60%);
}
@media (prefers-color-scheme: dark) {
:root {
:root:not([data-theme="guyane"]) {
--background: #0a0a0a;
--foreground: #ededed;
}

View file

@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Geist, Geist_Mono, Cormorant_Garamond } from "next/font/google";
import "./globals.css";
import { PluginProvider } from "@/lib/plugins/client";
import { getEnabledPluginKeys, syncPluginsFromRegistry } from "@/lib/plugins/server";
@ -14,6 +14,15 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
// Cormorant Garamond : typographie display pour le theme Guyane (gated par
// le plugin `theme-guyane`). Sert pour `font-serif`.
const cormorant = Cormorant_Garamond({
variable: "--font-serif",
subsets: ["latin"],
weight: ["400", "500", "600"],
display: "swap",
});
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000";
export const metadata: Metadata = {
@ -50,12 +59,17 @@ export default async function RootLayout({
enabledKeys = [];
}
const themeGuyane = enabledKeys.includes("theme-guyane");
return (
<html
lang="fr"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
className={`${geistSans.variable} ${geistMono.variable} ${cormorant.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<body
data-theme={themeGuyane ? "guyane" : undefined}
className="min-h-full flex flex-col font-sans"
>
<PluginProvider enabledKeys={enabledKeys}>{children}</PluginProvider>
</body>
</html>

View file

@ -1,32 +1,63 @@
import Link from "next/link";
import { IfPluginEnabled } from "@/components/IfPluginEnabled";
import { HeroSection } from "@/components/landing/HeroSection";
import { ExperiencesSection } from "@/components/landing/ExperiencesSection";
import { HowItWorksSection } from "@/components/landing/HowItWorksSection";
import { CESection } from "@/components/landing/CESection";
import { TestimonialsSection } from "@/components/landing/TestimonialsSection";
import { LandingFooter } from "@/components/landing/Footer";
/**
* Page d'accueil la majorité du contenu est conditionnée par les plugins :
* - `landing-hero` hero plein écran
* - `landing-sections` 2 expériences + comment ça marche + CE + témoignages + footer riche
*
* Si aucun de ces plugins n'est activé, on retombe sur la home historique
* minimaliste (fallback). Activable depuis /admin/plugins.
*/
export default function Home() {
return (
<div className="flex flex-1 items-center justify-center bg-zinc-50 px-6 dark:bg-black">
<main className="flex w-full max-w-2xl flex-col items-center gap-6 text-center">
<h1 className="text-4xl font-semibold tracking-tight text-black sm:text-5xl dark:text-zinc-50">
Karbé carbets fluviaux de Guyane
</h1>
<p className="max-w-xl text-lg leading-8 text-zinc-600 dark:text-zinc-400">
La marketplace pour louer des carbets le long des fleuves de Guyane.
Connecter voyageurs et hôtes pour des séjours authentiques au cœur de
la forêt amazonienne.
</p>
<div className="flex flex-wrap items-center justify-center gap-3">
<Link
href="/carbets"
className="rounded-md bg-emerald-600 px-5 py-2.5 text-sm font-semibold text-white hover:bg-emerald-700"
>
Découvrir les carbets
</Link>
<Link
href="/espace-hote"
className="rounded-md border border-zinc-300 px-5 py-2.5 text-sm font-medium text-zinc-700 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-900"
>
Espace hôte
</Link>
</div>
</main>
</div>
<>
<IfPluginEnabled
plugin="landing-hero"
fallback={
// Fallback héro minimaliste — historique
<div className="flex flex-1 items-center justify-center bg-zinc-50 px-6 dark:bg-black">
<main className="flex w-full max-w-2xl flex-col items-center gap-6 text-center">
<h1 className="text-4xl font-semibold tracking-tight text-black sm:text-5xl dark:text-zinc-50">
Karbé carbets fluviaux de Guyane
</h1>
<p className="max-w-xl text-lg leading-8 text-zinc-600 dark:text-zinc-400">
La marketplace pour louer des carbets le long des fleuves de Guyane.
</p>
<div className="flex flex-wrap items-center justify-center gap-3">
<Link
href="/carbets"
className="rounded-md bg-emerald-600 px-5 py-2.5 text-sm font-semibold text-white hover:bg-emerald-700"
>
Découvrir les carbets
</Link>
<Link
href="/espace-hote"
className="rounded-md border border-zinc-300 px-5 py-2.5 text-sm font-medium text-zinc-700 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-200 dark:hover:bg-zinc-900"
>
Espace hôte
</Link>
</div>
</main>
</div>
}
>
<HeroSection />
</IfPluginEnabled>
<IfPluginEnabled plugin="landing-sections">
<ExperiencesSection />
<HowItWorksSection />
<CESection />
<TestimonialsSection />
<LandingFooter />
</IfPluginEnabled>
</>
);
}

View file

@ -0,0 +1,128 @@
/**
* Illustration vectorielle carbet sur pilotis vu de l'eau, au crépuscule.
* Pure SVG, aucune dépendance externe. Style ligne claire stylisé.
*/
export function CarbetRiver({ className = "" }: { className?: string }) {
return (
<svg
viewBox="0 0 800 500"
xmlns="http://www.w3.org/2000/svg"
className={className}
aria-hidden
>
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#f5d09a" />
<stop offset="55%" stopColor="#e29067" />
<stop offset="100%" stopColor="#7b3a48" />
</linearGradient>
<linearGradient id="water" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#5e5e32" stopOpacity="0.55" />
<stop offset="100%" stopColor="#241d12" />
</linearGradient>
<linearGradient id="jungle" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#1f5530" />
<stop offset="100%" stopColor="#0a2516" />
</linearGradient>
</defs>
{/* sky */}
<rect width="800" height="320" fill="url(#sky)" />
{/* sun reflection */}
<circle cx="560" cy="220" r="42" fill="#f7e1b2" opacity="0.95" />
{/* distant jungle band */}
<path
d="M0,310 Q120,260 240,290 T480,275 T800,300 L800,330 L0,330 Z"
fill="url(#jungle)"
/>
{/* water */}
<rect y="320" width="800" height="180" fill="url(#water)" />
{/* water sun reflection lines */}
<g opacity="0.55">
<ellipse cx="560" cy="350" rx="80" ry="3" fill="#f7e1b2" />
<ellipse cx="560" cy="375" rx="55" ry="2" fill="#f7e1b2" />
<ellipse cx="560" cy="395" rx="35" ry="1.5" fill="#f7e1b2" />
</g>
{/* carbet shadow on water */}
<ellipse cx="270" cy="378" rx="115" ry="10" fill="#000" opacity="0.45" />
{/* stilts */}
<g fill="#5a4329">
<rect x="195" y="305" width="6" height="80" />
<rect x="245" y="305" width="6" height="80" />
<rect x="295" y="305" width="6" height="80" />
<rect x="335" y="305" width="6" height="80" />
</g>
{/* carbet floor */}
<rect x="180" y="290" width="180" height="14" fill="#8d6b48" />
<rect x="180" y="290" width="180" height="3" fill="#5a4329" />
{/* carbet wall */}
<rect x="195" y="225" width="150" height="65" fill="#c0a280" />
<rect x="195" y="225" width="150" height="65" fill="none" stroke="#5a4329" strokeWidth="2" />
{/* opening */}
<rect x="245" y="240" width="50" height="50" fill="#1a1a14" />
<rect x="245" y="240" width="50" height="50" fill="none" stroke="#5a4329" strokeWidth="1.5" />
{/* warm light from inside */}
<rect x="250" y="245" width="40" height="40" fill="#f5b65b" opacity="0.85" />
{/* roof — palm thatch */}
<path d="M170,225 L270,160 L370,225 Z" fill="#5a4329" />
<path d="M178,222 L270,168 L362,222 Z" fill="#7d5a3a" />
{/* roof thatch lines */}
<g stroke="#3d2d1a" strokeWidth="1" fill="none" opacity="0.6">
<path d="M195,222 L235,178" />
<path d="M215,222 L255,168" />
<path d="M235,222 L275,158" />
<path d="M270,222 L290,168" />
<path d="M305,222 L290,180" />
<path d="M325,222 L300,170" />
</g>
{/* palm tree on the right */}
<g>
<path d="M620,320 Q625,250 630,180" stroke="#3d2d1a" strokeWidth="6" fill="none" strokeLinecap="round" />
{/* leaves */}
<g fill="#1f5530">
<path d="M630,180 Q580,150 540,165 Q570,175 600,185 Z" />
<path d="M630,180 Q680,145 720,165 Q695,178 660,185 Z" />
<path d="M630,180 Q625,130 600,105 Q620,140 625,170 Z" />
<path d="M630,180 Q640,130 660,108 Q644,140 638,170 Z" />
<path d="M630,180 Q585,200 555,225 Q595,205 620,195 Z" />
<path d="M630,180 Q685,205 712,225 Q672,205 645,195 Z" />
</g>
</g>
{/* small palm left */}
<g>
<path d="M90,330 Q95,290 100,250" stroke="#3d2d1a" strokeWidth="4" fill="none" strokeLinecap="round" />
<g fill="#1f5530">
<path d="M100,250 Q65,235 40,243 Q75,250 95,254 Z" />
<path d="M100,250 Q135,232 160,242 Q125,252 105,254 Z" />
<path d="M100,250 Q95,215 85,195 Q100,225 102,250 Z" />
</g>
</g>
{/* foreground water ripples */}
<g stroke="#8a8a55" strokeWidth="1.2" fill="none" opacity="0.5">
<path d="M40,420 Q80,415 130,420 T240,418" />
<path d="M300,440 Q360,435 420,440 T540,438" />
<path d="M580,460 Q640,455 700,460 T760,458" />
<path d="M120,470 Q180,465 240,470 T350,468" />
</g>
{/* first stars */}
<g fill="#f5f1e8" opacity="0.8">
<circle cx="120" cy="60" r="1.4" />
<circle cx="380" cy="40" r="1.2" />
<circle cx="720" cy="80" r="1.6" />
</g>
</svg>
);
}

View file

@ -0,0 +1,93 @@
/**
* Mini-illustrations vectorielles icônes thématiques Karbé (route, pirogue,
* hamac, étoiles, palme, boussole, fleuve). Cohérentes ligne claire tropicale.
*/
const baseProps = {
viewBox: "0 0 64 64",
xmlns: "http://www.w3.org/2000/svg",
"aria-hidden": true as const,
};
export function RoadIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<path d="M14 56 L26 16 H38 L50 56" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
<path d="M32 22 V30 M32 36 V44 M32 50 V54" stroke="currentColor" strokeWidth="2.5" strokeDasharray="0 0" strokeLinecap="round" />
<path d="M22 12 Q32 8 42 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}
export function PirogueIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<path d="M6 42 Q32 60 58 42 L52 38 H12 Z" fill="none" stroke="currentColor" strokeWidth="3" strokeLinejoin="round" />
<path d="M12 38 H52" stroke="currentColor" strokeWidth="2.5" />
<path d="M22 38 V32 M32 38 V30 M42 38 V32" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<path d="M4 50 Q14 47 24 50 T44 50 T60 50" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" opacity="0.7" />
<path d="M2 56 Q12 53 22 56 T42 56 T58 56" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" opacity="0.5" />
</svg>
);
}
export function HammockIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<path d="M10 14 V50" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
<path d="M54 14 V50" stroke="currentColor" strokeWidth="3" strokeLinecap="round" />
<path d="M10 22 Q32 46 54 22" fill="none" stroke="currentColor" strokeWidth="3" strokeLinejoin="round" />
<path d="M10 22 Q32 38 54 22" fill="none" stroke="currentColor" strokeWidth="1.5" opacity="0.55" />
<path d="M12 14 H8 M52 14 H56" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
<path d="M14 8 L10 14 L6 8" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<path d="M50 8 L54 14 L58 8" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}
export function CompassIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<circle cx="32" cy="32" r="24" fill="none" stroke="currentColor" strokeWidth="3" />
<path d="M32 14 L36 30 L32 50 L28 30 Z" fill="currentColor" />
<path d="M14 32 H50" stroke="currentColor" strokeWidth="1" strokeDasharray="2 3" opacity="0.5" />
<circle cx="32" cy="32" r="2" fill="currentColor" />
</svg>
);
}
export function PalmIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<path d="M32 56 Q34 36 36 18" stroke="currentColor" strokeWidth="3" strokeLinecap="round" fill="none" />
<path d="M36 18 Q22 14 12 22 Q22 22 30 22 Z" fill="currentColor" opacity="0.85" />
<path d="M36 18 Q50 14 56 24 Q48 22 38 22 Z" fill="currentColor" opacity="0.85" />
<path d="M36 18 Q40 6 50 4 Q42 12 38 18 Z" fill="currentColor" opacity="0.85" />
<path d="M36 18 Q32 6 22 4 Q32 12 34 18 Z" fill="currentColor" opacity="0.85" />
<path d="M36 18 Q42 24 50 36 Q40 26 36 22 Z" fill="currentColor" opacity="0.85" />
</svg>
);
}
export function WaveIcon({ className = "" }: { className?: string }) {
return (
<svg viewBox="0 0 64 24" xmlns="http://www.w3.org/2000/svg" className={className} aria-hidden>
<path d="M0 12 Q8 6 16 12 T32 12 T48 12 T64 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<path d="M0 18 Q8 12 16 18 T32 18 T48 18 T64 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" opacity="0.6" />
</svg>
);
}
export function HeartHandIcon({ className = "" }: { className?: string }) {
return (
<svg {...baseProps} className={className}>
<path
d="M32 54 C20 46 10 38 10 26 C10 18 16 12 24 12 C28 12 30 14 32 18 C34 14 36 12 40 12 C48 12 54 18 54 26 C54 38 44 46 32 54 Z"
fill="none"
stroke="currentColor"
strokeWidth="3"
strokeLinejoin="round"
/>
</svg>
);
}

View file

@ -0,0 +1,55 @@
import Link from "next/link";
/**
* Section dédiée aux Comités d'Entreprise (CE). Registre coop/solidaire,
* voix différente du reste de la home (qui parle au touriste aventurier).
*/
export function CESection() {
return (
<section className="bg-[var(--color-karbe-canopy-700)] text-[var(--color-karbe-bone)]">
<div className="mx-auto grid max-w-6xl gap-8 px-6 py-20 sm:px-8 sm:py-24 lg:grid-cols-2 lg:gap-16 lg:px-12">
<div>
<span className="text-xs font-semibold uppercase tracking-[0.15em] text-[var(--color-karbe-laterite-300)]">
Pour comités d&apos;entreprise
</span>
<h2 className="mt-3 font-serif text-3xl font-medium leading-tight tracking-tight sm:text-4xl md:text-5xl">
Les carbets dorment quand vous n&apos;y êtes pas.
<br />
<span className="italic text-[var(--color-karbe-bone)]/80">Partageons-les.</span>
</h2>
<p className="mt-5 text-base leading-relaxed text-[var(--color-karbe-bone)]/85 sm:text-lg">
Karbé est conçu pour que les comités sociaux possédant déjà un carbet le réservent à
leurs membres certains week-ends, et l&apos;ouvrent au public touriste le reste de
l&apos;année. Sans commission sur le séjour : le paiement revient intégralement au CE.
</p>
</div>
<ul className="grid grid-cols-1 gap-4 self-center sm:grid-cols-2">
{[
{ k: "0 %", v: "de commission sur le séjour" },
{ k: "CE first", v: "vos membres réservent en priorité" },
{ k: "Public ouvert", v: "le reste des dates rentre dans le pot" },
{ k: "Sans paperasse", v: "Stripe encaisse et reverse direct" },
].map(({ k, v }) => (
<li
key={k}
className="rounded-2xl border border-[var(--color-karbe-bone)]/15 bg-[var(--color-karbe-bone)]/5 p-5 backdrop-blur-sm"
>
<div className="font-serif text-2xl font-medium text-[var(--color-karbe-laterite-300)]">{k}</div>
<div className="mt-1 text-sm leading-relaxed text-[var(--color-karbe-bone)]/85">{v}</div>
</li>
))}
<li className="sm:col-span-2">
<Link
href="/pour-comites-entreprise"
className="inline-flex items-center gap-2 rounded-full bg-[var(--color-karbe-laterite-500)] px-5 py-2.5 text-sm font-semibold text-[var(--color-karbe-bone)] transition hover:bg-[var(--color-karbe-laterite-700)]"
>
En savoir plus pour votre CE
<span aria-hidden></span>
</Link>
</li>
</ul>
</div>
</section>
);
}

View file

@ -0,0 +1,75 @@
import { RoadIcon, PirogueIcon } from "@/components/illustrations/Icons";
/**
* Section « 2 expériences » route+fleuve vs expédition fleuve.
* Reflète la distinction métier qu'on appliquera côté schema dans le plugin
* `access-type`. Ici on ne fait que l'éditorialiser pour la home.
*/
export function ExperiencesSection() {
return (
<section className="relative bg-[var(--color-karbe-bone)] py-20 sm:py-24">
<div className="mx-auto max-w-6xl px-6 sm:px-8 lg:px-12">
<div className="max-w-2xl">
<span className="text-xs font-semibold uppercase tracking-[0.15em] text-[var(--color-karbe-canopy-700)]">
Deux façons de vivre Karbé
</span>
<h2 className="mt-3 font-serif text-3xl font-medium tracking-tight text-[var(--color-karbe-ink)] sm:text-4xl md:text-5xl">
Du bord du fleuve à l&apos;expédition pirogue.
</h2>
<p className="mt-4 max-w-xl text-base leading-relaxed text-[var(--color-karbe-ink)]/75 sm:text-lg">
Selon l&apos;envie, on choisit le carbet qui se rejoint en voiture pour un week-end facile,
ou celui qu&apos;on n&apos;atteint qu&apos;en pirogue, à plusieurs heures du dernier village.
</p>
</div>
<div className="mt-12 grid gap-6 md:grid-cols-2 md:gap-8">
{/* Route + fleuve */}
<article className="group relative overflow-hidden rounded-3xl border border-[var(--color-karbe-canopy-100)] bg-white p-8 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
<div className="absolute right-6 top-6 h-12 w-12 rounded-full bg-[var(--color-karbe-canopy-50)] p-3 text-[var(--color-karbe-canopy-700)]">
<RoadIcon className="h-full w-full" />
</div>
<p className="text-xs font-semibold uppercase tracking-wider text-[var(--color-karbe-canopy-500)]">
🛣 Route + fleuve
</p>
<h3 className="mt-3 font-serif text-2xl font-medium text-[var(--color-karbe-ink)]">
Le carbet du week-end
</h3>
<p className="mt-3 text-sm leading-relaxed text-[var(--color-karbe-ink)]/75">
Accessible par la piste depuis Kourou, Saint-Laurent ou Régina. Garez la voiture,
prenez vos affaires et vous y êtes. Pour les familles, les couples qui veulent du calme
sans logistique, les CE qui réservent des séjours courts.
</p>
<ul className="mt-5 space-y-1.5 text-sm text-[var(--color-karbe-ink)]/70">
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-canopy-500)]" /> 1 à 3 nuits typiques</li>
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-canopy-500)]" /> Voiture ou 4×4 selon la piste</li>
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-canopy-500)]" /> Carbets équipés, baignade possible</li>
</ul>
</article>
{/* Expédition fleuve */}
<article className="group relative overflow-hidden rounded-3xl border border-[var(--color-karbe-laterite-300)]/60 bg-gradient-to-br from-[var(--color-karbe-canopy-50)] via-white to-[var(--color-karbe-bone)] p-8 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
<div className="absolute right-6 top-6 h-12 w-12 rounded-full bg-[var(--color-karbe-laterite-300)]/30 p-3 text-[var(--color-karbe-laterite-700)]">
<PirogueIcon className="h-full w-full" />
</div>
<p className="text-xs font-semibold uppercase tracking-wider text-[var(--color-karbe-laterite-700)]">
🛶 Expédition fleuve
</p>
<h3 className="mt-3 font-serif text-2xl font-medium text-[var(--color-karbe-ink)]">
Le carbet qu&apos;on mérite
</h3>
<p className="mt-3 text-sm leading-relaxed text-[var(--color-karbe-ink)]/75">
Aucune route n&apos;y mène. On embarque en pirogue depuis un dégrad, parfois deux ou
trois heures de remontée. Pour ceux qui veulent vraiment dormir loin singes hurleurs,
ciel sans halo, l&apos;eau du fleuve à 5 mètres du hamac.
</p>
<ul className="mt-5 space-y-1.5 text-sm text-[var(--color-karbe-ink)]/70">
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-laterite-500)]" /> 2 nuits minimum recommandées</li>
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-laterite-500)]" /> Pirogue avec passeur (loueur ou partenaire)</li>
<li className="flex items-center gap-2"><span className="h-1 w-1 rounded-full bg-[var(--color-karbe-laterite-500)]" /> Saison sèche conseillée (juillet-novembre)</li>
</ul>
</article>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,58 @@
import Link from "next/link";
import { PalmIcon, WaveIcon } from "@/components/illustrations/Icons";
export function LandingFooter() {
const year = new Date().getFullYear();
return (
<footer className="relative bg-[var(--color-karbe-canopy-900)] text-[var(--color-karbe-bone)]/85">
<WaveIcon className="absolute inset-x-0 -top-px h-6 w-full -translate-y-full text-[var(--color-karbe-canopy-900)]" />
<div className="mx-auto grid max-w-6xl gap-10 px-6 py-14 sm:grid-cols-2 sm:px-8 sm:py-16 lg:grid-cols-4 lg:px-12">
<div>
<div className="flex items-center gap-2">
<PalmIcon className="h-8 w-8 text-[var(--color-karbe-laterite-300)]" />
<span className="font-serif text-2xl font-medium tracking-tight text-[var(--color-karbe-bone)]">Karbé</span>
</div>
<p className="mt-3 text-sm leading-relaxed text-[var(--color-karbe-bone)]/65">
Marketplace des carbets fluviaux de Guyane. Solidaire avec les CE locaux. Sans
commission sur le séjour.
</p>
</div>
<div>
<h4 className="text-xs font-semibold uppercase tracking-wider text-[var(--color-karbe-laterite-300)]">Découvrir</h4>
<ul className="mt-4 space-y-2 text-sm">
<li><Link href="/carbets" className="hover:text-[var(--color-karbe-bone)]">Tous les carbets</Link></li>
<li><Link href="/comment-ca-marche" className="hover:text-[var(--color-karbe-bone)]">Comment ça marche</Link></li>
<li><Link href="/a-propos" className="hover:text-[var(--color-karbe-bone)]">À propos de Karbé</Link></li>
</ul>
</div>
<div>
<h4 className="text-xs font-semibold uppercase tracking-wider text-[var(--color-karbe-laterite-300)]">Proposer</h4>
<ul className="mt-4 space-y-2 text-sm">
<li><Link href="/espace-hote" className="hover:text-[var(--color-karbe-bone)]">Devenir loueur</Link></li>
<li><Link href="/pour-comites-entreprise" className="hover:text-[var(--color-karbe-bone)]">Pour comités d&apos;entreprise</Link></li>
<li><Link href="/connexion" className="hover:text-[var(--color-karbe-bone)]">Espace membre</Link></li>
</ul>
</div>
<div>
<h4 className="text-xs font-semibold uppercase tracking-wider text-[var(--color-karbe-laterite-300)]">Légal</h4>
<ul className="mt-4 space-y-2 text-sm">
<li><Link href="/cgv" className="hover:text-[var(--color-karbe-bone)]">CGV</Link></li>
<li><Link href="/mentions-legales" className="hover:text-[var(--color-karbe-bone)]">Mentions légales</Link></li>
<li><Link href="/politique-de-confidentialite" className="hover:text-[var(--color-karbe-bone)]">Confidentialité</Link></li>
</ul>
</div>
</div>
<div className="border-t border-[var(--color-karbe-bone)]/10">
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-2 px-6 py-5 text-xs text-[var(--color-karbe-bone)]/55 sm:flex-row sm:px-8 lg:px-12">
<span>© {year} Karbé projet associatif numérique en Guyane.</span>
<span className="font-mono text-[var(--color-karbe-bone)]/40">karbe.cosmolan.fr</span>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,53 @@
import Link from "next/link";
import { CarbetRiver } from "@/components/illustrations/CarbetRiver";
/**
* Hero plein écran. Plugin `landing-hero`.
* Pas de dépendance image externe illustration vectorielle inline.
*/
export function HeroSection() {
return (
<section className="relative isolate overflow-hidden bg-[var(--color-karbe-canopy-900)] text-[var(--color-karbe-bone)]">
{/* fond illustration */}
<div className="absolute inset-0 -z-10">
<CarbetRiver className="h-full w-full object-cover opacity-90 [object-position:center_60%]" />
{/* voile sombre pour lisibilité texte */}
<div className="absolute inset-0 bg-gradient-to-t from-[var(--color-karbe-canopy-900)] via-[var(--color-karbe-canopy-900)]/45 to-transparent" />
<div className="absolute inset-0 bg-gradient-to-r from-[var(--color-karbe-canopy-900)]/70 via-transparent to-transparent" />
</div>
<div className="mx-auto flex min-h-[78vh] max-w-6xl flex-col items-start justify-end gap-6 px-6 pb-16 pt-32 sm:px-8 sm:pb-20 sm:pt-40 lg:px-12">
<span className="inline-flex items-center gap-2 rounded-full border border-[var(--color-karbe-bone)]/30 bg-[var(--color-karbe-bone)]/10 px-3 py-1 text-xs font-medium uppercase tracking-wider backdrop-blur-sm">
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-karbe-laterite-300)]" />
Marketplace solidaire sans commission sur le séjour
</span>
<h1 className="max-w-3xl font-serif text-4xl font-medium leading-[1.05] tracking-tight sm:text-5xl md:text-6xl lg:text-7xl">
Le karbé qui dort
<br />
<span className="text-[var(--color-karbe-laterite-300)] italic">vous attend</span>.
</h1>
<p className="max-w-xl text-base leading-relaxed text-[var(--color-karbe-bone)]/85 sm:text-lg">
Louez un carbet le long du Maroni, de l&apos;Approuague ou de l&apos;Oyapock. Le hamac est tendu,
la pirogue glisse, le silence est vrai. Pour quelques nuits, le fleuve vous appartient.
</p>
<div className="mt-2 flex flex-wrap items-center gap-3">
<Link
href="/carbets"
className="rounded-full bg-[var(--color-karbe-laterite-500)] px-6 py-3 text-sm font-semibold text-[var(--color-karbe-bone)] shadow-lg shadow-black/30 transition hover:bg-[var(--color-karbe-laterite-700)]"
>
Découvrir un carbet
</Link>
<Link
href="/espace-hote"
className="rounded-full border border-[var(--color-karbe-bone)]/40 bg-[var(--color-karbe-bone)]/5 px-6 py-3 text-sm font-medium text-[var(--color-karbe-bone)] backdrop-blur-sm transition hover:bg-[var(--color-karbe-bone)]/15"
>
Proposer le mien
</Link>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,61 @@
import { CompassIcon, HammockIcon, HeartHandIcon } from "@/components/illustrations/Icons";
/**
* Section « Comment ça marche » 3 étapes côté voyageur.
*/
export function HowItWorksSection() {
const steps = [
{
icon: CompassIcon,
step: "01",
title: "Choisissez le fleuve",
body:
"Maroni, Approuague, Comté, Oyapock — chaque fleuve a son ambiance, son embarquement, ses carbets. Filtrez selon votre niveau d'aventure.",
},
{
icon: HammockIcon,
step: "02",
title: "Réservez le carbet",
body:
"Dates, capacité, durée de pirogue le cas échéant. Paiement sécurisé Stripe, reversé au loueur sans commission sur le séjour.",
},
{
icon: HeartHandIcon,
step: "03",
title: "Dormez vrai",
body:
"Le loueur (ou son partenaire) vous récupère au dégrad si besoin. Vous récupérez les clés du karbé, tendez le hamac, écoutez. Plus rien à faire.",
},
];
return (
<section className="bg-gradient-to-b from-[var(--color-karbe-bone)] to-[var(--color-karbe-canopy-50)] py-20 sm:py-24">
<div className="mx-auto max-w-6xl px-6 sm:px-8 lg:px-12">
<div className="max-w-2xl">
<span className="text-xs font-semibold uppercase tracking-[0.15em] text-[var(--color-karbe-canopy-700)]">
Comment ça marche
</span>
<h2 className="mt-3 font-serif text-3xl font-medium tracking-tight text-[var(--color-karbe-ink)] sm:text-4xl">
Trois étapes pour s&apos;échapper.
</h2>
</div>
<ol className="mt-12 grid gap-6 sm:grid-cols-3 sm:gap-8">
{steps.map(({ icon: Icon, step, title, body }) => (
<li
key={step}
className="relative flex flex-col gap-3 rounded-3xl border border-[var(--color-karbe-canopy-100)] bg-white/80 p-7 shadow-sm backdrop-blur-sm"
>
<div className="flex items-center justify-between">
<Icon className="h-10 w-10 text-[var(--color-karbe-canopy-700)]" />
<span className="font-serif text-3xl text-[var(--color-karbe-canopy-300)]">{step}</span>
</div>
<h3 className="mt-2 font-serif text-xl font-medium text-[var(--color-karbe-ink)]">{title}</h3>
<p className="text-sm leading-relaxed text-[var(--color-karbe-ink)]/75">{body}</p>
</li>
))}
</ol>
</div>
</section>
);
}

View file

@ -0,0 +1,58 @@
/**
* Section témoignages 3 stubs avec noms/contextes plausibles Guyane.
* Les contenus sont éditorialisés par défaut, remplaçables via le plugin
* `content-pages` (Phase 4) qui fournira un store éditable depuis l'admin.
*/
export function TestimonialsSection() {
const items = [
{
name: "Émilie · CE Hôpital Cayenne",
from: "Carbet sur le Comté",
quote:
"On a ouvert le karbé du CE aux touristes deux week-ends par mois. Les revenus financent l'entretien sans qu'on doive demander au directeur.",
},
{
name: "Yann · loueur particulier",
from: "Carbet sur le Maroni",
quote:
"Mon karbé dormait six mois par an. Je l'ai mis sur Karbé, j'ai cinq résas depuis. Et zéro commission, ça change tout.",
},
{
name: "Marina · touriste métropole",
from: "Karbé Awara, Approuague",
quote:
"Trois heures de pirogue, et soudain plus aucun bruit humain. J'ai dormi comme jamais. La photo du passeur en train de pêcher est ma photo de l'année.",
},
];
return (
<section className="bg-[var(--color-karbe-bone)] py-20 sm:py-24">
<div className="mx-auto max-w-6xl px-6 sm:px-8 lg:px-12">
<div className="max-w-2xl">
<span className="text-xs font-semibold uppercase tracking-[0.15em] text-[var(--color-karbe-canopy-700)]">
Pas de marketing
</span>
<h2 className="mt-3 font-serif text-3xl font-medium tracking-tight text-[var(--color-karbe-ink)] sm:text-4xl">
Ils nous l&apos;ont dit comme ça.
</h2>
</div>
<ul className="mt-12 grid gap-6 md:grid-cols-3">
{items.map(({ name, from, quote }) => (
<li
key={name}
className="flex flex-col gap-4 rounded-3xl border border-[var(--color-karbe-canopy-100)] bg-white p-7 shadow-sm"
>
<span className="text-3xl leading-none text-[var(--color-karbe-laterite-500)]">&ldquo;</span>
<p className="text-sm leading-relaxed text-[var(--color-karbe-ink)]/85">{quote}</p>
<div className="mt-auto border-t border-[var(--color-karbe-canopy-100)] pt-4">
<div className="text-sm font-semibold text-[var(--color-karbe-ink)]">{name}</div>
<div className="text-xs text-[var(--color-karbe-ink)]/60">{from}</div>
</div>
</li>
))}
</ul>
</div>
</section>
);
}