31 Commits

Author SHA1 Message Date
Shaz a343bb23e9 en attendant les tarifs
Build & Deploy / build-push-deploy (push) Failing after 5s
2026-06-23 00:01:27 +02:00
Shaz 9406cadb12 test
Build & Deploy / build-push-deploy (push) Failing after 4s
2026-06-21 23:17:47 +02:00
Shaz bffa738793 blabla
Build & Deploy / build-push-deploy (push) Failing after 4s
2026-06-21 23:11:14 +02:00
Shaz 05dc9cd5b2 update
Build & Deploy / build-push-deploy (push) Failing after 5s
2026-06-20 02:02:32 +02:00
Shaz 0692270628 sss
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-16 23:38:12 +02:00
Shaz 30337fd3c0 toto
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-16 23:35:29 +02:00
Shaz c345752632 putain de vscode
Build & Deploy / build-push-deploy (push) Failing after 3s
2026-06-16 23:28:10 +02:00
Shaz fe73342af8 update
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-16 23:14:32 +02:00
Shaz 64d99c9631 fix: Pricing dans index, liens navbar, h2 About, plage→rivière, grille Features 5 cols
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-16 22:58:47 +02:00
Shaz 6a3775b3e5 content: accordéons location, sans wifi, nettoyage doublon
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-14 21:15:52 +02:00
Shaz 062e815549 content: positionnement GR470, activités, ton authentique de la maison
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-06-14 20:52:51 +02:00
Shaz 8bb70aca2f feat: images locales avec composant Image Astro (WebP auto-optimisé)
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-05-30 19:01:55 +02:00
Shaz c6d4351ec7 zfesgfsdsfd
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-05-30 18:53:31 +02:00
Shaz a7ba3c0f69 toto
Build & Deploy / build-push-deploy (push) Failing after 2s
2026-05-30 17:35:56 +02:00
Shaz feca0102da la putain de merde
Build & Deploy / build-push-deploy (push) Failing after 0s
2026-05-30 17:13:24 +02:00
Shaz 60bde296aa ci: revert docker path to plain docker
Build & Deploy / build-push-deploy (push) Failing after 1s
2026-05-30 17:11:42 +02:00
Shaz ce5002e2fb qsdqsd
Build & Deploy / build-push-deploy (push) Failing after 0s
2026-05-30 17:07:44 +02:00
Shaz b7e8def54f sfdsf
Build & Deploy / build-push-deploy (push) Failing after 0s
2026-05-30 17:05:10 +02:00
Shaz 8cbf301548 sdf
Build & Deploy / build-push-deploy (push) Failing after 1s
2026-05-30 17:02:47 +02:00
Shaz 763aafbdaa trrt
Build & Deploy / build-push-deploy (push) Failing after 1s
2026-05-30 17:00:24 +02:00
Shaz e7aed17075 ci: chemin absolu /usr/bin/docker
Build & Deploy / build-push-deploy (push) Failing after 0s
2026-05-30 16:58:34 +02:00
Shaz 08a13bcd0f ci: remplace actions/checkout par git clone natif (runner host sans node)
Build & Deploy / build-push-deploy (push) Failing after 0s
2026-05-30 16:57:42 +02:00
Shaz 71272495b9 toti
Build & Deploy / build-push-deploy (push) Failing after 8s
2026-05-30 16:45:26 +02:00
Shaz d70b109851 ci: retrigger workflow
Build & Deploy / build-push-deploy (push) Has been cancelled
2026-05-30 16:32:50 +02:00
Shaz b34d8894b2 tata
Build & Deploy / build-push-deploy (push) Has been cancelled
2026-05-30 16:30:46 +02:00
Shaz d508ede166 ci: simplification workflow — commandes shell directes
Build & Deploy / build-push-deploy (push) Has been cancelled
2026-05-30 16:20:06 +02:00
Shaz 68ad7516da Toto
Build & Deploy / build-and-push (push) Has been cancelled
Build & Deploy / deploy (push) Has been cancelled
2026-05-30 16:18:15 +02:00
Shaz 080ebb2d4d ci: fix runs-on label → homelab
Build & Deploy / build-and-push (push) Has been cancelled
Build & Deploy / deploy (push) Has been cancelled
2026-05-28 23:58:15 +02:00
Shaz a7bf63053d ci: ajout workflow build & deploy Gitea Actions
Build & Deploy / build-and-push (push) Has been cancelled
Build & Deploy / deploy (push) Has been cancelled
2026-05-28 23:52:26 +02:00
Shaz 1411e6816d feat: site Le Moulin des Rives — design Tailwind + config Docker 2026-05-28 23:46:36 +02:00
houston[bot] 87186db304 "Initial commit from Astro" 2026-05-28 22:56:09 +02:00
48 changed files with 6807 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules
.astro
dist
.git
*.md
+36
View File
@@ -0,0 +1,36 @@
name: Build & Deploy
on:
push:
branches: [master]
env:
REGISTRY: gitea.lan
IMAGE: gitea.lan/shaz/le-site-de-ma-mamounette
jobs:
build-push-deploy:
runs-on: homelab
steps:
- name: Checkout
run: |
rm -rf workspace
git clone http://192.168.1.18:3000/Shaz/Le-site-de-ma-mamounette.git workspace
cd workspace && git checkout ${{ gitea.sha }}
- name: Login au registry Gitea
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.lan -u shaz --password-stdin
- name: Build & push image
run: |
docker build -t ${{ env.IMAGE }}:latest workspace/
docker push ${{ env.IMAGE }}:latest
- name: Déployer sur le serveur
run: |
docker compose -f /srv/ssd/stacks/moulin/docker-compose.yml pull
docker compose -f /srv/ssd/stacks/moulin/docker-compose.yml up -d
docker compose -f /srv/ssd/stacks/moulin/docker-compose.yml up -d --pull always
+24
View File
@@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/
+4
View File
@@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}
+11
View File
@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}
+18
View File
@@ -0,0 +1,18 @@
# ── Étape 1 : build ──────────────────────────────────────────────
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# ── Étape 2 : serveur statique ───────────────────────────────────
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
+42 -1
View File
@@ -1,2 +1,43 @@
# Le-site-de-ma-mamounette
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
+11
View File
@@ -0,0 +1,11 @@
// @ts-check
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [tailwindcss()]
}
});
+6
View File
@@ -0,0 +1,6 @@
services:
web:
image: gitea.lan/shaz/le-site-de-ma-mamounette:latest
ports:
- "127.0.0.1:8082:80"
restart: unless-stopped
+22
View File
@@ -0,0 +1,22 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Compression
gzip on;
gzip_types text/plain text/css application/javascript image/svg+xml;
# Cache long durée pour les assets avec hash
location ~* \.(js|css|woff2?|png|jpg|jpeg|webp|svg|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Toujours servir index.html (site one-page)
location / {
try_files $uri $uri/ /index.html;
}
}
+5359
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
{
"name": "evolved-event",
"type": "module",
"version": "0.0.1",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@tailwindcss/vite": "^4.3.0",
"astro": "^6.4.2",
"lucide-astro": "^0.556.0",
"tailwindcss": "^4.3.0"
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
// Autoriser uniquement les requêtes POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée.']);
exit;
}
// Protection anti-spam (honeypot)
if (!empty($_POST['website'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Spam détecté.']);
exit;
}
// Récupération et nettoyage des champs
$nom = trim(strip_tags($_POST['nom'] ?? ''));
$email = trim(strip_tags($_POST['email'] ?? ''));
$message = trim(strip_tags($_POST['message'] ?? ''));
// Validation
$errors = [];
if (empty($nom)) $errors[] = 'Le nom est requis.';
if (empty($email)) $errors[] = "L'adresse email est requise.";
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = "L'adresse email n'est pas valide.";
if (empty($message)) $errors[] = 'Le message est requis.';
if (strlen($message) > 5000) $errors[] = 'Le message est trop long (5000 caractères max).';
if (!empty($errors)) {
http_response_code(422);
echo json_encode(['success' => false, 'message' => implode(' ', $errors)]);
exit;
}
// Destinataire
$to = 'contact@la-maison-de-cecile.fr';
$subject = '=?UTF-8?B?' . base64_encode('[La Maison de Cécile] Message de ' . $nom) . '?=';
// Corps du mail
$body = "Vous avez reçu un nouveau message via le formulaire de contact.\n\n";
$body .= "Nom : {$nom}\n";
$body .= "Email : {$email}\n";
$body .= "Message:\n{$message}\n";
// En-têtes
$headers = "From: =?UTF-8?B?" . base64_encode($nom) . "?= <no-reply@la-maison-de-cecile.fr>\r\n";
$headers .= "Reply-To: {$email}\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
$headers .= "Content-Transfer-Encoding: base64\r\n";
$success = mail($to, $subject, base64_encode($body), $headers);
if ($success) {
echo json_encode(['success' => true, 'message' => 'Votre message a bien été envoyé. Nous vous répondrons sous 24h.']);
} else {
http_response_code(500);
echo json_encode(['success' => false, 'message' => "Une erreur s'est produite. Veuillez réessayer ou nous contacter par téléphone."]);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+164
View File
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
role="img"
aria-labelledby="title desc">
<title id="title">La Maison de Cécile — favicon maison rivière pont</title>
<desc id="desc">Icône simple avec maison blanche, toit couleur tuile, pont en arc et rivière bleue en forme de S.</desc>
<defs>
<clipPath id="circleClip">
<circle cx="256" cy="256" r="240"/>
</clipPath>
<linearGradient id="backgroundGradient" x1="256" y1="16" x2="256" y2="496" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#7EA35F"/>
<stop offset="1" stop-color="#3F703A"/>
</linearGradient>
<linearGradient id="riverGradient" x1="210" y1="230" x2="285" y2="505" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#8CC2C6"/>
<stop offset="1" stop-color="#4E8F9A"/>
</linearGradient>
</defs>
<!-- Background circle -->
<circle cx="256" cy="256" r="240" fill="url(#backgroundGradient)"/>
<g clip-path="url(#circleClip)">
<!-- Distant soft hill -->
<path
d="M-20 242
C70 210 145 220 220 250
C315 288 392 240 532 210
L532 512 L-20 512 Z"
fill="#5D8749"
opacity="0.72"/>
<!-- Left foreground hill -->
<path
d="M-20 360
C65 292 150 282 235 338
C170 405 100 455 -20 512 Z"
fill="#315F31"/>
<!-- Right foreground hill -->
<path
d="M532 350
C455 286 360 283 280 338
C350 414 430 465 532 512 Z"
fill="#315F31"/>
<!-- River body, S shape -->
<path
d="M279 247
C226 275 224 313 267 337
C315 364 323 404 272 430
C235 449 206 470 198 512
L351 512
C373 466 359 431 320 407
C280 382 286 358 330 334
C381 306 380 264 322 239
Z"
fill="url(#riverGradient)"/>
<!-- River white banks/highlights -->
<path
d="M284 259
C238 282 242 309 277 327
C326 352 337 400 282 426"
fill="none"
stroke="#FFFFFF"
stroke-width="16"
stroke-linecap="round"
opacity="0.95"/>
<path
d="M221 452
C247 436 280 429 303 406"
fill="none"
stroke="#FFFFFF"
stroke-width="13"
stroke-linecap="round"
opacity="0.95"/>
<!-- Bridge deck -->
<path
d="M82 336
C148 300 204 283 256 283
C308 283 364 300 430 336
L430 373
C365 336 309 319 256 319
C203 319 147 336 82 373 Z"
fill="#E7DFC8"/>
<!-- Bridge arch cutout -->
<path
d="M204 372
C213 333 299 333 308 372
L308 410
L204 410 Z"
fill="#315F31"/>
<!-- Subtle bridge outline -->
<path
d="M87 336
C150 302 205 286 256 286
C307 286 362 302 425 336"
fill="none"
stroke="#FFFFFF"
stroke-width="8"
stroke-linecap="round"
opacity="0.95"/>
<!-- House body -->
<rect x="173" y="165" width="166" height="126" rx="6" fill="#FFFFFF"/>
<!-- Roof fill -->
<path
d="M151 170
L256 78
L361 170
L343 190
L256 113
L169 190 Z"
fill="#C96F3A"/>
<!-- Roof underside / clean edge -->
<path
d="M171 181
L256 108
L341 181"
fill="none"
stroke="#B95F32"
stroke-width="13"
stroke-linecap="round"
stroke-linejoin="round"/>
<!-- Chimney -->
<rect x="306" y="102" width="30" height="68" rx="5" fill="#FFFFFF"/>
<!-- Repaint roof over chimney base for clean overlap -->
<path
d="M151 170
L256 78
L361 170"
fill="none"
stroke="#C96F3A"
stroke-width="22"
stroke-linecap="round"
stroke-linejoin="round"/>
<!-- Door -->
<rect x="207" y="231" width="33" height="60" rx="4" fill="#315F31"/>
<!-- Window -->
<g fill="#315F31">
<rect x="272" y="212" width="20" height="20" rx="3"/>
<rect x="300" y="212" width="20" height="20" rx="3"/>
<rect x="272" y="240" width="20" height="20" rx="3"/>
<rect x="300" y="240" width="20" height="20" rx="3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

+82
View File
@@ -0,0 +1,82 @@
---
import { Waves, Sprout, Bed, WifiOff, HousePlus } from 'lucide-astro';
import { Image } from 'astro:assets';
import facade from '../assets/images/façade.jpg';
import vue_arriere from '../assets/images/vue_arriere.jpg';
import terrasse from '../assets/images/terrasse.jpg';
import hamac from '../assets/images/hamac.jpg';
---
<section id="about" class="py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<div class="max-w-6xl mx-auto px-6">
<div class="grid md:grid-cols-2 gap-16 items-center">
<div>
<span class="block text-xs tracking-[0.3em] uppercase text-primary mb-5">
La maison
</span>
<h2 class="font-display text-5xl md:text-6xl font-light leading-tight mb-8">
Entre rivière, prairies et forêts verdoyantes...
</h2>
<p class="text-dark/80 leading-relaxed mb-4">
Charmante maison de village surplombant l'Allier, Cécile l'a faite à son image :
chaleureuse, vivante, colorée.
Vous pouvez profiter de la terrasse abritée et du jardin, accueillant et verdoyant où il fait bon traîner.<br/>
Ses murs épais garantissent une fraîcheur bienvenue par grosses chaleurs.
On y arrive parfois fatigué du sentier, on en repart ressourcé.
</p>
<p class="text-dark/70 leading-relaxed mb-8">
Pas de l'hôtellerie, pas un gîte comme les autres... plutôt une escale humaine,
dans une maison qui a une âme.
</p>
<ul class="space-y-3 list-none">
<li class="flex items-center gap-3 text-dark/70 font-semibold">
<Waves class="w-8 h-8 text-blue-500 flex-shrink-0" />
À 500m de la plage
</li>
<li class="flex items-center gap-3 text-dark/70 font-semibold">
<Sprout class="w-8 h-8 text-green-500 flex-shrink-0" />
Jardin, terrasse et coins de verdure
</li>
<li class="flex items-center gap-3 text-dark/70 font-semibold">
<Bed class="w-8 h-8 text-red-500 flex-shrink-0" />
3 chambres · 1 dortoir · Jusqu'à 9 personnes
</li>
<li class="flex items-center gap-3 text-dark/70 font-semibold">
<HousePlus class="w-8 h-8 text-yellow-500 flex-shrink-0" />
Au format Gite ou chambre d'hôtes, selon vos envies
</li>
<li class="flex items-center gap-3 text-dark/70 font-semibold">
<WifiOff class="w-8 h-8 text-gray-500 flex-shrink-0" />
Sans wifi, pour mieux décrocher
</li>
</ul>
</div>
<div class="relative flex items-center justify-center">
<div class="grid grid-cols-2 md:grid-cols-2 gap-1 bg-white p-1 rounded-md shadow-lg">
<Image
src={facade}
alt="La Maison de Cécile"
class="w-full h-64 object-cover rounded-sm"
/>
<Image
src={vue_arriere}
alt="Jardin et terrasse de la maison de Cécile"
class="w-full h-64 object-cover rounded-sm"
/>
<Image
src={terrasse}
alt="Vue sur l'Allier depuis la maison de Cécile"
class="w-full h-64 object-cover rounded-sm"
/>
<Image
src={hamac}
alt="Vue sur la terrasse et le jardin de la maison de Cécile"
class="w-full h-64 object-cover rounded-sm"
/>
</div>
</div>
</div>
</section>
+220
View File
@@ -0,0 +1,220 @@
---
import { Car, Clock, Train } from 'lucide-astro';
---
<section id="activities" class="py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<div class="max-w-6xl mx-auto px-6">
<div class="mb-14 text-center">
<span class="block text-xs tracking-[0.3em] uppercase text-primary mb-4">Une étape idéale</span>
<h2 class="font-display text-5xl font-normal">Bienvenue a Pont d'Alleyras</h2>
<p class="text-dark/80 mt-4 max-w-4xl mx-auto ">
Niché dans les gorges sauvages du Haut-Allier, au sud de la Haute-Loire et a deux pas de la Lozére,<br/> le village du Pont d'Alleyras est un point de départ idéal pour les randonneurs, cyclistes et amateurs de nature.
</p>
</div>
<div class="grid md:grid-cols-2 gap-16 items-start mb-16">
<!-- Accordéons activités -->
<div class="space-y-1">
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🏘️</span>
<span class="font-serif text-xl font-light">Le village de Pont d'Alleyras</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>Pont d'Alleyras est un village vivant, bien plus animé qu'il n'y paraît :</p>
<ul class="space-y-1 pl-4 border-l border-primary/30">
<li>Un restaurant gastronomique étoilé</li>
<li>Un marché le jeudi soir</li>
<li>Un théâtre</li>
<li>Des vendeurs ambulants qui s'annoncent à coups de klaxon</li>
</ul>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🥾</span>
<span class="font-serif text-xl font-light">GR470 · Gorges de l'Allier</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>
Le GR470 relie Brioude aux sources de l'Allier sur ~190 km. Alleyras se situe
exactement entre deux étapes officielles :
</p>
<div class="pl-4 border-l border-primary/30 space-y-1">
<div>Étape 6 · Monistrol-d'Allier → Pont d'Alleyras <span class="text-primary font-medium">17,6 km</span></div>
<div>Étape 7 · Pont d'Alleyras → Chapeauroux <span class="text-primary font-medium">22,2 km</span></div>
</div>
<a
href="https://www.gr-infos.com/gr470.htm"
target="_blank"
rel="noopener noreferrer"
class="inline-block text-xs tracking-[0.15em] uppercase text-primary border-b border-primary/40 hover:border-primary transition-colors mt-1"
>
Fiche officielle du GR470 →
</a>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🚴</span>
<span class="font-serif text-xl font-light">Via Allier · Nevers/Langogne à vélo</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>
La Via Allier est un itinéraire cyclable de 300 km le long de la rivière Allier.
Alleyras est un point de passage idéal pour les cyclistes itinérants, avec des
hébergements et services adaptés.
</p>
<div class="pl-4 border-l border-primary/30 space-y-1">
<div>Étape 6 · Monistrol-d'Allier → Pont d'Alleyras <span class="text-primary font-medium">17,6 km</span></div>
<div>Étape 7 · Pont d'Alleyras → Chapeauroux <span class="text-primary font-medium">22,2 km</span></div>
</div>
<a
href="https://www.via-allier.com/"
target="_blank"
rel="noopener noreferrer"
class="inline-block text-xs tracking-[0.15em] uppercase text-primary border-b border-primary/40 hover:border-primary transition-colors mt-1"
>
Fiche officielle de la Via Allier →
</a>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🚣</span>
<span class="font-serif text-xl font-light">Canoë & kayak sur l'Allier</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>
L'Allier est l'une des dernières rivières sauvages d'Europe et offre des paysages préservés.
Le secteur MonistrolAlleyrasChapeauroux-Langeac est très apprécié pour le kayak et
le canoë.
</p>
<a
href="https://tonic-aventure.fr/"
target="_blank"
rel="noopener noreferrer"
class="inline-block text-xs tracking-[0.15em] uppercase text-primary border-b border-primary/40 hover:border-primary transition-colors"
>
Fédération française de canoë-kayak →
</a>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🚵</span>
<span class="font-serif text-xl font-light">VTT & VTTAE</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>
Plusieurs circuits balisés autour d'Alleyras, Monistrol-d'Allier et Saugues, accessibles à tous les niveaux, permettent de découvrir la vallée de l'Allier et ses gorges.
</p>
<a
href="https://sitesvtt.ffc.fr/sites/velay-volcanique-entre-loire-et-allier-6/"
target="_blank"
rel="noopener noreferrer"
class="inline-block text-xs tracking-[0.15em] uppercase text-primary border-b border-primary/40 hover:border-primary transition-colors"
>
Circuits VTT du territoire →
</a>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🎣</span>
<span class="font-serif text-xl font-light">Pêche à la mouche</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed text-sm">
<p>
L'Allier est réputé pour la truite fario et le saumon atlantique sauvage.
Un terrain de jeu rare, directement accessible depuis la terrasse.
</p>
</div>
</details>
<details class="group border-b border-secondary/100">
<summary class="flex items-center justify-between py-2 cursor-pointer list-none">
<div class="flex items-center gap-4">
<span class="text-5xl">🚂</span>
<span class="font-serif text-xl font-light">Le Cévenol</span>
</div>
<span class="text-primary text-2xl transition-transform duration-300 group-open:rotate-45">+</span>
</summary>
<div class="pb-6 text-dark/70 leading-relaxed space-y-3 text-sm">
<p>
La ligne Paris Clermont-Ferrand Nîmes Marseille, dite « le Cévenol »,
est un chemin de fer emblématique et historique qui traverse les gorges.
Elle dessert Alleyras : venez sans voiture, partez randonner plusieurs jours
et rentrez en train — une combinaison rare et précieuse.
</p>
<a
href="https://www.sncf-connect.com/"
target="_blank"
rel="noopener noreferrer"
class="inline-block text-xs tracking-[0.15em] uppercase text-primary border-b border-primary/40 hover:border-primary transition-colors"
>
Horaires SNCF →
</a>
</div>
</details>
</div>
<!-- Carte + accès -->
<div class="space-y-6">
<div class="h-[420px] rounded-md overflow-hidden shadow-lg border border-secondary">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d440.97773965266185!2d3.6727355223841234!3d44.919259000000004!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x12b3549765f98705%3A0x8bf01e8fb12db201!2s3%20Imp.%20du%20Moulin%2C%2043580%20Alleyras!5e1!3m2!1sfr!2sfr!4v1780157726814!5m2!1sfr!2sfr"
width="100%"
height="100%"
style="border:0;"
allowfullscreen=""
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
></iframe>
</div>
<div class="text-base text-dark/80 space-y-2">
<div class="flex items-center gap-3">
<Car color="green"/>
<span>3 impasse du Moulin, 43580 Alleyras</span>
</div>
<div class="flex items-center gap-3">
<Clock color="green"/>
<span>À 30 min du Puy-en-Velay · 40 min de Langeac</span>
</div>
<div class="flex items-center gap-3">
<Train color="green"/>
<span>Gare d'Alleyras · Ligne des Cévennes</span>
</div>
</div>
</div>
</div>
</div>
</section>
+117
View File
@@ -0,0 +1,117 @@
---
const bonuses = [
{
emoji: "🧘",
title: "Yoga",
description: "Découvrez le yoga avec une enseignante diplômée et passionnée — 25 ans de pratique à votre service.",
},
{
emoji: "🌿",
title: "Plantes comestibles",
description: "Partez à la découverte des plantes sauvages et comestibles qui fleurissent autour du domaine.",
},
{
emoji: "🫧",
title: "Massages ayurvédiques",
description: "Offrez-vous un soin profond et revitalisant avec des massages ayurvédiques traditionnels.",
},
{
emoji: "🥾",
title: "Randonnée guidée",
description: "Suivez les pas d'une amoureuse des sentiers d'ici, qui vous révèlera les secrets des chemins locaux.",
},
{
emoji: "🎲",
title: "Jeux & bibliothèque",
description: "Une belle sélection de jeux de société et une bibliothèque accueillante pour les soirées douces.",
},
];
---
<details class="group bonus-details">
<!-- Ruban cliquable -->
<summary class="list-none cursor-pointer select-none">
<div class="bonus-ribbon relative flex items-center justify-center py-3 bg-gradient-to-r from-[#f5ede0] via-[#ead5b5] to-[#f5ede0] border-y border-[#d4a574]/40 hover:via-[#e0c49a] transition-colors duration-300">
<!-- Tirets décoratifs gauche -->
<div class="bonus-ribbon-content flex flex-row items-center gap-5">
<span class="text-5xl">✨</span>
<div class="flex flex-col items-center gap-0">
<span class="font-serif font-medium text-md tracking-[0.3em] uppercase text-[#995f11]">Les petits plus</span>
<!-- + / × -->
<span class="bonus-icon text-[#995f11] text-2xl font-bold leading-none transition-transform duration-300 group-open:rotate-45 inline-block"></span>
</div>
<span class="text-5xl">✨</span>
</div>
<!-- Tirets décoratifs droite -->
<span class="hidden sm:block absolute right-8 top-1/2 -translate-y-1/2 text-[#c4956a]/40 tracking-widest text-xs">· · · · · · · ·</span>
</div>
</summary>
<!-- Contenu avec animation -->
<div class="bonus-body">
<div class="bonus-inner bg-gradient-to-b from-[#fdf6ed] to-[#f5ede0] border-b border-[#d4a574]/30">
<div class="max-w-6xl mx-auto px-6 py-14">
<!-- En-tête -->
<div class="text-center mb-10">
<span class="block text-xs tracking-[0.3em] uppercase text-[#c4956a] mb-4">Selon les saisons & disponibilités</span>
<h2 class="font-display text-5xl text-[#8b6a3e] mb-3">Les surprises de votre séjour</h2>
<p class="text-dark/80 max-w-xl mx-auto text-sm leading-relaxed">
En fonction des emplois du temps et des saisons, votre hôte vous propose quelques pépites pour enrichir votre séjour.
</p>
</div>
<!-- Cartes bonus -->
<div class="flex flex-wrap justify-center gap-5">
{bonuses.map((b) => (
<div class="group/card w-full sm:w-[calc(50%-10px)] lg:w-[calc(33.333%-14px)] bg-white/60 border border-[#d4a574]/30 p-6 flex flex-col items-center text-center hover:bg-white/90 hover:border-[#d4a574]/60 hover:shadow-md transition-all duration-300">
<div class="text-8xl mb-4">{b.emoji}</div>
<h3 class="font-serif text-lg text-dark mb-2">{b.title}</h3>
<p class="text-dark/60 text-sm leading-relaxed">{b.description}</p>
</div>
))}
</div>
<p class="text-center text-[#c4956a]/90 text-xs mt-10 tracking-[0.25em] uppercase">Sur demande · selon disponibilités de votre hôte</p>
</div>
</div>
</div>
</details>
<style>
/* Ouverture du contenu */
.bonus-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
details[open].bonus-details .bonus-body {
grid-template-rows: 1fr;
}
.bonus-inner {
overflow: hidden;
}
/* Battement de coeur sur tout le contenu du bandeau */
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
25% { transform: scale(1.10); }
50% { transform: scale(1); }
75% { transform: scale(1.10); }
100% { transform: scale(1); }
}
.bonus-ribbon-content {
animation: heartbeat 2s ease-in-out infinite;
}
details[open] .bonus-ribbon-content {
animation: none;
}
details[open] .bonus-icon {
transform: rotate(45deg);
}
</style>
+119
View File
@@ -0,0 +1,119 @@
---
import { Phone, Mail} from 'lucide-astro';
---
<section id="contact" class="py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<div class="max-w-6xl mx-auto px-6">
<div class="grid md:grid-cols-2 gap-16" >
<!-- Infos -->
<div>
<span class="block text-xs tracking-[0.3em] uppercase text-primary mb-5">
Contact
</span>
<h2 class="font-display text-5xl md:text-6xl font-light leading-tight mb-8">
Réservez votre séjour
</h2>
<p class="text-dark/80 leading-relaxed mb-5">
Une question ou une demande de réservation ?
N'hésitez pas à nous contacter, nous répondons sous 24h.
</p>
<p class="text-dark/50 text-sm leading-relaxed mb-10">
Si vous souhaitez partager ou découvrir des activités particulières :
cueillette de champignons, randonnée, baignade, yoga… <br/>
Précisez-le dans votre message, je me ferai un plaisir d'en discuter avec vous.
</p>
<div class="space-y-4 text-dark/70">
<div class="flex items-center gap-4">
<span class="text-primary font-serif text-xl">→</span>
<span>07.85.31.59.28</span>
</div>
<div class="flex items-center gap-4">
<span class="text-primary font-serif text-xl">→</span>
<span>contact@la-maison-de-cecile.fr</span>
</div>
</div>
</div>
<!-- Formulaire -->
<div class="flex items-center justify-center">
<form id="contact-form" class="space-y-4 w-full" novalidate>
<!-- Honeypot anti-spam (caché) -->
<input type="text" name="website" class="hidden" tabindex="-1" autocomplete="off" />
<input
type="text"
name="nom"
placeholder="Votre nom"
required
class="w-full bg-white border border-secondary px-4 py-3 text-dark placeholder:text-dark/40 focus:outline-none focus:border-primary transition-colors"
/>
<input
type="email"
name="email"
placeholder="Votre adresse email"
required
class="w-full bg-white border border-secondary px-4 py-3 text-dark placeholder:text-dark/40 focus:outline-none focus:border-primary transition-colors"
/>
<textarea
name="message"
rows="5"
placeholder="Votre message (dates souhaitées, nombre de personnes, activités souhaitées...)"
required
class="w-full bg-white border border-secondary px-4 py-3 text-dark placeholder:text-dark/40 focus:outline-none focus:border-primary transition-colors resize-none"
></textarea>
<!-- Message de retour -->
<p id="form-feedback" class="hidden text-sm px-1"></p>
<button
id="form-submit"
type="submit"
class="w-full bg-primary text-white py-4 text-xs tracking-[0.25em] border border-secondary rounded-md shadow-lg uppercase hover:bg-primary/80 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
Envoyer le message
</button>
</form>
</div>
</div>
</div>
</section>
<script>
const form = document.getElementById('contact-form') as HTMLFormElement;
const feedback = document.getElementById('form-feedback') as HTMLParagraphElement;
const submit = document.getElementById('form-submit') as HTMLButtonElement;
form.addEventListener('submit', async (e) => {
e.preventDefault();
submit.disabled = true;
submit.textContent = 'Envoi en cours…';
feedback.className = 'hidden text-sm px-1';
try {
const res = await fetch('/contact.php', {
method: 'POST',
body: new FormData(form),
});
const data = await res.json();
feedback.textContent = data.message;
feedback.classList.remove('hidden');
if (data.success) {
feedback.classList.add('text-green-700');
form.reset();
} else {
feedback.classList.add('text-red-600');
}
} catch {
feedback.textContent = 'Erreur réseau. Veuillez réessayer.';
feedback.classList.remove('hidden');
feedback.classList.add('text-red-600');
} finally {
submit.disabled = false;
submit.textContent = 'Envoyer le message';
}
});
</script>
+44
View File
@@ -0,0 +1,44 @@
---
import { Image } from 'astro:assets';
import food from '../assets/images/food.png';
---
<section id="food" class="py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<div class="max-w-6xl mx-auto px-6">
<div class="grid md:grid-cols-2 gap-16 items-center">
<!-- Image -->
<div class="relative order-2 md:order-1 shadow-lg">
<Image
src={food}
alt="Les petits plats de Cécile, une cuisine inspirée par de nombreux voyages, les saisons et les produits du marché."
class="w-full h-[500px] object-cover rounded-sm"
/>
<div class="absolute -top-5 -right-5 w-40 h-40 border border-primary/25 -z-10"></div>
</div>
<!-- Texte -->
<div class="order-1 md:order-2">
<span class="block text-xs tracking-[0.3em] uppercase text-primary mb-5">
La table
</span>
<h2 class="font-display text-5xl md:text-6xl font-light leading-tight mb-8">
Cuisine du marché<br/>et du jardin
</h2>
<p class="text-dark/80 leading-relaxed mb-6">
Le matin, le copieux petit-déjeuner est fait maison : pain, yaourt, confitures,
fruits de saison, infusion du jardin.
</p>
<p class="text-dark/80 leading-relaxed mb-8">
Le soir, Cécile vous propose un repas partagé selon ce qu'elle
a trouvé au marché ou cueillit dans les bois alentour, inspirés par ses voyages.
Simple, vivant, sincère.
</p>
<p class="text-dark/50 text-sm">
Sur réservation · Végétarien et sans gluten possibles · Cuisine ayurvedique sur demande
</p>
</div>
</div>
</div>
</section>
+7
View File
@@ -0,0 +1,7 @@
<footer class="bg-dark py-10">
<div class="max-w-6xl mx-auto px-6 flex flex-col md:flex-row items-center justify-between gap-4">
<span class="font-serif text-xl text-white/70">La Maison de Cécile</span>
<span class="text-white/30 text-sm">© 2026 · Tous droits réservés</span>
<span class="text-white/40 text-sm">Soyez gentil avec ma maman ❤️</span>
</div>
</footer>
+154
View File
@@ -0,0 +1,154 @@
---
import { Image } from 'astro:assets';
import salon1 from '../assets/images/salon_1.jpg';
import salon2 from '../assets/images/salon_2.jpg';
import chambreRiviere1 from '../assets/images/chambre_riviere_1.jpg';
import chambreTilleul from '../assets/images/chambre_tilleul.jpg';
import petiteChambre from '../assets/images/petite_chambre.jpg';
import grenier from '../assets/images/grenier.jpg';
import cecile from '../assets/images/cecile.jpg';
import hamac from '../assets/images/hamac.jpg';
import terrasse from '../assets/images/terrasse.jpg';
import jardin from '../assets/images/jardin.jpg';
import riviere from '../assets/images/riviere.jpg';
import prairie from '../assets/images/prairie.jpg';
// Pour ajouter une photo : importer l'image et ajouter une ligne ici. C'est tout.
const photos = [
{ src: salon1, alt: 'Le salon', caption: 'Le salon' },
{ src: chambreRiviere1, alt: 'Chambre Rivière', caption: 'Chambre Rivière · 1 lit 140×190' },
{ src: terrasse, alt: 'La terrasse', caption: 'La terrasse' },
{ src: salon2, alt: 'Le salon', caption: 'Le salon' },
{ src: grenier, alt: 'Le grenier', caption: 'Le grenier' },
{ src: jardin, alt: 'Le jardin', caption: 'Le jardin' },
{ src: chambreTilleul, alt: 'Chambre Tilleul', caption: 'Chambre Tilleul · 1 lit 140×190' },
{ src: riviere, alt: "L'Allier", caption: "L'Allier, à 100m" },
{ src: petiteChambre, alt: 'La petite chambre', caption: 'La petite chambre · 1 lit 90×190' },
{ src: hamac, alt: 'Espace détente', caption: 'Espace détente · Hamac' },
{ src: cecile, alt: 'Cécile', caption: 'Cécile sur son fauteuil, scrutant la nature…' },
];
---
<section id="gallery" class="py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<div class="max-w-6xl mx-auto px-6">
<div class="mb-14 text-center">
<span class="block text-md tracking-[0.3em] text-primary mb-4 font-display">Galerie</span>
<h2 class="font-display text-5xl font-light">Découvrez le lieu</h2>
</div>
<!-- CSS columns : les photos gardent leurs proportions naturelles,
s'empilent automatiquement, aucun calcul de span nécessaire. -->
<div class="columns-2 md:columns-3 gap-2">
{photos.map((photo, i) => (
<div
class="break-inside-avoid mb-3 cursor-pointer group relative overflow-hidden rounded-sm shadow-lg border border-secondary"
data-lightbox={i}
data-caption={photo.caption}
role="button"
tabindex="0"
aria-label={`Voir en grand : ${photo.alt}`}
>
<Image
src={photo.src}
alt={photo.alt}
class="w-full h-auto block group-hover:scale-105 transition-transform duration-700 pointer-events-none"
/>
<div class="absolute inset-0 bg-dark/0 group-hover:bg-dark/30 transition-colors duration-300 flex items-end">
<span class="translate-y-2 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition-all duration-300 text-white text-xs tracking-[0.15em] uppercase px-4 pb-4">
{photo.caption}
</span>
</div>
</div>
))}
</div>
</div>
</section>
<!-- Lightbox -->
<div
id="lightbox"
class="fixed inset-0 z-50 bg-black/90 items-center justify-center"
style="display:none"
aria-hidden="true"
role="dialog"
aria-modal="true"
aria-label="Visionneuse de photos"
>
<button
id="lb-close"
class="absolute top-5 right-6 text-white/60 hover:text-white text-4xl font-light leading-none transition-colors"
aria-label="Fermer"
>×</button>
<button
id="lb-prev"
class="absolute left-4 md:left-8 top-1/2 -translate-y-1/2 text-white/60 hover:text-white text-6xl font-light leading-none transition-colors px-2"
aria-label="Photo précédente"
></button>
<div class="flex flex-col items-center max-w-5xl w-full mx-auto px-20">
<img id="lb-img" src="" alt="" class="max-h-[80vh] max-w-full object-contain" />
<p id="lb-caption" class="mt-5 text-white/50 text-xs tracking-[0.25em] uppercase"></p>
</div>
<button
id="lb-next"
class="absolute right-4 md:right-8 top-1/2 -translate-y-1/2 text-white/60 hover:text-white text-6xl font-light leading-none transition-colors px-2"
aria-label="Photo suivante"
></button>
</div>
<script>
var lightbox = document.getElementById('lightbox');
var lbImg = document.getElementById('lb-img');
var lbCap = document.getElementById('lb-caption');
var lbClose = document.getElementById('lb-close');
var lbPrev = document.getElementById('lb-prev');
var lbNext = document.getElementById('lb-next');
var items = Array.from(document.querySelectorAll('[data-lightbox]'));
var current = 0;
function showAt(i) {
current = ((i % items.length) + items.length) % items.length;
var wrapper = items[current];
var img = wrapper.querySelector('img');
lbImg.src = img.currentSrc || img.src;
lbImg.alt = img.alt;
lbCap.textContent = wrapper.dataset.caption || '';
}
function openAt(i) {
showAt(i);
lightbox.style.display = 'flex';
lightbox.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden';
lbClose.focus();
}
function closeLightbox() {
lightbox.style.display = 'none';
lightbox.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
}
items.forEach(function(el, i) {
el.addEventListener('click', function() { openAt(i); });
el.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openAt(i); }
});
});
lbClose.addEventListener('click', closeLightbox);
lbPrev.addEventListener('click', function() { showAt(current - 1); });
lbNext.addEventListener('click', function() { showAt(current + 1); });
lightbox.addEventListener('click', function(e) { if (e.target === lightbox) closeLightbox(); });
document.addEventListener('keydown', function(e) {
if (lightbox.style.display === 'none') return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowLeft') showAt(current - 1);
if (e.key === 'ArrowRight') showAt(current + 1);
});
</script>
+53
View File
@@ -0,0 +1,53 @@
---
import { Image } from 'astro:assets';
import alleyras from '../assets/images/alleyras.png';
---
<section class="relative h-screen flex items-center justify-center overflow-hidden">
<!-- Image de fond -->
<div class="absolute inset-0">
<Image
src={alleyras}
alt="La Maison de Cécile"
class="w-full h-full object-cover"
loading="eager"
/>
<div class="absolute inset-0 bg-dark/50"></div>
</div>
<!-- Contenu centré -->
<div class="relative z-10 text-center text-white px-6 max-w-4xl mx-auto">
<span class="block text-sm font-medium text-white/80 tracking-[0.1em] uppercase mb-6">
Gite et Chambres d'hôtes · Gorges du Haut-Allier<br/>Pont d'Alleyras, Haute-Loire
</span>
<h1 class="font-display text-7xl md:text-9xl font-light leading-none mb-6">
La Maison <br/> de Cécile
</h1>
<p class="text-xl font-medium text-white/80 mb-12 max-w-lg mx-auto">
Une halte authentique sur la Via Allier & le GR470,<br/> sur les rives de l'Allier sauvage.
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a
href="#about"
class="border border-white/80 text-white px-10 py-4 text-xs tracking-[0.25em] uppercase hover:bg-white/10 transition-colors"
>
Découvrir la maison
</a>
<a
href="#activities"
class="border border-white/80 text-white/80 px-10 py-4 text-xs tracking-[0.25em] uppercase hover:text-white transition-colors"
>
Les activités
</a>
</div>
</div>
<!-- Indicateur de défilement -->
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 text-white/80">
<span class="text-[12px] tracking-widest uppercase">Défiler</span>
<div class="w-px h-8 bg-white/80"></div>
</div>
</section>
+18
View File
@@ -0,0 +1,18 @@
<header class="fixed top-0 left-0 right-0 z-50 bg-cream/95 backdrop-blur-sm border-b border-secondary/60 shadow-md">
<div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
<a href="#" class="font-display text-4xl text-dark">
La Maison de Cécile
</a>
<nav class="hidden md:flex items-center gap-8">
<a href="#about" class="text-xs tracking-[0.2em] uppercase text-dark/60 hover:text-primary transition-colors">La maison</a>
<a href="#gallery" class="text-xs tracking-[0.2em] uppercase text-dark/60 hover:text-primary transition-colors">Galerie</a>
<a href="#food" class="text-xs tracking-[0.2em] uppercase text-dark/60 hover:text-primary transition-colors">La table</a>
<a href="#activities" class="text-xs tracking-[0.2em] uppercase text-dark/60 hover:text-primary transition-colors">Activités</a>
<a href="#pricing" class="text-xs tracking-[0.2em] uppercase text-dark/60 hover:text-primary transition-colors">Tarifs</a>
<a href="#contact" class="text-xs tracking-[0.2em] uppercase bg-primary border border-secondary rounded-md text-white px-6 py-2.5 hover:bg-primary/80 transition-colors">
Me contacter
</a>
</nav>
</div>
</header>
+118
View File
@@ -0,0 +1,118 @@
---
import constructionMedium from '../assets/images/construction_medium.png';
import constructionSmall from '../assets/images/construction_small.png';
---
<section id="pricing" class="relative py-16 scroll-mt-20 bg-gradient-to-b from-[#f7f3e8] via-[#f2efdf] to-[#e4ecd2] border-y border-[#d9d2c3]/70">
<!-- Filtre "en construction" -->
<div class="absolute inset-0 z-10 pointer-events-none">
<!-- Mobile -->
<img src={constructionSmall.src} alt="En construction" class="sm:hidden absolute inset-0 w-full h-full object-cover object-top" />
<!-- Desktop -->
<img src={constructionMedium.src} alt="En construction" class="hidden sm:block absolute inset-0 w-full h-full object-cover object-center" />
<div class="absolute inset-0 bg-black/30"></div>
</div>
<div class="max-w-6xl mx-auto px-6">
<!-- En-tête -->
<div class="text-center mb-16">
<span class="block text-xs tracking-[0.3em] uppercase text-primary mb-5">
Les tarifs
</span>
<h2 class="font-display text-5xl md:text-6xl font-light leading-tight mb-6">
Mes formules
</h2>
<p class="text-dark/80 max-w-4xl mx-auto leading-relaxed">
Je vous accueille dans une maison joliement restaurée et chaleureuse, pour un séjour ressourçant en pleine nature. <br/>
Deux formules d'hébergement s'offrent à vous : la location complète du gîte, ou la réservation d'une chambre d'hôtes.
</p>
</div>
<!-- Deux colonnes -->
<div class="grid md:grid-cols-2 gap-8 mb-12">
<!-- Gîte -->
<div class="border border-secondary bg-white shadow-lg">
<div class="px-8 pt-8 pb-6 border-b border-secondary">
<span class="block text-xs tracking-[0.2em] uppercase text-primary mb-2">Location complète</span>
<h3 class="font-serif text-3xl font-light text-dark">Gîte</h3>
<p class="text-dark/50 text-sm mt-2">Privatisation de la maison entière · jusqu'à 9 personnes</p>
</div>
<div class="px-8 py-6">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-secondary">
<th class="text-left font-normal text-dark/40 tracking-[0.15em] uppercase text-xs pb-3">Formule</th>
<th class="text-right font-normal text-dark/40 tracking-[0.15em] uppercase text-xs pb-3">Tarif / nuit</th>
</tr>
</thead>
<tbody class="divide-y divide-secondary/60">
<tr>
<td class="py-4 text-dark/80">Nuit seule</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Avec petit-déjeuner</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Avec dîner</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Demi-pension <span class="text-dark/40 text-xs">(dîner + pdj)</span></td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Chambre d'hôtes -->
<div class="border border-secondary bg-white shadow-lg">
<div class="px-8 pt-8 pb-6 border-b border-secondary">
<span class="block text-xs tracking-[0.2em] uppercase text-primary mb-2">Par chambre</span>
<h3 class="font-serif text-3xl font-light text-dark">Chambre d'hôtes</h3>
<p class="text-dark/50 text-sm mt-2">Chambre double ou dortoir · par nuit</p>
</div>
<div class="px-8 py-6">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-secondary">
<th class="text-left font-normal text-dark/40 tracking-[0.15em] uppercase text-xs pb-3">Formule</th>
<th class="text-right font-normal text-dark/40 tracking-[0.15em] uppercase text-xs pb-3">Tarif / nuit</th>
</tr>
</thead>
<tbody class="divide-y divide-secondary/60">
<tr>
<td class="py-4 text-dark/80">Nuit seule</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Avec petit-déjeuner</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Avec dîner</td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
<tr>
<td class="py-4 text-dark/80">Demi-pension <span class="text-dark/40 text-xs">(dîner + pdj)</span></td>
<td class="py-4 text-right font-serif text-lg text-dark">— €</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Note de bas de section -->
<p class="text-center text-dark/40 text-xs tracking-[0.2em] uppercase">
Tarifs indicatifs · me contacter pour toute demande spécifique
</p>
</div>
</section>
+41
View File
@@ -0,0 +1,41 @@
---
import '../styles/global.css';
interface Props {
title?: string;
}
const {
title = 'La Maison de Cécile'
} = Astro.props;
---
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<title>{title}</title>
<meta
name="description"
content="Cécile vous accueille dans sa charmante maison située à Pont d'Alleyras, sur les rives de l'Allier sauvage au sud de la Haute-Loire.
Gîte et chambres d'hôtes, pour un séjour en pleine nature ou une escale d'une nuit sur la Via Allier & le GR470."
/>
<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=Parisienne&family=Marcellus&family=Source+Sans+3:wght@300;400;500;600&display=swap"
rel="stylesheet"
>
</head>
<body class="bg-cream text-dark font-sans antialiased">
<slot />
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
---
import MainLayout from '../layouts/MainLayout.astro';
import Navbar from '../components/Navbar.astro';
import Hero from '../components/Hero.astro';
import About from '../components/About.astro';
import Gallery from '../components/Gallery.astro';
import Food from '../components/Food.astro';
import Pricing from '../components/Pricing.astro';
import Activities from '../components/Activities.astro';
import Bonus from '../components/Bonus.astro';
import Contact from '../components/Contact.astro';
import Footer from '../components/Footer.astro';
---
<MainLayout>
<Navbar />
<Hero />
<main>
<About />
<Gallery />
<Food />
<Pricing />
<Activities />
<Bonus />
<Contact />
</main>
<Footer />
</MainLayout>
+15
View File
@@ -0,0 +1,15 @@
@import "tailwindcss";
@theme {
--color-primary: #8b8d62;
--color-secondary: #d9d2c3;
--color-cream: #f5f2eb;
--color-dark: #1f1f1f;
--font-display: 'Parisienne', cursive;
--font-serif: 'Marcellus', serif;
--font-sans: 'Cormorant Garamond', sans-serif;
}
html {
scroll-behavior: smooth;
}
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}