Compare commits

...

10 Commits

Author SHA1 Message Date
sucupira 542323ceaa 🐛 Fix: Corrige le déploiement GitHub Pages (chemins sous /gwada-sirius/)
Deploy to GitHub Pages / build (push) Failing after 25s
Deploy to GitHub Pages / deploy (push) Has been skipped
Le site était bien déployé mais tous les chemins absolus (CSS/JS bundlés
par Vite, polices, favicon, manifest, service worker, liens de nav/footer)
pointaient vers la racine du domaine (/assets/..., /favicon.svg, /, /en/…)
au lieu de /gwada-sirius/... — GitHub Pages sert un site de projet sous
/<nom-du-repo>/, pas à la racine. Résultat : CSS et polices en 404, liens
de navigation cassés.

Ajoute une variable d'env BASE_PATH (vide par défaut, /gwada-sirius en CI
via .github/workflows/deploy.yml) : passée à viteOptions.base pour que
Vite préfixe lui-même les assets qu'il bundle, et à un filtre maison
withBase/localeHref pour les liens de nav, favicon, manifest et
l'enregistrement du service worker. Le manifest PWA passe en chemins
relatifs pour ne pas avoir besoin d'être templaté.

Vérifié en simulant un vrai déploiement (build préfixé servi depuis un
sous-dossier /gwada-sirius/ d'un serveur statique) : plus aucune requête
404, navigation, service worker (scope correctement limité au
sous-chemin) et carte Leaflet fonctionnels de bout en bout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 21:11:51 -04:00
sucupira 6a80cbc34f 🎨 Design: Raffine les tokens (hiérarchie de texte, lavis dorés, lueurs)
Suite à l'analyse du vrai CSS d'unabyss.com (root tokens complets) : leur
système est bien plus granulaire que le nôtre (8 niveaux de texte, opacités
pré-mélangées en tokens, vocabulaire de lueur dorée à plusieurs usages).
Emprunt de la STRUCTURE, pas des valeurs :

- --ink-dim / --ink-ghost : deux niveaux de texte supplémentaires pour les
  métadonnées (coordonnées GPS) et le très discret, sans casser les 2
  niveaux existants (--ink / --ink-soft)
- --gold-wash-subtle / --gold-wash : fonds teintés or pré-mélangés,
  remplacent les oklch(...) ad hoc dispersés dans les composants
- --glow-soft / --glow-strong : vocabulaire de lueur distinct d'une ombre
  générique — douce sur le titre du hero et la date du prochain lever,
  forte sur l'onglet actif (Science, Dogon)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:55:50 -04:00
sucupira 217f1d9691 Design: Anneau comète doré sur le CTA principal (inspiré d'unabyss.com)
Après inspection réelle du code d'unabyss.com (HTML/CSS servis, pas juste
une description générée) : anneau tournant en conic-gradient CSS pur,
masqué en anneau via mask-composite:exclude — aucun JS/WebGL. Reproduit à
l'identique dans la palette or/corail du site, appliqué au CTA principal
du hero ("Découvrir les prédictions") pour renforcer l'action unique mise
en avant (Von Restorff). Fige automatiquement sous prefers-reduced-motion
via le kill-switch déjà présent dans tokens.css.

Correction au passage : la première analyse d'unabyss (WebFetch, texte
seul) avait spéculé sur des animations "probables" sans les avoir
observées — un fetch réel du HTML/CSS a permis d'identifier précisément
les techniques utilisées (comet-ring, scroll-reveal, marquee CSS,
glassmorphism) plutôt que d'improviser.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:41:57 -04:00
sucupira 49fdb7fae4 🎨 Design: Direction visuelle inspirée d'unabyss.com
Refonte de la direction artistique en gardant Fraunces (serif) comme
signature éditoriale, mais en empruntant à unabyss.com : fond de nuit
presque neutre ("premium near-black" plutôt que bleu nuit saturé), accent
corail/orange brûlé en complément de l'or de Sirius, typographie plus
confiante (échelle et tracking resserrés sur les grands titres), rythme
d'espacement plus généreux entre sections.

Déboîte le contenu purement éditorial (cartes de fonctionnalités, cartes
de culture, conseils d'observation) qui n'a plus de bordure/fond — plus
fluide, plus proche de la sobriété d'unabyss. Les regroupements réellement
fonctionnels (calculateur, équipement, onglets, formules) gardent .card.

Ajoute une vitrine de badges (13 villes de la vague planétaire) sur la
page d'accueil — écho panafricain à la grille d'intégrations colorée
d'unabyss, sans rien copier de son contenu. Les données de villes sont
déplacées vers src/_data/waveCities.js (source unique pour les pages 11ty
et pour l'îlot SiriusGlobe).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:33:29 -04:00
sucupira 142bc0c9df 🗺️ Fix: Convertit aussi le globe (vague planétaire) en Leaflet
Même problème que la carte d'observation : le globe des 13 villes de la
diaspora dessinait des continents à main levée sur un canvas vide. Remplacé
par la même carte Leaflet + OSM, marqueurs colorés (or pour la Caraïbe,
turquoise ailleurs), popup nom + date au clic. Le chunk Leaflet est partagé
entre les deux îlots (un seul téléchargement).

Supprime les polygones de continents simplifiés devenus inutiles
(src/islands/globe/world.js) et la classe CSS .island-canvas orpheline.
Corrige aussi le texte d'aide ("glisser pour tourner" → "glisser pour
déplacer", plus juste pour une carte plate que pour un globe 3D).

Bump CACHE_NAME du service worker (v1 → v2) : découvert pendant le debug
que la stratégie stale-while-revalidate servait l'ancien composant aux
visiteurs déjà passés sur le site malgré le nouveau déploiement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:15:10 -04:00
sucupira 4af585d778 🗺️ Fix: Remplace la carte canvas schématique par Leaflet + OSM
La carte d'observation ne montrait qu'un nuage de points sur un canvas
vide, sans littoral ni repère géographique — d'où une impression de bug.
Remplacée par une vraie carte Leaflet (fond OpenStreetMap réel), utilisé
directement plutôt que via un wrapper Svelte : Sveaflet (publié hier,
ciblé sur une prérelease de Svelte 5) et svelte-openlayers (mature mais
embarque OpenLayers, disproportionné pour 7 marqueurs) ont été évalués et
écartés au profit de Leaflet nu, plus mature et plus léger.

Ajoute une config de preview secondaire (port 8099) dans .claude/launch.json
pour vérifier le site sans interrompre un serveur de dev déjà lancé.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:09:05 -04:00
sucupira ec1528ed06 🏗️ Refonte v2 : migration 11ty + îlots Svelte 5
Remplace la SPA Vite/Svelte 4 par un site 11ty statique (contenu/HTML/CSS
fonctionnels sans JS) avec Svelte 5 en îlots pour les 3 seuls widgets
interactifs (calculateur de prédictions, carte d'observation, globe de la
vague planétaire). Conforme à la doctrine UI/UX OKI : nav ramenée à 5
entrées, design system CSS natif (tokens oklch, cascade @layer, light-dark),
i18n compilée via Paraglide (FR/EN/HT, routée par URL), onglets 100% CSS,
polices auto-hébergées, service worker minimal.

Les dates de prédiction sont recoupées avec la méthode Jean Meeus / Jeffrey
L. Hunt (whenthecurveslineup.com), consolidées sur un seul jeu de 7 sites,
étendues jusqu'en 2032, et le "22 juillet 2025" figé en dur est remplacé par
un calcul dynamique du prochain lever au build.

Supprime le code mort (3 variantes de globe, fichier vide, prototype HTML
legacy) et animejs, devenu inutile une fois les animations portées en CSS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 19:45:33 -04:00
sucupira d68cc17042 Feature: Système solaire animé dans Hero + Nettoyage patterns
- Remplacement du pattern .pattern-adinkra par système solaire complet
- Système solaire positionné à gauche (10%) pour éviter conflit avec texte
- 6 orbites : 5 planètes + Sirius avec couleurs et tailles réalistes
- Animations CSS fluides : rotation orbites, pulsation soleil, scintillement Sirius
- Suppression patterns géométriques mobiles (kente, bogolan) et animations JS
- Conservation étoiles, étoiles filantes et arrière-plan cosmique
- Interface épurée focalisée sur éléments astronomiques

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-21 02:15:01 -04:00
sucupira 0434b1fce6 🌍 Feature: Traductions multilingues section Associations
- Traductions complètes en 3 langues (FR/EN/HT) pour la section Associations
- Intégration des traductions dans le composant GlobeGeoJSON.svelte
- Traductions fidèles des descriptions officielles des événements
- Support multilingue pour titres, descriptions et liens communauté
- Maintien de la cohérence terminologique (Kamit/Kamite, Wep Ronpet)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-21 01:17:36 -04:00
sucupira cb1bf19bd4 Feature: Section Associations Wep Ronpet + Navigation
- Nouvelle section "Associations qui fêtent le Wep Ronpet" après le globe
- 2 événements Weezevent avec descriptions officielles détaillées
- Liens communauté : 14ème Membre + Page Facebook MUN
- Vidéo YouTube intégrée de façon responsive
- Ajout menu navigation "Associations" avec traductions (FR/EN/HT)
- Design cohérent avec animations hover et style doré
- Correction gratuité : "GRATUIT - de 16 ans" pour Afrocentricity

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-21 01:14:14 -04:00
86 changed files with 5828 additions and 7954 deletions
+23
View File
@@ -0,0 +1,23 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "gwada-sirius-dev",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"port": 8080
},
{
"name": "gwada-sirius-preview",
"runtimeExecutable": "npx",
"runtimeArgs": ["eleventy", "--serve", "--port=8099"],
"port": 8099
},
{
"name": "gwada-sirius-ghpages-sim",
"runtimeExecutable": "npx",
"runtimeArgs": ["serve", "/tmp/gh-pages-sim", "-l", "8097"],
"port": 8097
}
]
}
+3 -1
View File
@@ -31,11 +31,13 @@ jobs:
- name: Build
run: npm run build
env:
BASE_PATH: /${{ github.event.repository.name }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
path: ./_site
deploy:
environment:
+7
View File
@@ -2,6 +2,9 @@
dist/
build/
.svelte-kit/
_site/
.11ty-vite/
src/paraglide/
# Dépendances
node_modules/
@@ -27,6 +30,10 @@ Thumbs.db
*.swp
*.swo
# Claude Code (local uniquement)
.claude/settings.local.json
.claude/scheduled_tasks.lock
# Temporaires
*.tmp
*.temp
+13
View File
@@ -1,5 +1,18 @@
# Changelog
## [2.0.0] - 2026-07-06
### 🏗️ Refonte complète : Vite/Svelte 4 SPA → 11ty + îlots Svelte 5
- **Migration du build** : remplacement de la SPA Vite par un site 11ty statique ; Svelte 5 n'hydrate plus que 3 widgets réellement interactifs (calculateur de prédictions, carte d'observation, globe de la vague planétaire). Tout le reste (Science, Culture, Dogon, Observatoire, Associations, navigation) est du HTML/CSS pur, fonctionnel sans JavaScript.
- **Navigation** : passage de 7 entrées (dont Dogon et Calculs absents du menu) à 5 entrées consolidées (Accueil, Sirius & Science, Culture & Savoirs, Observer, Associations), conformément à la loi de Hick-Hyman.
- **i18n** : remplacement du store Svelte custom par Paraglide (inlang), i18n compilée et routée par URL (`/`, `/en/`, `/ht/`), avec repli automatique vers le français pour les traductions manquantes.
- **Design system** : nouveaux tokens CSS en `oklch()` (contraste AA/AAA calculé, pas décoré après coup), cascade `@layer` explicite, `light-dark()` pour un vrai mode clair/sombre (le site n'avait qu'un thème sombre fixe), motifs panafricains (kente/bogolan/adinkra) resserrés en accents ponctuels plutôt qu'en texture de fond généralisée.
- **Typographie** : polices auto-hébergées (Fraunces, Atkinson Hyperlegible) en remplacement de Google Fonts.
- **Performance** : suppression totale d'animejs (toutes les animations d'entrée/interaction sont maintenant en CSS ou omises) ; onglets (Science, Dogon) réimplémentés en CSS pur (`input[type=radio]`), zéro JS.
- **Données astronomiques** : consolidation sur un unique jeu de 7 sites d'observation (l'ancien composant Predictions utilisait des données différentes, moins précises, du jeu de données `predictions.js` jamais branché) ; méthodologie de calcul explicitée et sourcée (Jean Meeus, Jeffrey L. Hunt) ; prédictions étendues jusqu'en 2032 ; date figée "22 juillet 2025" remplacée par un calcul dynamique du prochain lever au build.
- **Nettoyage** : suppression de 3 variantes de globe mortes (`Globe.svelte`, `GlobeThreeJS.svelte`, `GlobeOpenGlobus.svelte.bak`), d'un fichier vide (`og.umd.js`) et du prototype HTML pré-Svelte jamais référencé.
## [1.5.0] - 2024-01-07
### 🚀 Nouvelles fonctionnalités
+23 -97
View File
@@ -2,116 +2,42 @@
## 📋 Vue d'ensemble
J'ai créé une application web one-page avec Svelte qui combine les 3 pages HTML que vous aviez et enrichit le contenu avec les informations du PDF. L'application est moderne, mobile-first, avec un design panafricain/caribéen.
Refonte complète (v2) : passage d'une SPA Vite+Svelte 4 à un site **11ty** statique avec **Svelte 5 en îlots**, un design system CSS natif (oklch, `@layer`, container queries) et une i18n compilée (Paraglide/inlang). Conforme à la doctrine UI/UX OKI : nav à 5 entrées, fonctionne sans JavaScript pour tout le contenu éditorial, budget JS minimal.
## 🏗️ Structure du projet
## 🏗️ Architecture
```
/home/sucupira/dev/Gwada-Sirius/
├── src/
│ ├── App.svelte # Composant principal
├── app.css # Styles globaux avec patterns africains
│ ├── main.js # Point d'entrée
│ ├── components/ # Composants Svelte
│ │ ├── Navigation.svelte # Menu responsive
│ │ ├── Hero.svelte # Section héro avec animation d'étoiles
│ │ ├── About.svelte # Introduction à Sirius
│ │ ├── Predictions.svelte # Calculateur interactif
│ │ ├── Science.svelte # Explication scientifique
│ │ ├── Culture.svelte # Héritage culturel
│ │ ├── Observatory.svelte # Guide d'observation
│ │ ├── Globe.svelte # Visualisation globe (Canvas 2D)
│ │ └── Footer.svelte # Pied de page
│ └── data/
│ └── predictions.js # Données des prédictions
├── package.json
├── vite.config.js
├── index.html
├── README.md
├── .gitignore
└── start.sh # Script de démarrage rapide
```
- **Contenu** : pages Nunjucks (`src/*.njk`), chacune paginée sur les 3 langues (`fr`/`en`/`ht`) via `pagination.data: locales`.
- **Données** : `src/_data/sites.js` (7 sites d'observation, dates interpolées Jean Meeus / Jeffrey L. Hunt), `src/_data/nav.js`, `src/_data/nextRising.js` (calculé au build).
- **i18n** : catalogues `messages/{fr,en,ht}.json`, compilés par `@inlang/paraglide-js` en `src/paraglide/` avant chaque build (`npm run messages`). `ht` retombe automatiquement sur `fr` pour les clés non traduites (contenu long, cf. portée d'origine).
- **Îlots Svelte 5** (3 seulement, le reste est HTML/CSS pur) :
- `PredictionsCalculator` — hydratation immédiate, améliore le tableau statique des 7 sites
- `ObservationMap` — vraie carte Leaflet + fond OSM, marqueurs/popups pour les 7 sites, hydratation différée (IntersectionObserver). Leaflet est utilisé directement (pas de wrapper Svelte type Sveaflet/svelte-openlayers — évalués mais écartés : le premier cible une prérelease de Svelte 5 et vient d'être publié, le second embarque OpenLayers, bien plus lourd que nécessaire pour 7 marqueurs)
- `SiriusGlobe` — vague planétaire du lever héliaque (13 villes), carte canvas schématique (continents simplifiés), hydratation différée
- **Onglets** (Science, Dogon) : 100 % CSS (`input[type=radio]` + `label`), zéro JS.
- **Build** : `@11ty/eleventy-plugin-vite` fait passer la sortie 11ty par Vite (bundling CSS/JS, compilation Svelte). Un plugin maison recopie les assets passthrough après le build Vite (voir commentaire dans `eleventy.config.js``emptyOutDir` de Vite les efface sinon).
## 🎨 Design et fonctionnalités
## ⚠️ Points d'attention connus
### Palette de couleurs panafricaine :
- **Or (#FFD700)** : Couleur principale, représente Sirius et le soleil
- **Rouge profond (#DC143C)** : Accent
- **Vert forêt (#228B22)** : Nature
- **Bleu nuit (#1a1a2e)** : Fond, ciel nocturne
### Patterns africains intégrés :
- **Kente** : Motif géométrique en diagonale
- **Bogolan** : Cercles et points
- **Adinkra** : Motifs symboliques
### Sections principales :
1. **Hero** : Animation d'étoiles interactive avec effet parallaxe
2. **About** : Introduction avec cartes d'information
3. **Predictions** : Calculateur interactif avec sélecteurs
4. **Science** : Tabs pour explorer la mécanique céleste
5. **Culture** : Timeline et cartes culturelles
6. **Observatory** : Guide pratique étape par étape
7. **Globe** : Visualisation 2D animée (plus léger que Three.js)
## 🚀 Pour démarrer
```bash
cd /home/sucupira/dev/Gwada-Sirius
./start.sh
```
Ou manuellement :
```bash
npm install
npm run dev
```
## 📱 Responsive Design
- Mobile-first approach
- Menu hamburger pour mobile
- Grilles flexibles
- Tailles de police adaptatives avec clamp()
## ⚡ Performance
- Svelte compile en vanilla JS (pas de runtime)
- Canvas 2D au lieu de Three.js pour le globe
- Lazy loading des animations avec IntersectionObserver
- CSS optimisé avec variables custom properties
1. **`<link>` et `vite-ignore`** : Vite traite tout `<link href>` comme une référence d'asset à résoudre, y compris `rel="alternate"`/`rel="icon"`/`rel="preload"`. Un `href="/"` ou `href="/en/"` (URL de répertoire) fait planter le build (`EISDIR`, cf. issue amont). Toujours ajouter `vite-ignore` sur ces `<link>` de métadonnées dans `layouts/base.njk`.
2. **Alias `/src`** : `eleventy-plugin-vite` fait tourner Vite avec pour racine une copie du dossier de **sortie** (`_site`), pas la racine du projet. Toute référence absolue à un fichier source (`/src/styles/app.css`, `/src/islands/.../mount.js`) nécessite l'alias `resolve.alias["/src"]` défini dans `eleventy.config.js`.
3. **Dates ISO sans heure** : `new Date("2026-07-22")` est interprété en UTC ; formaté en heure locale Guadeloupe (UTC-4), ça peut reculer d'un jour. Toujours parser les composants (`year, month, day`) et construire la date en local — voir le filtre `formatDate` et `PredictionsCalculator.svelte`.
4. **Slinkity est abandonné** (son créateur travaille sur Astro) : ne pas l'utiliser pour l'intégration 11ty+Svelte, `eleventy-plugin-vite` (officiel) est la voie robuste.
5. **Service worker et cache obsolète** : la stratégie stale-while-revalidate de `public/sw.js` peut servir une version périmée des îlots (vécu pendant le debug de la carte : un visiteur déjà passé sur le site voyait l'ancien composant malgré un déploiement). **Toujours incrémenter `CACHE_NAME` dans `sw.js`** quand un îlot ou un asset critique change, sinon les visiteurs récurrents restent bloqués sur l'ancienne version jusqu'à revalidation.
6. **GitHub Pages = sous-répertoire, pas la racine du domaine** : `https://cyber-mawonaj.github.io/gwada-sirius/` sert le site sous `/gwada-sirius/`, pas `/`. Tous les chemins absolus (`/assets/...`, `/favicon.svg`, liens de nav, `sw.js`) doivent donc être préfixés. Géré via la variable d'env `BASE_PATH` (vide en local/domaine perso, `/gwada-sirius` en CI — voir `.github/workflows/deploy.yml`) : passée à `viteOptions.base` pour les assets bundlés par Vite, et au filtre maison `withBase`/`localeHref` pour les liens et fichiers passthrough. Le manifest PWA utilise des chemins **relatifs** (`"start_url": "."`) pour ne pas avoir besoin d'être templaté. Pour retester ce scénario en local : `BASE_PATH=/gwada-sirius npm run build`, puis servir `_site` depuis un dossier `gwada-sirius/` à la racine d'un serveur statique.
## 🔄 Améliorations possibles
1. **Ajouter un compte à rebours dynamique** jusqu'au 22 juillet
2. **Intégrer une API météo** pour les prévisions
3. **Ajouter des sons ambiants** africains/caribéens
4. **Mode sombre/clair** automatique selon l'heure
5. **PWA** pour installation mobile
6. **Partage social** avec meta tags optimisés
7. **Multilingue** (Français, Créole, Anglais)
8. **Animations GSAP** pour plus de fluidité
- Traduire le contenu long en Kreyòl (actuellement chrome/labels uniquement, contenu de fond en français)
- Mettre à jour chaque année les informations d'événements associatifs (`associations.njk`, données à vérifier auprès des organisateurs)
- Le chunk Leaflet (~44 Ko gzip) n'est chargé qu'au scroll sur la carte (`observer/`), mais reste le plus lourd du site ; envisager des tuiles vectorielles/un fournisseur plus léger si le budget JS devient un problème
## 📦 Build pour production
```bash
npm run build
# Les fichiers seront dans dist/
# Sortie dans _site/
```
## 🌐 Déploiement
Peut être déployé facilement sur :
- Netlify (drag & drop du dossier dist)
- Vercel
- GitHub Pages
- Surge.sh
## 💡 Points techniques importants
1. **Données astronomiques** : Toutes les prédictions sont stockées dans `src/data/predictions.js`
2. **Animations** : Utilisation de CSS animations et requestAnimationFrame
3. **État** : Gestion simple avec les stores Svelte (pas de store global pour l'instant)
4. **SEO** : Ajouter les meta tags appropriés dans index.html
Le projet est maintenant prêt et fonctionnel ! Vous pouvez le personnaliser selon vos besoins.
GitHub Pages via `.github/workflows/deploy.yml` (upload de `_site/`), déclenché sur push vers `main`.
+38 -48
View File
@@ -1,71 +1,61 @@
# Sirius en Guadeloupe - Gwada-Sirius
# Sirius en Guadeloupe Gwada-Sirius
Un projet web célébrant le lever héliaque de Sirius en Guadeloupe, fusionnant astronomie, culture africaine et héritage caribéen.
Un site célébrant le lever héliaque de Sirius en Guadeloupe (Wep Ronpet), à la croisée de l'astronomie, de l'héritage égyptien et dogon, et de la culture caribéenne.
## 🌟 À propos
Cette application web interactive permet de :
- Calculer les dates précises du lever héliaque de Sirius pour différents lieux en Guadeloupe
- Explorer l'héritage culturel de Sirius à travers les civilisations (Égypte ancienne, Dogons, Caraïbes)
- Comprendre la science astronomique derrière ce phénomène
- Découvrir les meilleurs sites d'observation en Guadeloupe
Ce site permet de :
- Calculer la date précise du lever héliaque pour 7 sites d'observation en Guadeloupe
- Explorer l'héritage culturel de Sirius (Égypte ancienne, Dogon du Mali, Caraïbes)
- Comprendre la science du phénomène (Arcus Visionis, méthode de calcul)
- Préparer une observation (guide pratique, équipement, carte des sites)
- Découvrir les associations qui célèbrent le Wep Ronpet
## 🚀 Technologies utilisées
## 🚀 Stack technique
- **Svelte** - Framework JavaScript réactif
- **Canvas 2D** - Pour les animations et visualisations
- **GeoJSON** - Pour les cartes interactives
- **i18n** - Support multilingue (Français, English, Kreyòl Ayisyen)
- **[11ty (Eleventy)](https://www.11ty.dev/)** — génération du site statique, contenu et navigation en HTML/CSS, fonctionnel sans JavaScript
- **[Svelte 5](https://svelte.dev/)** en îlots — hydratation ciblée pour les 3 seuls widgets interactifs (calculateur de prédictions, carte d'observation, globe de la vague planétaire), via `@11ty/eleventy-plugin-vite`
- **[Paraglide (inlang)](https://paraglidejs.com/)** — i18n compilée, FR (défaut) / EN / Kreyòl Ayisyen, routée par URL (`/`, `/en/`, `/ht/`)
- **CSS natif** — tokens `oklch()`, cascade `@layer`, container queries, aucun framework CSS ni préprocesseur
- Polices auto-hébergées (Fraunces, Atkinson Hyperlegible), zéro dépendance à Google Fonts
Conforme à la doctrine UI/UX OKI (voir document de référence interne) : navigation à 5 entrées, cibles tactiles ≥ 48 px, contraste AA/AAA, `prefers-reduced-motion` respecté, budget JS minimal.
## 📦 Installation
```bash
# Cloner le repository
git clone https://github.com/[votre-username]/gwada-sirius.git
# Entrer dans le dossier
cd gwada-sirius
# Installer les dépendances
npm install
# Lancer le serveur de développement
npm run dev
npm run dev # serveur de développement sur http://localhost:8080
npm run build # build de production dans _site/
npm run preview # prévisualiser le build de production
```
## 🌍 Fonctionnalités
## 🗂️ Structure
- **Calculateur de prédictions** : Dates précises pour 7 sites d'observation
- **Globe interactif** : Visualisation de la trajectoire de Sirius
- **Carte des sites** : Localisation des meilleurs points d'observation
- **Multilingue** : Interface en français, anglais et créole haïtien
- **Responsive** : Optimisé pour mobile et desktop
- **Animations** : Ciel étoilé animé et patterns africains
## 🎨 Design
Le design s'inspire des motifs africains (Kente, Adinkra, Bogolan) avec une palette de couleurs symbolique :
- Or (#FFD700) - Sirius et la lumière divine
- Rouge (#FF6B35) - Force et énergie vitale
- Vert (#4CAF50) - Nature et renouveau
- Bleu nuit (#0A1628) - Ciel nocturne
```
src/
├── _data/ # données globales (sites d'observation, navigation)
├── _includes/ # layout de base, partiels (nav, footer)
├── styles/ # design system CSS (tokens, reset, base, layouts, composants)
├── islands/ # composants Svelte 5 hydratés côté client (prédictions, carte, globe)
├── paraglide/ # messages i18n compilés (généré, ignoré par git)
├── index.njk # accueil
├── sirius-science.njk # astronomie + méthode de calcul
├── culture-savoirs.njk# héritage culturel + savoir dogon
├── observer.njk # calculateur, carte, guide d'observation
└── associations.njk # associations qui fêtent le Wep Ronpet
messages/ # catalogues de traduction (fr/en/ht), source pour Paraglide
project.inlang/ # configuration du projet i18n
```
## 👥 Crédits
Projet réalisé avec ❤️ par :
- [cyber-mawonaj](https://github.com/cyber-mawonaj)
- [OKI](https://o-k-i.net)
Projet réalisé avec ❤️ par [cyber-mawonaj](https://github.com/cyber-mawonaj) et [OKI](https://o-k-i.net).
## 📜 Licence
Ce projet est sous licence MIT - voir le fichier [LICENSE](LICENSE) pour plus de détails.
## 🙏 Remerciements
- Les anciens Égyptiens pour leur sagesse astronomique
- Les Dogons du Mali pour avoir préservé ces connaissances
- La communauté guadeloupéenne pour perpétuer ces traditions
MIT voir [LICENSE](LICENSE).
---
*"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain*
*"Les étoiles sont les ancêtres qui veillent sur nous" — proverbe africain*
+112
View File
@@ -0,0 +1,112 @@
import fs from "node:fs";
import path from "node:path";
import EleventyVitePlugin from "@11ty/eleventy-plugin-vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import * as m from "./src/paraglide/messages.js";
import { locales, baseLocale } from "./src/paraglide/runtime.js";
const OUTPUT_DIR = "_site";
// GitHub Pages project sites are served under /<repo-name>/, not at the
// domain root — set BASE_PATH (no trailing slash, e.g. "/gwada-sirius")
// in CI to prefix internal links and Vite's own asset URLs accordingly.
// Left empty for local dev and for a future custom-domain deployment.
const BASE_PATH = (process.env.BASE_PATH || "").replace(/\/+$/, "");
// Vite's build wipes the output dir (emptyOutDir) after 11ty has already
// copied passthrough assets into it — re-copy them once Vite is done.
// https://github.com/11ty/eleventy-plugin-vite/issues/42
function restorePassthroughCopy(copies) {
return {
name: "restore-passthrough-copy",
apply: "build",
closeBundle() {
for (const [from, to] of copies) {
if (!fs.existsSync(from)) continue;
fs.cpSync(from, path.join(OUTPUT_DIR, to), {
recursive: true,
force: true,
});
}
},
};
}
export default function (eleventyConfig) {
eleventyConfig.addPlugin(EleventyVitePlugin, {
viteOptions: {
appType: "mpa",
plugins: [
svelte(),
restorePassthroughCopy([
["src/assets", "assets"],
["public", "."],
]),
],
resolve: {
alias: {
"/src": path.resolve(".", "src"),
},
},
base: BASE_PATH ? `${BASE_PATH}/` : "/",
build: {
emptyOutDir: false,
},
},
});
eleventyConfig.ignores.add("src/paraglide/**");
eleventyConfig.ignores.add("src/islands/**");
eleventyConfig.addPassthroughCopy("src/assets");
eleventyConfig.addPassthroughCopy({ public: "." });
// Trilingual message lookup for Nunjucks templates: {{ "hero_title1" | t(locale) }}
eleventyConfig.addFilter("t", (key, locale, params = {}) => {
const fn = m[key];
if (!fn) return key;
return fn(params, { locale });
});
eleventyConfig.addGlobalData("locales", locales);
eleventyConfig.addGlobalData("baseLocale", baseLocale);
eleventyConfig.addGlobalData("currentYear", () => new Date().getFullYear());
eleventyConfig.addGlobalData("basePath", BASE_PATH);
eleventyConfig.addFilter("localeHref", (pagePath, locale) => {
const clean = pagePath.replace(/^\/+/, "");
const localePath = locale === baseLocale ? `/${clean}` : `/${locale}/${clean}`;
return BASE_PATH + localePath;
});
// For static/passthrough assets referenced by absolute path outside of
// Vite's own bundling (favicon, manifest, service worker, font preloads —
// anything marked vite-ignore in layouts/base.njk).
eleventyConfig.addFilter("withBase", (assetPath) => BASE_PATH + assetPath);
eleventyConfig.addFilter("dump", (value) => JSON.stringify(value).replace(/</g, "\\u003c"));
eleventyConfig.addFilter("formatDate", (isoDate, locale, options = {}) => {
// new Date("YYYY-MM-DD") parses as UTC midnight, which can roll back a
// day once formatted in a timezone behind UTC (e.g. Guadeloupe, UTC-4).
// Build the date from its local components instead.
const [year, month, day] = isoDate.split("-").map(Number);
return new Date(year, month - 1, day).toLocaleDateString(
locale === "ht" ? "fr" : locale,
{ day: "numeric", month: "long", year: "numeric", ...options }
);
});
return {
dir: {
input: "src",
output: OUTPUT_DIR,
includes: "_includes",
data: "_data",
},
templateFormats: ["njk", "md"],
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
};
}
-475
View File
@@ -1,475 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gwada-Sirius : L'Étoile de nos Ancêtres</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;900&family=Playfair+Display:wght@400;700;900&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
:root {
--gold: #FFD700;
--red: #DC143C;
--green: #228B22;
--black: #000000;
--caribbean-blue: #1E40AF;
--sunset-orange: #FF6B35;
--deep-purple: #4C1D95;
}
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #000000 0%, #1E40AF 25%, #DC143C 50%, #228B22 75%, #FFD700 100%);
background-size: 400% 400%;
animation: panafricanGradient 15s ease infinite;
}
@keyframes panafricanGradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
h1, h2, h3 {
font-family: 'Playfair Display', serif;
}
.glass-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.gradient-text {
background: linear-gradient(45deg, #FFD700, #DC143C, #228B22);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradientShift 3s ease-in-out infinite;
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.star-glow {
text-shadow: 0 0 20px #FFD700, 0 0 40px #FFD700, 0 0 60px #FFD700;
}
.floating {
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
}
.pulse-gold {
animation: pulseGold 2s ease-in-out infinite;
}
@keyframes pulseGold {
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 215, 0, 0.7); }
50% { box-shadow: 0 0 0 20px rgba(255, 215, 0, 0); }
}
#globe-container {
cursor: grab;
background: radial-gradient(circle, rgba(30, 64, 175, 0.3) 0%, rgba(0, 0, 0, 0.8) 100%);
}
.prediction-card {
transition: all 0.3s ease;
border-left: 4px solid transparent;
}
.prediction-card:hover {
border-left-color: #FFD700;
transform: translateX(10px);
background: rgba(255, 215, 0, 0.1);
}
.cultural-section {
background: linear-gradient(135deg,
rgba(220, 20, 60, 0.1) 0%,
rgba(34, 139, 34, 0.1) 50%,
rgba(255, 215, 0, 0.1) 100%);
}
.section-divider {
height: 4px;
background: linear-gradient(90deg, #DC143C, #FFD700, #228B22);
margin: 4rem 0;
border-radius: 2px;
}
</style>
</head>
<body class="text-white min-h-screen">
<!-- Header Hero -->
<header class="relative min-h-screen flex items-center justify-center overflow-hidden">
<div class="absolute inset-0 bg-black bg-opacity-40"></div>
<div class="relative z-10 text-center px-4">
<div class="floating">
<h1 class="text-4xl md:text-7xl font-black mb-6 gradient-text">
SIRIUS
</h1>
<div class="star-glow text-6xl md:text-8xl mb-8"></div>
</div>
<h2 class="text-xl md:text-3xl mb-4 font-light">
L'Étoile de nos Ancêtres
</h2>
<p class="text-lg md:text-xl max-w-3xl mx-auto leading-relaxed mb-8">
Depuis l'Égypte antique jusqu'aux Caraïbes, découvrez la danse cosmique de l'étoile la plus brillante du ciel nocturne et son lever héliaque en Guadeloupe.
</p>
<div class="glass-card rounded-2xl p-6 md:p-8 max-w-4xl mx-auto">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 text-center">
<div class="pulse-gold rounded-xl p-4">
<div class="text-3xl md:text-4xl font-black text-yellow-400 mb-2">-1.46</div>
<div class="text-sm md:text-base opacity-90">Magnitude</div>
</div>
<div class="pulse-gold rounded-xl p-4">
<div class="text-3xl md:text-4xl font-black text-red-400 mb-2">22 JUILLET</div>
<div class="text-sm md:text-base opacity-90">Lever en Guadeloupe</div>
</div>
<div class="pulse-gold rounded-xl p-4">
<div class="text-3xl md:text-4xl font-black text-green-400 mb-2">~70</div>
<div class="text-sm md:text-base opacity-90">Jours d'invisibilité</div>
</div>
</div>
</div>
</div>
<div class="absolute bottom-8 left-1/2 transform -translate-x-1/2 animate-bounce">
<div class="w-6 h-10 border-2 border-white rounded-full flex justify-center">
<div class="w-1 h-3 bg-white rounded-full mt-2 animate-pulse"></div>
</div>
</div>
</header>
<!-- Prédictions Section -->
<section class="py-16 px-4 relative">
<div class="section-divider"></div>
<div class="max-w-6xl mx-auto">
<h2 class="text-3xl md:text-5xl font-bold text-center mb-12 gradient-text">
Prédictions pour la Guadeloupe
</h2>
<div class="glass-card rounded-2xl p-6 md:p-8 mb-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div>
<label class="block text-sm font-medium mb-2">Site d'observation :</label>
<select id="location-select" class="w-full p-3 bg-black bg-opacity-50 border border-gray-600 rounded-lg text-white focus:ring-2 focus:ring-yellow-400">
<option value="Pointe des Châteaux">Pointe des Châteaux</option>
<option value="Pointe de la Grande Vigie">Pointe de la Grande Vigie</option>
<option value="Duzer (Sainte-Rose)">Duzer (Sainte-Rose)</option>
<option value="Pointe du Vieux-Fort">Pointe du Vieux-Fort</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-2">Année :</label>
<select id="year-select" class="w-full p-3 bg-black bg-opacity-50 border border-gray-600 rounded-lg text-white focus:ring-2 focus:ring-yellow-400">
<option value="2025">2025</option>
<option value="2026">2026</option>
<option value="2027">2027</option>
<option value="2028">2028</option>
<option value="2029">2029</option>
</select>
</div>
</div>
<div id="prediction-result" class="text-center bg-gradient-to-r from-red-600 via-yellow-500 to-green-600 rounded-xl p-8">
<h3 class="text-2xl font-bold text-black mb-2" id="result-location">Pointe des Châteaux</h3>
<div class="text-5xl font-black text-black mb-2" id="result-date">22 juillet</div>
<div class="text-2xl text-black" id="result-time">vers 05:12</div>
<div class="mt-4 pt-4 border-t border-black border-opacity-30">
<p class="text-sm text-black opacity-80">Fenêtre d'observation :</p>
<p class="font-bold text-lg text-black" id="result-window">20 au 24 Juillet</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="prediction-card glass-card rounded-xl p-6">
<h4 class="font-bold text-yellow-400 mb-2">Équipement nécessaire</h4>
<p class="text-sm opacity-90">Vos yeux suffisent ! Des jumelles peuvent aider dans les lueurs de l'aube.</p>
</div>
<div class="prediction-card glass-card rounded-xl p-6">
<h4 class="font-bold text-red-400 mb-2">Meilleure direction</h4>
<p class="text-sm opacity-90">Est-Sud-Est, horizon dégagé loin des lumières de la ville.</p>
</div>
<div class="prediction-card glass-card rounded-xl p-6">
<h4 class="font-bold text-green-400 mb-2">Conseil d'expert</h4>
<p class="text-sm opacity-90">Observez 2 jours avant et après la date prédite pour maximiser vos chances.</p>
</div>
</div>
</div>
</section>
<!-- Globe 3D Section -->
<section class="py-16 px-4">
<div class="section-divider"></div>
<div class="max-w-6xl mx-auto text-center">
<h2 class="text-3xl md:text-5xl font-bold mb-8 gradient-text">
La Vague Planétaire de Sirius
</h2>
<p class="text-lg mb-12 max-w-3xl mx-auto">
Découvrez comment le lever héliaque de Sirius se propage comme une vague à travers la planète,
de l'hémisphère Sud vers le Nord, reliant l'Afrique aux Caraïbes.
</p>
<div class="glass-card rounded-2xl p-8 mb-8">
<div id="globe-container" class="w-full h-96 rounded-xl mb-6"></div>
<p class="text-sm opacity-80">
Cliquez et glissez pour faire tourner le globe. La vague animée représente la progression du lever héliaque.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="glass-card rounded-xl p-6">
<h3 class="text-xl font-bold text-yellow-400 mb-4">🌍 Départ au Sud</h3>
<p class="text-sm opacity-90">
La vague commence dans l'hémisphère Sud car c'est l'hiver austral.
Les nuits étant plus longues, Sirius apparaît plus tôt (fin juin).
</p>
</div>
<div class="glass-card rounded-xl p-6">
<h3 class="text-xl font-bold text-red-400 mb-4">🌅 Arrivée au Nord</h3>
<p class="text-sm opacity-90">
En été boréal, les nuits sont plus courtes. Il faut attendre plus longtemps
pour que Sirius soit visible à l'aube (fin juillet).
</p>
</div>
</div>
</div>
</section>
<!-- Cultural Heritage Section -->
<section class="py-16 px-4 cultural-section">
<div class="section-divider"></div>
<div class="max-w-6xl mx-auto">
<h2 class="text-3xl md:text-5xl font-bold text-center mb-12 gradient-text">
Héritage Culturel Panafricain
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<div class="glass-card rounded-xl p-8">
<div class="text-4xl mb-4">🏺</div>
<h3 class="text-2xl font-bold text-yellow-400 mb-4">Égypte Antique : Sothis</h3>
<p class="opacity-90 leading-relaxed">
Pour les anciens Égyptiens, le lever héliaque de Sirius (Sothis) était l'événement le plus important de l'année.
Il annonçait la crue vitale du Nil qui fertilisait les terres. Ce "retour" de l'étoile marquait le Nouvel An
et symbolisait la renaissance, associée à la déesse Isis.
</p>
</div>
<div class="glass-card rounded-xl p-8">
<div class="text-4xl mb-4">🌟</div>
<h3 class="text-2xl font-bold text-red-400 mb-4">Dogon du Mali : Po Tolo</h3>
<p class="opacity-90 leading-relaxed">
Le peuple Dogon possède un savoir ancestral stupéfiant sur Sirius, décrivant une compagne invisible,
"Po Tolo" (Sirius B), qui tourne autour de l'étoile principale en 50 ans. Des connaissances qui correspondent
précisément aux découvertes modernes astronomiques.
</p>
</div>
</div>
<div class="glass-card rounded-xl p-8 text-center">
<h3 class="text-2xl font-bold text-green-400 mb-6">De l'Afrique aux Caraïbes</h3>
<p class="text-lg opacity-90 max-w-4xl mx-auto leading-relaxed">
Sirius unit nos territoires caribéens à notre héritage africain. Cette étoile brillante,
guide des navigateurs et messager des saisons, continue d'éclairer nos îles comme elle
éclairait nos ancêtres. Observer Sirius en Guadeloupe, c'est participer à une tradition
millénaire qui traverse les océans et les générations.
</p>
<div class="mt-8 flex justify-center space-x-8 text-3xl">
<span title="Guadeloupe">🇬🇵</span>
<span title="Martinique">🇲🇶</span>
<span title="Afrique">🌍</span>
<span title="Égypte">🔺</span>
</div>
</div>
</div>
</section>
<!-- Understanding Section -->
<section class="py-16 px-4">
<div class="section-divider"></div>
<div class="max-w-6xl mx-auto">
<h2 class="text-3xl md:text-5xl font-bold text-center mb-12 gradient-text">
Comprendre le Phénomène
</h2>
<div class="glass-card rounded-xl p-8 mb-8">
<div class="text-center mb-8">
<h3 class="text-2xl font-bold text-yellow-400 mb-4">Arcus Visionis ≈ 9-10°</h3>
<p class="opacity-90">
L'angle clé : la hauteur négative du Soleil sous l'horizon nécessaire pour que Sirius devienne visible à l'aube.
</p>
</div>
<div class="relative h-32 bg-gradient-to-t from-blue-900 to-blue-600 rounded-lg p-4 mb-8">
<div class="absolute bottom-0 w-full h-px bg-yellow-400"></div>
<div class="absolute bottom-0 left-1/2 transform -translate-x-1/2 text-xs text-yellow-400">HORIZON</div>
<div class="absolute bottom-6 left-1/4 text-2xl animate-pulse"></div>
<div class="absolute bottom-6 right-1/4 text-2xl text-yellow-400">☀️</div>
<div class="absolute top-4 left-1/2 transform -translate-x-1/2 text-xs text-center text-white opacity-80">
Le Soleil doit être à 9-10° sous l'horizon<br>pour que Sirius devienne visible
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="glass-card rounded-xl p-6 text-center">
<div class="text-3xl mb-4"></div>
<h4 class="font-bold text-yellow-400 mb-3">Magnitude</h4>
<p class="text-sm opacity-90">
Plus une étoile est brillante, plus elle est facile à voir dans la lueur de l'aube.
Sirius (-1.46) est l'étoile la plus brillante du ciel nocturne.
</p>
</div>
<div class="glass-card rounded-xl p-6 text-center">
<div class="text-3xl mb-4">🌍</div>
<h4 class="font-bold text-red-400 mb-3">Latitude</h4>
<p class="text-sm opacity-90">
La position sur Terre change l'angle du lever et la durée du crépuscule.
En Guadeloupe (16°N), les conditions sont idéales.
</p>
</div>
<div class="glass-card rounded-xl p-6 text-center">
<div class="text-3xl mb-4">🌫️</div>
<h4 class="font-bold text-green-400 mb-3">Atmosphère</h4>
<p class="text-sm opacity-90">
L'humidité, la brume de sable ou les nuages peuvent "éteindre" l'étoile
et retarder sa visibilité. D'où l'importance de la fenêtre d'observation.
</p>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="py-12 px-4 text-center">
<div class="section-divider"></div>
<div class="max-w-4xl mx-auto">
<div class="glass-card rounded-xl p-8">
<h3 class="text-2xl font-bold gradient-text mb-6">Levez les yeux vers le ciel</h3>
<p class="opacity-90 mb-6">
Une application créée pour célébrer notre héritage astronomique et culturel panafricain.
De l'Égypte antique aux Antilles françaises, Sirius continue de nous guider.
</p>
<div class="text-sm opacity-70">
© 2025 - Basé sur le rapport "Sirius en Guadeloupe : Guide Astronomique et Culturel"
</div>
</div>
</div>
</footer>
<script>
// Données de prédictions
const predictionsData = {
"Pointe des Châteaux": {"2025": "22 juillet, 05:12", "2026": "22 juillet, 05:12", "2027": "22 juillet, 05:13", "2028": "21 juillet, 05:13", "2029": "22 juillet, 05:14"},
"Pointe de la Grande Vigie": {"2025": "22 juillet, 05:11", "2026": "22 juillet, 05:12", "2027": "22 juillet, 05:12", "2028": "21 juillet, 05:12", "2029": "22 juillet, 05:13"},
"Duzer (Sainte-Rose)": {"2025": "22 juillet, 05:13", "2026": "22 juillet, 05:14", "2027": "22 juillet, 05:14", "2028": "21 juillet, 05:14", "2029": "22 juillet, 05:15"},
"Pointe du Vieux-Fort": {"2025": "22 juillet, 05:14", "2026": "22 juillet, 05:14", "2027": "22 juillet, 05:15", "2028": "21 juillet, 05:15", "2029": "22 juillet, 05:16"}
};
// Fonction de mise à jour des prédictions
function updatePrediction() {
const location = document.getElementById('location-select').value;
const year = document.getElementById('year-select').value;
const prediction = predictionsData[location][year];
const [datePart, timePart] = prediction.split(', ');
const day = parseInt(datePart.split(' ')[0]);
const month = datePart.split(' ')[1];
document.getElementById('result-location').textContent = location;
document.getElementById('result-date').textContent = datePart;
document.getElementById('result-time').textContent = `vers ${timePart}`;
document.getElementById('result-window').textContent = `${day - 2} au ${day + 2} ${month}`;
}
// Initialisation du globe 3D
function initGlobe() {
const container = document.getElementById('globe-container');
if (!container) return;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(renderer.domElement);
// Lumières
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);
// Sphere pour la Terre
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: 0x4A90E2,
shininess: 30
});
const globe = new THREE.Mesh(geometry, material);
scene.add(globe);
// Vague animée
const waveGeometry = new THREE.TorusGeometry(1.1, 0.03, 8, 32);
const waveMaterial = new THREE.MeshBasicMaterial({
color: 0xFFD700,
transparent: true,
opacity: 0.7
});
const wave = new THREE.Mesh(waveGeometry, waveMaterial);
scene.add(wave);
camera.position.z = 3;
// Animation
function animate() {
requestAnimationFrame(animate);
globe.rotation.y += 0.005;
// Animation de la vague
const time = Date.now() * 0.001;
wave.position.y = Math.sin(time) * 0.5;
wave.rotation.x = Math.PI / 2;
renderer.render(scene, camera);
}
animate();
// Gestion du redimensionnement
window.addEventListener('resize', () => {
if (container.clientWidth > 0) {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
});
}
// Initialisation
document.addEventListener('DOMContentLoaded', () => {
updatePrediction();
initGlobe();
// Event listeners
document.getElementById('location-select').addEventListener('change', updatePrediction);
document.getElementById('year-select').addEventListener('change', updatePrediction);
});
</script>
</body>
</html>
-17
View File
@@ -1,17 +0,0 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sirius en Guadeloupe - Guide du Lever Héliaque</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Inter:wght@300;400;600;700&family=Playfair+Display:wght@700;900&display=swap" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>
+231
View File
@@ -0,0 +1,231 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"meta_title": "Sirius in Guadeloupe — Wep Ronpet, the heliacal rising",
"meta_description": "Calculate and understand the heliacal rising of Sirius in Guadeloupe: astronomy, Egyptian and Dogon heritage, observation guide.",
"skip_to_content": "Skip to content",
"lang_name_fr": "Français",
"lang_name_en": "English",
"lang_name_ht": "Kreyòl",
"nav_home": "Home",
"nav_science": "Sirius & Science",
"nav_culture": "Culture & Knowledge",
"nav_observer": "Observe",
"nav_associations": "Associations",
"hero_eyebrow": "Wep Ronpet",
"hero_title1": "Sirius",
"hero_title2": "in Guadeloupe",
"hero_subtitle": "The Opening of the Cosmic Year",
"hero_description": "The millennial meeting between Sky, Science and Culture",
"hero_date_label": "Next heliacal rising",
"hero_cta_predictions": "Discover the predictions",
"hero_cta_learn": "Learn more",
"home_intro_title": "Sirius and Wep Ronpet",
"home_intro_subtitle": "The Opening of the Year — the cosmic New Year of the ancient Egyptians",
"home_intro_p1": "<strong>Wep Ronpet</strong> — \"the Opening of the Year\" in ancient Egyptian — marked the most sacred day of the Egyptian calendar. This day corresponded to the heliacal rising of Sirius, when the star reappeared at dawn after 70 days of invisibility.",
"home_intro_p2": "The heliacal rising of Sirius is not just an astronomical phenomenon. It is a millennial meeting between humanity and the cosmos, a moment where science and spirituality meet in the light of dawn.",
"home_intro_p3": "In Guadeloupe, observing this phenomenon connects us directly to African roots, where Sirius was venerated as the herald of life and renewal.",
"feature_brightest_title": "The brightest star",
"feature_brightest_desc": "Sirius shines with a magnitude of -1.46, dominating our night sky.",
"feature_rising_title": "The heliacal rising",
"feature_rising_desc": "Its first appearance at dawn after 70 days of invisibility marks a cosmic event.",
"feature_heritage_title": "Ancestral heritage",
"feature_heritage_desc": "From ancient Egypt to the Dogon of Mali, Sirius has guided humanity for millennia.",
"science_title": "Sirius & Science",
"science_subtitle": "Understanding the cosmic dance between Earth, Sun and Sirius",
"science_tab_mechanics": "Celestial mechanics",
"science_tab_arcus": "Arcus Visionis",
"science_tab_factors": "Influencing factors",
"science_tab_calc": "Calculation method",
"science_mechanics_title": "The cosmic ballet",
"science_mechanics_p1": "The heliacal rising is the result of a precise celestial choreography. For about 70 days, Sirius stays invisible, hidden by the Sun's brilliance. Its return at dawn marks a unique moment when the star rises just before the Sun, visible for a few instants in the glow of morning twilight.",
"science_mechanics_p2": "Earth, in its yearly orbital motion, changes our perspective on the stars. When the Sun sits between us and Sirius, the star becomes invisible. Only when the angle becomes favourable does Sirius reappear.",
"science_diagram_sun": "Sun",
"science_diagram_earth": "Earth",
"science_diagram_sirius": "Sirius",
"science_arcus_title": "The arc of visibility",
"science_arcus_p1": "The Arcus Visionis is the critical angle: the Sun must be about 9 to 10° below the horizon for Sirius to become visible. It's a delicate balance between the star's brightness and the sky's luminosity.",
"science_arcus_p2": "For Sirius, with its exceptional magnitude of -1.46, this angle is relatively small compared to dimmer stars — which is why it is one of the first stars visible at dawn.",
"science_arcus_horizon_label": "Horizon",
"science_factor_magnitude_title": "Magnitude",
"science_factor_magnitude_desc": "Sirius shines at -1.46, making it visible even in difficult conditions. The brighter a star, the earlier it pierces the glow of dawn.",
"science_factor_latitude_title": "Latitude",
"science_factor_latitude_desc": "At 16°N in Guadeloupe, stars rise almost vertically. This tropical geometry shapes the duration of twilight and visibility.",
"science_factor_atmosphere_title": "Atmosphere",
"science_factor_atmosphere_desc": "Caribbean humidity and Saharan dust haze can delay visibility by several days. A dry, clear sky is ideal.",
"science_calc_title": "Calculating the heliacal rising of Sirius",
"science_calc_formula_label": "Main formula",
"science_calc_formula": "Heliacal rising date = Conjunction date + (Arcus Visionis / Sun's angular speed)",
"science_calc_step1_title": "Guadeloupe's coordinates",
"science_calc_step1_value": "Latitude: 16.25°N — Longitude: 61.58°W",
"science_calc_step2_title": "Sirius's position",
"science_calc_step2_ra": "Right ascension: 6h 45m 09s",
"science_calc_step2_dec": "Declination: -16° 42' 58\"",
"science_calc_arcus_title": "Arcus Visionis",
"science_calc_arcus_desc": "The minimum angle between the Sun and a star for it to become visible — about 2° of altitude above the horizon for a star as bright as Sirius, reached around 50 minutes before sunrise.",
"science_calc_sources_title": "Method and sources",
"science_calc_sources_text": "Per-site dates are interpolated from Jean Meeus's tables (arc of vision as a function of latitude) and cross-checked against astronomer Jeffrey L. Hunt's calculations (whenthecurveslineup.com). At 16°N, interpolating between 10°N (July 17) and 20°N (July 24) gives a heliacal rising around July 22-23 — consistent with traditional observation in Guadeloupe. The actual observed date still depends on weather and the clarity of the eastern horizon.",
"culture_title": "Culture & Knowledge",
"culture_subtitle": "A millennial heritage uniting Africa and the Caribbean",
"culture_egypt_title": "Egypt: Sothis, goddess of the Nile",
"culture_egypt_desc": "For the ancient Egyptians, the heliacal rising of Sirius (Sothis) was the most important event of the year. It announced the flooding of the Nile, a source of life and fertility. This \"return\" of the star marked the New Year and symbolised rebirth, associated with the goddess Isis.",
"culture_dogon_title": "Dogon: keepers of stellar knowledge",
"culture_dogon_desc": "The Dogon people of Mali hold astonishing knowledge about Sirius. They knew of the existence of Sirius B, a white dwarf invisible to the naked eye, long before its scientific discovery. Their 60-year ritual cycle, the Sigui, is directly linked to their understanding of the Sirius system.",
"culture_caribbean_title": "Caribbean: stellar navigation",
"culture_caribbean_desc": "In the Caribbean, Sirius has guided sailors for centuries. Indigenous peoples and African mariners used its rising to mark the seasons and navigate between islands. Even today it remains a fundamental landmark for traditional fishermen.",
"culture_timeline_title": "Timeline of knowledge",
"culture_timeline_1_date": "3000 BCE",
"culture_timeline_1_title": "Ancient Egypt",
"culture_timeline_1_desc": "First calendar based on Sirius",
"culture_timeline_2_date": "13th century",
"culture_timeline_2_title": "Dogon migration",
"culture_timeline_2_desc": "Preservation of astronomical knowledge",
"culture_timeline_3_date": "15th-17th century",
"culture_timeline_3_title": "Atlantic crossing",
"culture_timeline_3_desc": "African knowledge travels to the Caribbean",
"culture_timeline_4_date": "Today",
"culture_timeline_4_title": "Renaissance",
"culture_timeline_4_desc": "Rediscovery and celebration of Wep Ronpet in Guadeloupe",
"dogon_title": "The Dogon, keepers of the sky",
"dogon_intro": "For centuries, the Dogon people of Mali have cultivated a cosmogony centred on Sirius, passed down orally from generation to generation.",
"dogon_tab_cosmogony": "Cosmogony",
"dogon_tab_system": "The Sirius system",
"dogon_tab_astronomy": "Dogon astronomy",
"dogon_tab_heritage": "Heritage",
"dogon_cosmogony_title": "Dogon cosmogony",
"dogon_cosmogony_p1": "Sigi Tolo — Sirius, the brightest star in the sky — sits at the centre of a highly sophisticated Dogon cosmogony, passed down orally for millennia.",
"dogon_cosmogony_p2": "The Sigui, celebrated every 60 years, marks the renewal of the world and commemorates the revelation of speech to humankind. This roughly 60-year period is close to Sirius B's 50-year orbit around Sirius A, adjusted by ritual considerations.",
"dogon_cosmogony_nommo_title": "The Nommo",
"dogon_cosmogony_nommo_desc": "A primordial amphibious being said to come from Sirius, the Nommo is said to have brought civilisation and foundational knowledge to the Dogon ancestors.",
"dogon_system_sigutolo_title": "Sigi Tolo (Sirius A)",
"dogon_system_sigutolo_desc": "The main star, source of light and life. Associated with femininity and fertility, it guides the seasons and the harvests.",
"dogon_system_potolo_title": "Po Tolo (Sirius B)",
"dogon_system_potolo_desc": "A companion invisible to the naked eye, described as \"heavy\" and \"white\". Its 50-year orbit around Sirius A was only confirmed by Western astronomers in 1862 — the heart of the Dogon mystery.",
"dogon_system_emmeya_title": "Emme Ya Tolo",
"dogon_system_emmeya_desc": "A hypothetical third star, four times lighter than Po Tolo and on a wider orbit. Not confirmed by modern astronomy.",
"dogon_mystery_title": "The mystery of the knowledge",
"dogon_mystery_theory1_title": "Ancient transmission",
"dogon_mystery_theory1_desc": "A heritage passed down from an earlier, advanced civilisation.",
"dogon_mystery_theory2_title": "Contact",
"dogon_mystery_theory2_desc": "The account of the Nommo, said to come from Sirius according to oral tradition.",
"dogon_mystery_theory3_title": "Careful observation",
"dogon_mystery_theory3_desc": "A patient deduction from perturbations observed on Sirius A.",
"dogon_astronomy_geometry_title": "Sacred geometry",
"dogon_astronomy_geometry_desc": "Complex geometric figures represent celestial motions and cosmic cycles.",
"dogon_astronomy_calendar_title": "Lunar calendar",
"dogon_astronomy_calendar_desc": "A sophisticated calendar system based on lunar phases and the position of Sirius, in service of agriculture.",
"dogon_astronomy_planets_title": "Planetary knowledge",
"dogon_astronomy_planets_desc": "A description of Saturn's rings and Jupiter's moons, invisible without a telescope.",
"dogon_astronomy_universe_title": "Structure of the universe",
"dogon_astronomy_universe_desc": "A universe conceived as infinite, made of multiple worlds — an intuition close to modern cosmology.",
"dogon_calc_example_title": "A striking correspondence",
"dogon_calc_example_text": "Po Tolo's orbital period: 50 years. Ritual adjustment: × 1.2. Sigui period: 60 years. Match with Sirius B's actual orbit (50.1 years): 99.8%.",
"dogon_heritage_1_date": "Antiquity",
"dogon_heritage_1_desc": "Possible links with the Egyptian priests who venerated Sothis (Sirius)",
"dogon_heritage_2_date": "13th century",
"dogon_heritage_2_desc": "Migration to the Bandiagara cliffs in Mali, preservation of traditions",
"dogon_heritage_3_date": "1931-1956",
"dogon_heritage_3_desc": "Marcel Griaule's ethnographic study reveals Dogon astronomical knowledge",
"dogon_heritage_4_date": "Today",
"dogon_heritage_4_desc": "The Dogon continue to inspire researchers and scientists worldwide",
"dogon_heritage_diaspora_title": "An influence that crossed the Atlantic",
"dogon_heritage_diaspora_desc": "African astronomical traditions, including Dogon knowledge, crossed the Atlantic with the diaspora, enriching the spiritual and calendrical practices of the Caribbean.",
"dogon_heritage_diaspora_haiti": "Vodou and sacred astronomy",
"dogon_heritage_diaspora_guadeloupe": "Gwoka traditions and cosmic cycles",
"dogon_heritage_diaspora_martinique": "Bèlè and stellar rhythms",
"dogon_heritage_diaspora_jamaica": "Rastafari and cosmic consciousness",
"observer_title": "Observe",
"observer_subtitle": "Where, when and how to observe the heliacal rising in Guadeloupe",
"observer_calculator_title": "Prediction calculator",
"observer_calculator_subtitle": "Choose your site to find out the exact date",
"observer_calculator_select_label": "Your observation site",
"observer_calculator_table_site": "Site",
"observer_calculator_table_date": "2026 date",
"observer_calculator_table_time": "Time",
"observer_calculator_result_heliacal": "Heliacal rising",
"observer_calculator_result_window": "Observation window",
"observer_map_title": "Map of observation sites",
"observer_map_subtitle": "Seven landmark locations across the archipelago",
"observer_timeline_title": "Observation timeline",
"observer_phase1_time": "4:30 am",
"observer_phase1_title": "Preparation",
"observer_phase1_desc": "Arrive on site, let your eyes adapt to the darkness",
"observer_phase2_time": "5:00 am",
"observer_phase2_title": "Search",
"observer_phase2_desc": "Look towards the East-Southeast, near the horizon",
"observer_phase3_time": "5:15 am",
"observer_phase3_title": "Appearance",
"observer_phase3_desc": "Sirius emerges from the dawn glow",
"observer_phase4_time": "5:30 am",
"observer_phase4_title": "Observation",
"observer_phase4_desc": "The optimal moment to contemplate",
"observer_equipment_title": "Recommended equipment",
"observer_equipment_essential_badge": "Essential",
"observer_equipment_binoculars_name": "Binoculars",
"observer_equipment_binoculars_desc": "7x50 or 10x50 recommended",
"observer_equipment_lamp_name": "Red lamp",
"observer_equipment_lamp_desc": "Preserves night vision",
"observer_equipment_compass_name": "Compass",
"observer_equipment_compass_desc": "To find East",
"observer_equipment_clothes_name": "Warm clothes",
"observer_equipment_clothes_desc": "Mornings are cool",
"observer_equipment_notebook_name": "Notebook",
"observer_equipment_notebook_desc": "To record your observations",
"observer_tips_title": "Practical tips",
"observer_tip_weather_title": "Ideal weather",
"observer_tip_weather_desc": "Clear sky to the East, no low clouds, moderate humidity",
"observer_tip_moon_title": "Moon phase",
"observer_tip_moon_desc": "New moon or thin crescent for a darker sky",
"observer_tip_apps_title": "Useful apps",
"observer_tip_apps_desc": "Stellarium, SkySafari, Star Walk to locate the constellations",
"observer_tip_group_title": "As a group",
"observer_tip_group_desc": "Share this unique moment with family and friends",
"globe_title": "The wave phenomenon",
"globe_p1": "The heliacal rising of Sirius does not happen simultaneously across the Earth. Like a cosmic wave, it moves from East to West, first reaching Réunion in late June, then sweeping across Africa, before finally reaching the Caribbean in July.",
"globe_p2": "This animation shows the progression of this \"planetary wave\", following Earth's rotation and the position of Sirius in the sky. Each place experiences this sacred moment on a precise date, forming a natural cosmic calendar that has guided civilisations for millennia.",
"globe_controls_hint": "Drag to pan · scroll to zoom",
"globe_timeline_title": "Timeline of the wave",
"wave_showcase_title": "A wave that crosses the world",
"wave_showcase_subtitle": "The heliacal rising touches thirteen cities across Africa, Europe and the Caribbean between late June and mid-August",
"wave_showcase_cta": "See the full map",
"associations_title": "Associations celebrating Wep Ronpet",
"associations_subtitle": "Discover the events and communities celebrating the Kamit New Year",
"associations_events_title": "Wep Ronpet events",
"associations_event1_title": "Kamite New Year",
"associations_event1_organizer": "Afrocentricity International & Famille Muntu",
"associations_event1_desc": "A sacred time of passage into a new cosmic rotation, lived in harmony with the laws of the universe. Ceremony and rituals, Kamite gastronomy, artisan market.",
"associations_event2_title": "Kamit New Year",
"associations_event2_organizer": "Mouvement Politique Spirituel MUN",
"associations_event2_desc": "Celebration with the New Moon. Ceremony, rituals, conferences, Kamit dinner, dance evening.",
"associations_community_title": "Community & resources",
"associations_link1_title": "14th Member",
"associations_link1_desc": "Website — Events",
"associations_link2_title": "Mouvement Politique Spirituel MUN",
"associations_link2_desc": "Official Facebook page",
"associations_video_title": "Discover Wep Ronpet",
"associations_note": "Dates, venues and links should be confirmed each year with the organisers — the information above is provided as a guide only.",
"footer_tagline": "A project celebrating African and Caribbean astronomical heritage through the observation of the heliacal rising of Sirius.",
"footer_links_title": "Quick links",
"footer_resources_title": "Resources",
"footer_resources_stellarium": "Stellarium",
"footer_resources_guide": "PDF guide",
"footer_resources_references": "References",
"footer_resources_contact": "Contact",
"footer_next_title": "Next observation",
"footer_copyright_prefix": "Project made with ❤️ by",
"footer_and": "and",
"footer_quote": "\"The stars are the ancestors watching over us\" — African proverb",
"footer_rights": "Sirius Gwada. Look up to the sky."
}
+231
View File
@@ -0,0 +1,231 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"meta_title": "Sirius en Guadeloupe — Wep Ronpet, le lever héliaque",
"meta_description": "Calculez et comprenez le lever héliaque de Sirius en Guadeloupe : astronomie, héritage égyptien et dogon, guide d'observation.",
"skip_to_content": "Aller au contenu",
"lang_name_fr": "Français",
"lang_name_en": "English",
"lang_name_ht": "Kreyòl",
"nav_home": "Accueil",
"nav_science": "Sirius & Science",
"nav_culture": "Culture & Savoirs",
"nav_observer": "Observer",
"nav_associations": "Associations",
"hero_eyebrow": "Wep Ronpet",
"hero_title1": "Sirius",
"hero_title2": "en Guadeloupe",
"hero_subtitle": "L'Ouverture de l'Année Cosmique",
"hero_description": "Le rendez-vous millénaire entre Ciel, Science et Culture",
"hero_date_label": "Prochain lever héliaque",
"hero_cta_predictions": "Découvrir les prédictions",
"hero_cta_learn": "En savoir plus",
"home_intro_title": "Sirius et Wep Ronpet",
"home_intro_subtitle": "L'Ouverture de l'Année — le Nouvel An cosmique des anciens Égyptiens",
"home_intro_p1": "<strong>Wep Ronpet</strong> — « l'Ouverture de l'Année » en égyptien ancien — marquait le jour le plus sacré du calendrier égyptien. Ce jour correspondait au lever héliaque de Sirius, quand l'étoile réapparaissait à l'aube après 70 jours d'invisibilité.",
"home_intro_p2": "Le lever héliaque de Sirius n'est pas qu'un simple phénomène astronomique. C'est un rendez-vous millénaire entre l'humanité et le cosmos, un moment où science et spiritualité se rencontrent dans la lumière de l'aube.",
"home_intro_p3": "En Guadeloupe, observer ce phénomène nous connecte directement aux racines africaines, où Sirius était vénérée comme l'annonciatrice de la vie et du renouveau.",
"feature_brightest_title": "L'étoile la plus brillante",
"feature_brightest_desc": "Sirius brille avec une magnitude de -1,46, dominant notre ciel nocturne.",
"feature_rising_title": "Le lever héliaque",
"feature_rising_desc": "Sa première apparition à l'aube après 70 jours d'invisibilité marque un événement cosmique.",
"feature_heritage_title": "Héritage ancestral",
"feature_heritage_desc": "De l'Égypte ancienne aux Dogons du Mali, Sirius guide l'humanité depuis des millénaires.",
"science_title": "Sirius & Science",
"science_subtitle": "Comprendre la danse cosmique entre Terre, Soleil et Sirius",
"science_tab_mechanics": "Mécanique céleste",
"science_tab_arcus": "Arcus Visionis",
"science_tab_factors": "Facteurs d'influence",
"science_tab_calc": "Méthode de calcul",
"science_mechanics_title": "Le ballet cosmique",
"science_mechanics_p1": "Le lever héliaque est le fruit d'une chorégraphie céleste précise. Pendant environ 70 jours, Sirius reste invisible, cachée par l'éclat du Soleil. Son retour à l'aube marque un moment unique où l'étoile se lève juste avant le Soleil, visible quelques instants dans les lueurs du crépuscule matinal.",
"science_mechanics_p2": "La Terre, dans son mouvement orbital annuel, modifie notre perspective sur les étoiles. Quand le Soleil se trouve entre nous et Sirius, l'étoile devient invisible. C'est seulement quand l'angle devient favorable que Sirius réapparaît.",
"science_diagram_sun": "Soleil",
"science_diagram_earth": "Terre",
"science_diagram_sirius": "Sirius",
"science_arcus_title": "L'arc de visibilité",
"science_arcus_p1": "L'Arcus Visionis est l'angle critique : le Soleil doit être à environ 9 à 10° sous l'horizon pour que Sirius devienne visible. C'est un équilibre délicat entre la brillance de l'étoile et la luminosité du ciel.",
"science_arcus_p2": "Pour Sirius, avec sa magnitude exceptionnelle de -1,46, cet angle est relativement faible comparé à d'autres étoiles moins brillantes. C'est pourquoi elle est l'une des premières à être visible à l'aube.",
"science_arcus_horizon_label": "Horizon",
"science_factor_magnitude_title": "Magnitude",
"science_factor_magnitude_desc": "Sirius brille à -1,46, la rendant visible même dans des conditions difficiles. Plus une étoile est brillante, plus tôt elle perce les lueurs de l'aube.",
"science_factor_latitude_title": "Latitude",
"science_factor_latitude_desc": "À 16°N en Guadeloupe, les astres se lèvent presque verticalement. Cette géométrie tropicale influence la durée du crépuscule et la visibilité.",
"science_factor_atmosphere_title": "Atmosphère",
"science_factor_atmosphere_desc": "L'humidité caribéenne et les brumes de sable saharien peuvent retarder la visibilité de plusieurs jours. Un ciel sec et clair est idéal.",
"science_calc_title": "Calcul du lever héliaque de Sirius",
"science_calc_formula_label": "Formule principale",
"science_calc_formula": "Date du lever héliaque = Date de conjonction + (Arcus Visionis / Vitesse angulaire du Soleil)",
"science_calc_step1_title": "Coordonnées de la Guadeloupe",
"science_calc_step1_value": "Latitude : 16,25°N — Longitude : 61,58°O",
"science_calc_step2_title": "Position de Sirius",
"science_calc_step2_ra": "Ascension droite : 6h 45m 09s",
"science_calc_step2_dec": "Déclinaison : -16° 42' 58\"",
"science_calc_arcus_title": "Arcus Visionis",
"science_calc_arcus_desc": "Angle minimal entre le Soleil et une étoile pour que celle-ci devienne visible — environ 2° de hauteur au-dessus de l'horizon pour une étoile aussi brillante que Sirius, atteint autour de 50 minutes avant le lever du Soleil.",
"science_calc_sources_title": "Méthode et sources",
"science_calc_sources_text": "Les dates par site sont interpolées à partir des tables de Jean Meeus (arc de vision en fonction de la latitude) et recoupées avec les calculs de l'astronome Jeffrey L. Hunt (whenthecurveslineup.com). À 16°N, l'interpolation entre 10°N (17 juillet) et 20°N (24 juillet) donne un lever héliaque autour du 22-23 juillet — cohérent avec l'observation traditionnelle en Guadeloupe. La date réelle observée dépend toujours de la météo et de la clarté de l'horizon Est.",
"culture_title": "Culture & Savoirs",
"culture_subtitle": "Un héritage millénaire qui unit l'Afrique et les Caraïbes",
"culture_egypt_title": "Égypte : Sothis, la déesse du Nil",
"culture_egypt_desc": "Pour les anciens Égyptiens, le lever héliaque de Sirius (Sothis) était l'événement le plus important de l'année. Il annonçait la crue du Nil, source de vie et de fertilité. Ce « retour » de l'étoile marquait le Nouvel An et symbolisait la renaissance, associée à la déesse Isis.",
"culture_dogon_title": "Dogon : les gardiens du savoir stellaire",
"culture_dogon_desc": "Le peuple Dogon du Mali possède un savoir stupéfiant sur Sirius. Ils connaissaient l'existence de Sirius B, une naine blanche invisible à l'œil nu, bien avant sa découverte scientifique. Leur cycle rituel de 60 ans, le Sigui, est directement lié à leur compréhension du système de Sirius.",
"culture_caribbean_title": "Caraïbes : la navigation stellaire",
"culture_caribbean_desc": "Dans les Caraïbes, Sirius a guidé les navigateurs depuis des siècles. Les peuples autochtones et les marins africains utilisaient son lever pour marquer les saisons et naviguer entre les îles. Aujourd'hui encore, elle reste un repère fondamental pour les pêcheurs traditionnels.",
"culture_timeline_title": "Chronologie du savoir",
"culture_timeline_1_date": "3000 av. J.-C.",
"culture_timeline_1_title": "Égypte ancienne",
"culture_timeline_1_desc": "Premier calendrier basé sur Sirius",
"culture_timeline_2_date": "XIIIᵉ siècle",
"culture_timeline_2_title": "Migration dogon",
"culture_timeline_2_desc": "Préservation du savoir astronomique",
"culture_timeline_3_date": "XVᵉ-XVIIᵉ siècle",
"culture_timeline_3_title": "Traversée atlantique",
"culture_timeline_3_desc": "Le savoir africain voyage vers les Caraïbes",
"culture_timeline_4_date": "Aujourd'hui",
"culture_timeline_4_title": "Renaissance",
"culture_timeline_4_desc": "Redécouverte et célébration du Wep Ronpet en Guadeloupe",
"dogon_title": "Les Dogon, gardiens du ciel",
"dogon_intro": "Le peuple Dogon du Mali cultive depuis des siècles une cosmogonie centrée sur Sirius, transmise oralement de génération en génération.",
"dogon_tab_cosmogony": "Cosmogonie",
"dogon_tab_system": "Système de Sirius",
"dogon_tab_astronomy": "Astronomie dogon",
"dogon_tab_heritage": "Héritage",
"dogon_cosmogony_title": "La cosmogonie dogon",
"dogon_cosmogony_p1": "Sigi Tolo — Sirius, l'étoile la plus brillante du ciel — est au centre d'une cosmogonie dogon d'une grande sophistication, transmise oralement pendant des millénaires.",
"dogon_cosmogony_p2": "Le Sigui, célébré tous les 60 ans, marque le renouvellement du monde et commémore la révélation de la parole aux hommes. Cette périodicité d'environ 60 ans se rapproche de l'orbite de 50 ans de Sirius B autour de Sirius A, ajustée par des considérations rituelles.",
"dogon_cosmogony_nommo_title": "Le Nommo",
"dogon_cosmogony_nommo_desc": "Être amphibie primordial venu de Sirius, le Nommo aurait apporté la civilisation et les savoirs fondateurs aux ancêtres dogon.",
"dogon_system_sigutolo_title": "Sigi Tolo (Sirius A)",
"dogon_system_sigutolo_desc": "L'étoile principale, source de lumière et de vie. Associée à la féminité et à la fécondité, elle guide les saisons et les récoltes.",
"dogon_system_potolo_title": "Po Tolo (Sirius B)",
"dogon_system_potolo_desc": "Compagnon invisible à l'œil nu, décrit comme « lourd » et « blanc ». Son orbite de 50 ans autour de Sirius A n'a été confirmée par les astronomes occidentaux qu'en 1862 — le cœur du mystère dogon.",
"dogon_system_emmeya_title": "Emme Ya Tolo",
"dogon_system_emmeya_desc": "Une troisième étoile hypothétique, quatre fois plus légère que Po Tolo et sur une orbite plus large. Non confirmée par l'astronomie moderne.",
"dogon_mystery_title": "Le mystère du savoir",
"dogon_mystery_theory1_title": "Transmission ancienne",
"dogon_mystery_theory1_desc": "Un héritage venu d'une civilisation antérieure avancée.",
"dogon_mystery_theory2_title": "Contact",
"dogon_mystery_theory2_desc": "Le récit des Nommo, venus de Sirius selon la tradition orale.",
"dogon_mystery_theory3_title": "Observation minutieuse",
"dogon_mystery_theory3_desc": "Une déduction patiente à partir des perturbations observées sur Sirius A.",
"dogon_astronomy_geometry_title": "Géométrie sacrée",
"dogon_astronomy_geometry_desc": "Des figures géométriques complexes représentent les mouvements célestes et les cycles cosmiques.",
"dogon_astronomy_calendar_title": "Calendrier lunaire",
"dogon_astronomy_calendar_desc": "Un système calendaire sophistiqué fondé sur les phases lunaires et la position de Sirius, au service de l'agriculture.",
"dogon_astronomy_planets_title": "Connaissance planétaire",
"dogon_astronomy_planets_desc": "Une description des anneaux de Saturne et des lunes de Jupiter, invisibles sans télescope.",
"dogon_astronomy_universe_title": "Structure de l'univers",
"dogon_astronomy_universe_desc": "Un univers conçu comme infini, fait de mondes multiples — une intuition proche de la cosmologie moderne.",
"dogon_calc_example_title": "Une correspondance troublante",
"dogon_calc_example_text": "Période orbitale de Po Tolo : 50 ans. Ajustement rituel : × 1,2. Période du Sigui : 60 ans. Concordance avec l'orbite réelle de Sirius B (50,1 ans) : 99,8 %.",
"dogon_heritage_1_date": "Antiquité",
"dogon_heritage_1_desc": "Liens possibles avec les prêtres égyptiens qui vénéraient Sothis (Sirius)",
"dogon_heritage_2_date": "XIIIᵉ siècle",
"dogon_heritage_2_desc": "Migration vers les falaises de Bandiagara au Mali, préservation des traditions",
"dogon_heritage_3_date": "1931-1956",
"dogon_heritage_3_desc": "L'étude ethnographique de Marcel Griaule révèle le savoir astronomique dogon",
"dogon_heritage_4_date": "Aujourd'hui",
"dogon_heritage_4_desc": "Les Dogon continuent d'inspirer chercheurs et scientifiques du monde entier",
"dogon_heritage_diaspora_title": "Une influence qui traverse l'Atlantique",
"dogon_heritage_diaspora_desc": "Les traditions astronomiques africaines, dogon comprises, ont traversé l'Atlantique avec la diaspora, enrichissant les pratiques spirituelles et calendaires des Antilles.",
"dogon_heritage_diaspora_haiti": "Vaudou et astronomie sacrée",
"dogon_heritage_diaspora_guadeloupe": "Traditions du gwoka et cycles cosmiques",
"dogon_heritage_diaspora_martinique": "Bèlè et rythmes stellaires",
"dogon_heritage_diaspora_jamaica": "Rastafari et conscience cosmique",
"observer_title": "Observer",
"observer_subtitle": "Où, quand et comment observer le lever héliaque en Guadeloupe",
"observer_calculator_title": "Calculateur de prédictions",
"observer_calculator_subtitle": "Choisissez votre site pour connaître la date exacte",
"observer_calculator_select_label": "Votre lieu d'observation",
"observer_calculator_table_site": "Site",
"observer_calculator_table_date": "Date 2026",
"observer_calculator_table_time": "Heure",
"observer_calculator_result_heliacal": "Lever héliaque",
"observer_calculator_result_window": "Fenêtre d'observation",
"observer_map_title": "Carte des sites d'observation",
"observer_map_subtitle": "Sept lieux emblématiques répartis sur l'archipel",
"observer_timeline_title": "Chronologie de l'observation",
"observer_phase1_time": "04h30",
"observer_phase1_title": "Préparation",
"observer_phase1_desc": "Arrivez sur site, laissez vos yeux s'adapter à l'obscurité",
"observer_phase2_time": "05h00",
"observer_phase2_title": "Recherche",
"observer_phase2_desc": "Cherchez vers l'Est-Sud-Est, près de l'horizon",
"observer_phase3_time": "05h15",
"observer_phase3_title": "Apparition",
"observer_phase3_desc": "Sirius émerge de la lueur de l'aube",
"observer_phase4_time": "05h30",
"observer_phase4_title": "Observation",
"observer_phase4_desc": "Moment optimal pour contempler",
"observer_equipment_title": "Équipement recommandé",
"observer_equipment_essential_badge": "Essentiel",
"observer_equipment_binoculars_name": "Jumelles",
"observer_equipment_binoculars_desc": "7x50 ou 10x50 recommandées",
"observer_equipment_lamp_name": "Lampe rouge",
"observer_equipment_lamp_desc": "Préserve la vision nocturne",
"observer_equipment_compass_name": "Boussole",
"observer_equipment_compass_desc": "Pour trouver l'Est",
"observer_equipment_clothes_name": "Vêtements chauds",
"observer_equipment_clothes_desc": "Les matins sont frais",
"observer_equipment_notebook_name": "Carnet de notes",
"observer_equipment_notebook_desc": "Pour documenter l'observation",
"observer_tips_title": "Conseils pratiques",
"observer_tip_weather_title": "Météo idéale",
"observer_tip_weather_desc": "Ciel dégagé à l'Est, pas de nuages bas, humidité modérée",
"observer_tip_moon_title": "Phase lunaire",
"observer_tip_moon_desc": "Nouvelle lune ou croissant fin pour un ciel plus sombre",
"observer_tip_apps_title": "Applications utiles",
"observer_tip_apps_desc": "Stellarium, SkySafari, Star Walk pour repérer les constellations",
"observer_tip_group_title": "En groupe",
"observer_tip_group_desc": "Partagez ce moment unique avec famille et amis",
"globe_title": "Le phénomène de la vague",
"globe_p1": "Le lever héliaque de Sirius ne se produit pas simultanément sur toute la Terre. Telle une vague cosmique, il progresse d'Est en Ouest, touchant d'abord La Réunion fin juin, puis traversant l'Afrique, pour finalement atteindre les Caraïbes en juillet.",
"globe_p2": "Cette animation montre la progression de cette « vague planétaire » qui suit la rotation terrestre et la position de Sirius dans le ciel. Chaque lieu expérimente ce moment sacré à une date précise, créant un calendrier cosmique naturel qui a guidé les civilisations pendant des millénaires.",
"globe_controls_hint": "Glisser pour déplacer · molette pour zoomer",
"globe_timeline_title": "Chronologie de la vague",
"wave_showcase_title": "Une vague qui traverse le monde",
"wave_showcase_subtitle": "Le lever héliaque touche treize villes d'Afrique, d'Europe et des Caraïbes entre fin juin et mi-août",
"wave_showcase_cta": "Voir la carte complète",
"associations_title": "Associations qui fêtent le Wep Ronpet",
"associations_subtitle": "Découvrez les événements et communautés qui célèbrent le Nouvel An Kamit",
"associations_events_title": "Événements Wep Ronpet",
"associations_event1_title": "Nouvel An Kamite",
"associations_event1_organizer": "Afrocentricity International & Famille Muntu",
"associations_event1_desc": "Temps sacré de passage vers une nouvelle rotation cosmique, vécu en harmonie avec les lois de l'univers. Cérémonie et rituels, gastronomie kamite, marché artisanal.",
"associations_event2_title": "Nouvel An Kamit",
"associations_event2_organizer": "Mouvement Politique Spirituel MUN",
"associations_event2_desc": "Célébration avec la Nouvelle Lune. Cérémonie, rituels, conférences, dîner kamit, soirée dansante.",
"associations_community_title": "Communauté & ressources",
"associations_link1_title": "14ème Membre",
"associations_link1_desc": "Site web — Événements",
"associations_link2_title": "Mouvement Politique Spirituel MUN",
"associations_link2_desc": "Page Facebook officielle",
"associations_video_title": "Découvrir le Wep Ronpet",
"associations_note": "Dates, lieux et liens à confirmer chaque année auprès des organisateurs — les informations ci-dessus sont données à titre indicatif.",
"footer_tagline": "Un projet célébrant le patrimoine astronomique africain et caribéen à travers l'observation du lever héliaque de Sirius.",
"footer_links_title": "Liens rapides",
"footer_resources_title": "Ressources",
"footer_resources_stellarium": "Stellarium",
"footer_resources_guide": "Guide PDF",
"footer_resources_references": "Références",
"footer_resources_contact": "Contact",
"footer_next_title": "Prochaine observation",
"footer_copyright_prefix": "Projet réalisé avec ❤️ par",
"footer_and": "et",
"footer_quote": "« Les étoiles sont les ancêtres qui veillent sur nous » — proverbe africain",
"footer_rights": "Sirius Gwada. Levez les yeux vers le ciel."
}
+97
View File
@@ -0,0 +1,97 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"meta_title": "Sirius nan Gwadloup — Wep Ronpet, leve elyak la",
"skip_to_content": "Ale nan kontni an",
"lang_name_fr": "Français",
"lang_name_en": "English",
"lang_name_ht": "Kreyòl",
"nav_home": "Akèy",
"nav_science": "Sirius & Syans",
"nav_culture": "Kilti & Konesans",
"nav_observer": "Obsève",
"nav_associations": "Asosyasyon",
"hero_eyebrow": "Wep Ronpet",
"hero_title1": "Sirius",
"hero_title2": "nan Gwadloup",
"hero_subtitle": "Ouvèti Ane Kosmik la",
"hero_description": "Randevou milenè ant Syèl, Syans ak Kilti",
"hero_date_label": "Pwochen leve elyak",
"hero_cta_predictions": "Dekouvri prediksyon yo",
"hero_cta_learn": "Aprann plis",
"home_intro_title": "Sirius ak Wep Ronpet",
"home_intro_subtitle": "Ouvèti Ane a — Nouvèl An kosmik ansyen ejipsyen yo",
"feature_brightest_title": "Zetwal ki pi klere a",
"feature_rising_title": "Leve elyak la",
"feature_heritage_title": "Eritaj zansèt",
"science_title": "Sirius & Syans",
"science_tab_mechanics": "Mekanik selès",
"science_tab_arcus": "Arcus Visionis",
"science_tab_factors": "Faktè enfliyans",
"science_tab_calc": "Metòd kalkil",
"science_diagram_sun": "Solèy",
"science_diagram_earth": "Latè",
"science_diagram_sirius": "Sirius",
"science_factor_magnitude_title": "Mayitid",
"science_factor_latitude_title": "Latitid",
"science_factor_atmosphere_title": "Atmosfè",
"culture_title": "Kilti & Konesans",
"culture_egypt_title": "Lejip: Sothis, deyès Nil la",
"culture_dogon_title": "Dogon: gadyen konesans zetwal yo",
"culture_caribbean_title": "Karayib: navigasyon selès",
"culture_timeline_title": "Kwonoloji konesans lan",
"dogon_title": "Dogon yo, gadyen syèl la",
"dogon_tab_cosmogony": "Kosmogoni",
"dogon_tab_system": "Sistèm Sirius la",
"dogon_tab_astronomy": "Astwonomi dogon",
"dogon_tab_heritage": "Eritaj",
"observer_title": "Obsève",
"observer_calculator_title": "Kalkilatè prediksyon",
"observer_calculator_select_label": "Kote w ap obsève",
"observer_calculator_table_site": "Sit",
"observer_calculator_table_date": "Dat 2026",
"observer_calculator_table_time": "Lè",
"observer_map_title": "Kat sit obsèvasyon yo",
"observer_timeline_title": "Kwonoloji obsèvasyon an",
"observer_phase1_title": "Preparasyon",
"observer_phase2_title": "Rechèch",
"observer_phase3_title": "Aparisyon",
"observer_phase4_title": "Obsèvasyon",
"observer_equipment_title": "Ekipman rekòmande",
"observer_equipment_essential_badge": "Esansyèl",
"observer_equipment_binoculars_name": "Jimèl",
"observer_equipment_lamp_name": "Lanp wouj",
"observer_equipment_compass_name": "Bousòl",
"observer_equipment_clothes_name": "Rad cho",
"observer_equipment_notebook_name": "Kaye nòt",
"observer_tips_title": "Konsèy pratik",
"globe_title": "Fenomèn vag la",
"wave_showcase_title": "Yon vag ki travèse mond lan",
"wave_showcase_cta": "Gade kat konplè a",
"globe_controls_hint": "Trennen pou deplase · molèt pou zoome",
"associations_title": "Asosyasyon ki fete Wep Ronpet",
"associations_subtitle": "Dekouvri evènman ak kominote ki selebre Nouvèl An Kamit la",
"associations_events_title": "Evènman Wep Ronpet",
"associations_event1_title": "Nouvèl An Kamite",
"associations_event2_title": "Nouvèl An Kamit",
"associations_community_title": "Kominote & resous",
"associations_video_title": "Dekouvri Wep Ronpet",
"footer_links_title": "Lyen rapid",
"footer_resources_title": "Resous",
"footer_next_title": "Pwochen obsèvasyon",
"footer_copyright_prefix": "Pwojè fèt ak ❤️ pa",
"footer_and": "ak",
"footer_quote": "« Zetwal yo se zansèt k ap veye sou nou » — pwovèb afriken",
"footer_rights": "Sirius Gwada. Leve je nou gade syèl la."
}
+2490 -770
View File
File diff suppressed because it is too large Load Diff
+16 -10
View File
@@ -1,20 +1,26 @@
{
"name": "gwada-sirius",
"version": "1.5.0",
"version": "2.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"deploy": "npm run build && gh-pages -d dist"
"messages": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide",
"dev": "npm run messages && eleventy --serve",
"build": "npm run messages && eleventy",
"preview": "npx serve _site",
"deploy": "npm run build && gh-pages -d _site"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@11ty/eleventy": "^3.1.6",
"@11ty/eleventy-plugin-vite": "^8.0.0",
"@fontsource-variable/fraunces": "^5.2.9",
"@fontsource/atkinson-hyperlegible": "^5.2.8",
"@inlang/paraglide-js": "^2.20.2",
"@sveltejs/vite-plugin-svelte": "^7.1.4",
"gh-pages": "^6.1.1",
"svelte": "^4.2.7",
"vite": "^5.0.8"
"svelte": "^5.56.4",
"vite": "^8.1.3"
},
"type": "module",
"dependencies": {
"animejs": "^3.2.1"
"leaflet": "^1.9.4"
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "fr",
"locales": ["fr", "en", "ht"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{locale}.json"
}
}
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<circle cx="32" cy="32" r="32" fill="#141a33"/>
<circle cx="32" cy="30" r="5" fill="#e8c766"/>
<path d="M32 30 L26 46 L38 46 Z" fill="#e8c766" opacity="0.85"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

+12
View File
@@ -0,0 +1,12 @@
{
"name": "Sirius en Guadeloupe — Wep Ronpet",
"short_name": "Sirius Gwada",
"description": "Lever héliaque de Sirius en Guadeloupe : astronomie, culture et observation.",
"start_url": ".",
"display": "standalone",
"background_color": "#141a33",
"theme_color": "#141a33",
"icons": [
{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }
]
}
View File
+36
View File
@@ -0,0 +1,36 @@
// Service worker minimal : résilience réseau (3G/4G irrégulier) et usage
// hors-ligne des pages déjà visitées. Stratégie stale-while-revalidate.
const CACHE_NAME = "gwada-sirius-v2";
self.addEventListener("install", (event) => {
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
)
);
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
if (event.request.method !== "GET") return;
const url = new URL(event.request.url);
if (url.origin !== self.location.origin) return;
event.respondWith(
caches.open(CACHE_NAME).then(async (cache) => {
const cached = await cache.match(event.request);
const network = fetch(event.request)
.then((response) => {
if (response.ok) cache.put(event.request, response.clone());
return response;
})
.catch(() => cached);
return cached || network;
})
);
});
-75
View File
@@ -1,75 +0,0 @@
<script>
import { onMount } from 'svelte';
import { initLanguage } from './lib/i18n.js';
import Loader from './components/Loader.svelte';
import Hero from './components/Hero.svelte';
import Navigation from './components/Navigation.svelte';
import About from './components/About.svelte';
import Predictions from './components/Predictions.svelte';
import Science from './components/Science.svelte';
import Culture from './components/Culture.svelte';
import Observatory from './components/Observatory.svelte';
import GlobeGeoJSON from './components/GlobeGeoJSON.svelte';
import Footer from './components/Footer.svelte';
import Dogon from './components/Dogon.svelte';
import Calculations from './components/Calculations.svelte';
import ObservationMap from './components/ObservationMap.svelte';
let scrollY = 0;
let activeSection = 'home';
onMount(() => {
// Initialiser la langue
initLanguage();
const handleScroll = () => {
scrollY = window.scrollY;
// Déterminer la section active
const sections = ['home', 'about', 'predictions', 'science', 'culture', 'observatory'];
for (const section of sections) {
const element = document.getElementById(section);
if (element) {
const rect = element.getBoundingClientRect();
if (rect.top <= 100 && rect.bottom >= 100) {
activeSection = section;
break;
}
}
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
});
</script>
<Loader />
<Navigation {activeSection} />
<main>
<Hero />
<About />
<Predictions />
<Science />
<Calculations />
<Dogon />
<Culture />
<Observatory />
<ObservationMap />
<GlobeGeoJSON />
</main>
<Footer />
<style>
:global(body) {
margin: 0;
padding: 0;
}
main {
position: relative;
z-index: 1;
}
</style>
+7
View File
@@ -0,0 +1,7 @@
export default [
{ key: "nav_home", path: "/" },
{ key: "nav_science", path: "/sirius-science/" },
{ key: "nav_culture", path: "/culture-savoirs/" },
{ key: "nav_observer", path: "/observer/" },
{ key: "nav_associations", path: "/associations/" },
];
+22
View File
@@ -0,0 +1,22 @@
import sites from "./sites.js";
// Prochain lever héliaque (calculé au build, à partir de Pointe des Châteaux
// comme site de référence — les 7 sites ne divergent que de quelques minutes).
export default function () {
const reference = sites[0];
const today = new Date();
const years = Object.keys(reference.predictions)
.map(Number)
.sort((a, b) => a - b);
for (const year of years) {
const p = reference.predictions[year];
const when = new Date(`${p.date}T${p.time}:00`);
if (when >= today) {
return { ...p, year };
}
}
const lastYear = years[years.length - 1];
return { ...reference.predictions[lastYear], year: lastYear };
}
+152
View File
@@ -0,0 +1,152 @@
// Sept sites d'observation du lever héliaque de Sirius en Guadeloupe.
//
// Dates interpolées à partir des tables d'Arcus Visionis de Jean Meeus, recoupées
// avec les calculs de Jeffrey L. Hunt (whenthecurveslineup.com) : à 16°N, la
// heliacal rising tombe autour du 22 juillet (~24 juillet à 20°N, ~17 juillet à
// 10°N). Les petits écarts entre sites reflètent la longitude et l'altitude de
// l'horizon Est. Ce sont des estimations de modèle — la date réellement observée
// dépend toujours de la météo et de la clarté de l'horizon.
export default [
{
id: "pointe-des-chateaux",
name: "Pointe des Châteaux",
lat: 16.2476,
lon: -61.1771,
alt: 30,
description_fr:
"Point de vue élevé avec panorama dégagé vers l'Est sur l'Atlantique.",
description_en:
"Elevated viewpoint with a clear panorama east over the Atlantic.",
predictions: {
2025: { date: "2025-07-22", time: "05:12" },
2026: { date: "2026-07-22", time: "05:12" },
2027: { date: "2027-07-22", time: "05:13" },
2028: { date: "2028-07-21", time: "05:13" },
2029: { date: "2029-07-22", time: "05:14" },
2030: { date: "2030-07-22", time: "05:14" },
2031: { date: "2031-07-22", time: "05:15" },
2032: { date: "2032-07-21", time: "05:15" },
},
},
{
id: "grande-vigie",
name: "Pointe de la Grande Vigie",
lat: 16.5098,
lon: -61.4666,
alt: 81,
description_fr:
"Point le plus septentrional de la Guadeloupe, horizon marin parfait.",
description_en:
"Guadeloupe's northernmost point, with a perfect sea horizon.",
predictions: {
2025: { date: "2025-07-22", time: "05:11" },
2026: { date: "2026-07-22", time: "05:12" },
2027: { date: "2027-07-22", time: "05:12" },
2028: { date: "2028-07-21", time: "05:12" },
2029: { date: "2029-07-22", time: "05:13" },
2030: { date: "2030-07-22", time: "05:13" },
2031: { date: "2031-07-22", time: "05:14" },
2032: { date: "2032-07-21", time: "05:14" },
},
},
{
id: "duzer",
name: "Duzer (Sainte-Rose)",
lat: 16.3361,
lon: -61.743,
alt: 20,
description_fr:
"Site nécessitant un point dégagé vers l'Est sur les mornes.",
description_en: "Requires a clear vantage point east over the hills.",
predictions: {
2025: { date: "2025-07-22", time: "05:13" },
2026: { date: "2026-07-22", time: "05:14" },
2027: { date: "2027-07-22", time: "05:14" },
2028: { date: "2028-07-21", time: "05:14" },
2029: { date: "2029-07-22", time: "05:15" },
2030: { date: "2030-07-22", time: "05:15" },
2031: { date: "2031-07-22", time: "05:16" },
2032: { date: "2032-07-21", time: "05:16" },
},
},
{
id: "saint-felix",
name: "Saint-Félix (Le Gosier)",
lat: 16.2005,
lon: -61.4605,
alt: 5,
description_fr: "Plage avec observation au niveau de la mer.",
description_en: "Beach with sea-level observation.",
predictions: {
2025: { date: "2025-07-22", time: "05:13" },
2026: { date: "2026-07-22", time: "05:13" },
2027: { date: "2027-07-22", time: "05:14" },
2028: { date: "2028-07-21", time: "05:14" },
2029: { date: "2029-07-22", time: "05:15" },
2030: { date: "2030-07-22", time: "05:15" },
2031: { date: "2031-07-22", time: "05:16" },
2032: { date: "2032-07-21", time: "05:16" },
},
},
{
id: "grande-pointe",
name: "Grande Pointe (Trois-Rivières)",
lat: 15.9696,
lon: -61.6308,
alt: 42,
description_fr:
"Site côtier avec une altitude intéressante pour l'observation.",
description_en: "Coastal site with a useful elevation for observation.",
predictions: {
2025: { date: "2025-07-22", time: "05:13" },
2026: { date: "2026-07-22", time: "05:14" },
2027: { date: "2027-07-22", time: "05:14" },
2028: { date: "2028-07-21", time: "05:15" },
2029: { date: "2029-07-22", time: "05:15" },
2030: { date: "2030-07-22", time: "05:16" },
2031: { date: "2031-07-22", time: "05:16" },
2032: { date: "2032-07-21", time: "05:17" },
},
},
{
id: "petit-perou",
name: "Petit-Pérou",
lat: 16.0517,
lon: -61.5567,
alt: 10,
description_fr:
"Site des roches gravées, observation depuis la côte.",
description_en: "Site of the engraved rocks, observed from the coast.",
predictions: {
2025: { date: "2025-07-22", time: "05:14" },
2026: { date: "2026-07-22", time: "05:14" },
2027: { date: "2027-07-22", time: "05:15" },
2028: { date: "2028-07-21", time: "05:15" },
2029: { date: "2029-07-22", time: "05:16" },
2030: { date: "2030-07-22", time: "05:16" },
2031: { date: "2031-07-22", time: "05:17" },
2032: { date: "2032-07-21", time: "05:17" },
},
},
{
id: "vieux-fort",
name: "Pointe du Vieux-Fort",
lat: 15.9483,
lon: -61.7072,
alt: 20,
description_fr:
"Point le plus méridional de Basse-Terre, vue sur le canal des Saintes.",
description_en:
"Basse-Terre's southernmost point, overlooking the Saintes channel.",
predictions: {
2025: { date: "2025-07-22", time: "05:14" },
2026: { date: "2026-07-22", time: "05:14" },
2027: { date: "2027-07-22", time: "05:15" },
2028: { date: "2028-07-21", time: "05:15" },
2029: { date: "2029-07-22", time: "05:16" },
2030: { date: "2030-07-22", time: "05:16" },
2031: { date: "2031-07-22", time: "05:17" },
2032: { date: "2032-07-21", time: "05:17" },
},
},
];
+19
View File
@@ -0,0 +1,19 @@
// Progression du lever héliaque à travers la diaspora africaine et caribéenne
// (fin juin à mi-août), du plus oriental au plus occidental. Source unique,
// consommée à la fois par les pages 11ty (aperçu statique) et par l'îlot
// SiriusGlobe (carte Leaflet interactive) — voir src/islands/globe/world.js.
export default [
{ name: "La Réunion", lat: -21.1151, lon: 55.5364, date: "27 juin" },
{ name: "Kinshasa", lat: -4.4419, lon: 15.2663, date: "16 juillet" },
{ name: "Douala", lat: 4.0511, lon: 9.7679, date: "18 juillet" },
{ name: "Yaoundé", lat: 3.848, lon: 11.5021, date: "19 juillet" },
{ name: "Abidjan", lat: 5.36, lon: -4.0083, date: "20 juillet" },
{ name: "Accra", lat: 5.6037, lon: -0.187, date: "21 juillet" },
{ name: "Guadeloupe", lat: 16.265, lon: -61.551, date: "22 juillet" },
{ name: "Martinique", lat: 14.6415, lon: -61.0242, date: "23 juillet" },
{ name: "Dakar", lat: 14.7167, lon: -17.4677, date: "23 juillet" },
{ name: "Cayenne", lat: 4.9346, lon: -52.3281, date: "24 juillet" },
{ name: "Bamako", lat: 12.6392, lon: -8.0029, date: "25 juillet" },
{ name: "Le Caire", lat: 30.0444, lon: 31.2357, date: "3 août" },
{ name: "Paris", lat: 48.8566, lon: 2.3522, date: "15 août" },
];
+36
View File
@@ -0,0 +1,36 @@
<!doctype html>
<html lang="{{ locale }}" data-theme="oki">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ pageTitle }} — {{ "meta_title" | t(locale) }}</title>
<meta name="description" content="{{ 'meta_description' | t(locale) }}">
<link vite-ignore rel="icon" href="{{ '/favicon.svg' | withBase }}" type="image/svg+xml">
<link vite-ignore rel="manifest" href="{{ '/manifest.webmanifest' | withBase }}">
{% for loc in locales %}
<link vite-ignore rel="alternate" hreflang="{{ loc }}" href="{{ slug | localeHref(loc) }}">
{% endfor %}
<link vite-ignore rel="preload" href="{{ '/assets/fonts/fraunces-variable.woff2' | withBase }}" as="font" type="font/woff2" crossorigin>
<link vite-ignore rel="preload" href="{{ '/assets/fonts/atkinson-400.woff2' | withBase }}" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/src/styles/app.css">
</head>
<body>
<a class="skip-link" href="#main-content">{{ "skip_to_content" | t(locale) }}</a>
{% include "partials/nav.njk" %}
<main id="main-content">
{{ content | safe }}
</main>
{% include "partials/footer.njk" %}
<script>
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => navigator.serviceWorker.register("{{ '/sw.js' | withBase }}"));
}
</script>
</body>
</html>
+49
View File
@@ -0,0 +1,49 @@
<footer class="site-footer">
<div class="container footer-grid">
<div class="footer-section">
<h2>Sirius Gwada</h2>
<p>{{ "footer_tagline" | t(locale) }}</p>
</div>
<div class="footer-section">
<h3>{{ "footer_links_title" | t(locale) }}</h3>
<ul>
{% for item in nav %}
<li><a href="{{ item.path | localeHref(locale) }}">{{ item.key | t(locale) }}</a></li>
{% endfor %}
</ul>
</div>
<div class="footer-section">
<h3>{{ "footer_resources_title" | t(locale) }}</h3>
<ul>
<li><a href="https://stellarium.org" target="_blank" rel="noopener">{{ "footer_resources_stellarium" | t(locale) }}</a></li>
<li><a href="{{ '/observer/' | localeHref(locale) }}">{{ "footer_resources_guide" | t(locale) }}</a></li>
<li><a href="{{ '/sirius-science/' | localeHref(locale) }}">{{ "footer_resources_references" | t(locale) }}</a></li>
<li><a href="mailto:contact@o-k-i.net">{{ "footer_resources_contact" | t(locale) }}</a></li>
</ul>
</div>
<div class="footer-section">
<h3>{{ "footer_next_title" | t(locale) }}</h3>
<div class="countdown">
<div class="countdown-item">
<span class="countdown-value">{{ nextRising.date | formatDate(locale, { day: "numeric", month: "long" }) }}</span>
<span class="countdown-label">{{ nextRising.time }} · {{ nextRising.year }}</span>
</div>
</div>
</div>
</div>
<div class="container footer-bottom">
<p class="pattern-strip kente" aria-hidden="true"></p>
<p>&copy; {{ currentYear }} {{ "footer_rights" | t(locale) }}</p>
<p class="footer-credits">
{{ "footer_copyright_prefix" | t(locale) }}
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
{{ "footer_and" | t(locale) }}
<a href="https://o-k-i.net" target="_blank" rel="noopener">OKI</a>
</p>
<p class="footer-quote">{{ "footer_quote" | t(locale) }}</p>
</div>
</footer>
+36
View File
@@ -0,0 +1,36 @@
<header class="nav">
<div class="container nav-inner">
<a class="nav-brand" href="{{ '' | localeHref(locale) }}">
<span aria-hidden="true">☥</span> Sirius Gwada
</a>
<details class="nav-disclosure">
<summary class="nav-toggle">
<span class="visually-hidden">Menu</span>
<span aria-hidden="true" class="nav-toggle-icon"></span>
</summary>
<ul class="nav-menu">
{% for item in nav %}
<li>
<a href="{{ item.path | localeHref(locale) }}"
{% if pageKey == item.key %}aria-current="page"{% endif %}>
{{ item.key | t(locale) }}
</a>
</li>
{% endfor %}
</ul>
</details>
<ul class="nav-lang">
{% for loc in locales %}
<li>
<a href="{{ slug | localeHref(loc) }}"
lang="{{ loc }}"
{% if loc == locale %}aria-current="true" class="active"{% endif %}>
{{ ("lang_name_" + loc) | t(locale) }}
</a>
</li>
{% endfor %}
</ul>
</div>
</header>
-217
View File
@@ -1,217 +0,0 @@
/* Variables CSS pour le thème panafricain/caribéen */
:root {
/* Couleurs principales inspirées du panafricanisme et des Caraïbes */
--color-primary: #FFD700; /* Or - Soleil, Sirius */
--color-secondary: #DC143C; /* Rouge profond */
--color-tertiary: #228B22; /* Vert forêt */
--color-dark: #1a1a2e; /* Bleu nuit profond */
--color-light: #f5f5dc; /* Beige clair */
--color-accent: #FF6B35; /* Orange corail caribéen */
/* Dégradés */
--gradient-dawn: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
--gradient-sunrise: linear-gradient(135deg, #FFD700 0%, #FFA500 50%, #FF6B35 100%);
--gradient-caribbean: linear-gradient(135deg, #00CED1 0%, #20B2AA 50%, #48D1CC 100%);
/* Typographie */
--font-display: 'Bebas Neue', sans-serif;
--font-heading: 'Playfair Display', serif;
--font-body: 'Inter', sans-serif;
/* Espacements */
--spacing-xs: 0.5rem;
--spacing-sm: 1rem;
--spacing-md: 2rem;
--spacing-lg: 3rem;
--spacing-xl: 4rem;
/* Animations */
--transition-base: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Reset et base */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-body);
background-color: var(--color-dark);
color: var(--color-light);
overflow-x: hidden;
line-height: 1.6;
}
/* Patterns géométriques africains en CSS */
.pattern-kente {
background-image:
repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(255, 215, 0, 0.1) 10px, rgba(255, 215, 0, 0.1) 20px),
repeating-linear-gradient(-45deg, transparent, transparent 10px, rgba(220, 20, 60, 0.1) 10px, rgba(220, 20, 60, 0.1) 20px);
}
.pattern-bogolan {
background-image:
radial-gradient(circle at 20% 50%, transparent 30%, rgba(255, 215, 0, 0.05) 30%, rgba(255, 215, 0, 0.05) 32%, transparent 32%),
radial-gradient(circle at 60% 50%, transparent 30%, rgba(255, 215, 0, 0.05) 30%, rgba(255, 215, 0, 0.05) 32%, transparent 32%),
radial-gradient(circle at 40% 80%, transparent 30%, rgba(255, 215, 0, 0.05) 30%, rgba(255, 215, 0, 0.05) 32%, transparent 32%);
background-size: 50px 50px;
}
.pattern-adinkra {
background-image:
repeating-conic-gradient(from 45deg at 50% 50%, transparent 0deg, rgba(255, 215, 0, 0.1) 90deg, transparent 90deg, transparent 180deg),
repeating-conic-gradient(from 0deg at 50% 50%, transparent 0deg, rgba(34, 139, 34, 0.1) 90deg, transparent 90deg, transparent 180deg);
background-size: 40px 40px;
}
/* Utilités */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-sm);
}
/* Typographie */
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-heading);
font-weight: 700;
line-height: 1.2;
margin-bottom: var(--spacing-sm);
}
h1 {
font-size: clamp(2.5rem, 8vw, 4rem);
background: var(--gradient-sunrise);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
h2 {
font-size: clamp(2rem, 6vw, 3rem);
color: var(--color-primary);
}
h3 {
font-size: clamp(1.5rem, 4vw, 2rem);
}
/* Boutons */
.btn {
display: inline-block;
padding: var(--spacing-sm) var(--spacing-md);
background: var(--gradient-sunrise);
color: var(--color-dark);
text-decoration: none;
border: none;
border-radius: 50px;
font-weight: 600;
font-family: var(--font-body);
cursor: pointer;
transition: var(--transition-base);
position: relative;
overflow: hidden;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(255, 215, 0, 0.3);
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn:active::before {
width: 300px;
height: 300px;
}
/* Cards */
.card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 215, 0, 0.2);
border-radius: 20px;
padding: var(--spacing-md);
transition: var(--transition-base);
}
.card:hover {
transform: translateY(-5px);
border-color: rgba(255, 215, 0, 0.5);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
/* Animations */
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes glow {
0%, 100% { opacity: 0.5; }
50% { opacity: 1; }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.float {
animation: float 6s ease-in-out infinite;
}
.glow {
animation: glow 2s ease-in-out infinite;
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 0 var(--spacing-xs);
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2rem;
}
}
/* Scrollbar personnalisée */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--color-dark);
}
::-webkit-scrollbar-thumb {
background: var(--gradient-sunrise);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-primary);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+79
View File
@@ -0,0 +1,79 @@
---
pagination:
data: locales
size: 1
alias: locale
permalink: "{% if locale == baseLocale %}/associations/index.html{% else %}/{{ locale }}/associations/index.html{% endif %}"
layout: layouts/base.njk
pageKey: associations
slug: "associations/"
eleventyComputed:
pageTitle: "{{ 'associations_title' | t(locale) }}"
---
<section class="section">
<div class="container">
<div class="section-header center">
<p class="section-eyebrow">Wep Ronpet</p>
<h1>{{ "associations_title" | t(locale) }}</h1>
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "associations_subtitle" | t(locale) }}</p>
</div>
<div class="grid-auto">
<div class="card">
<h2>{{ "associations_events_title" | t(locale) }}</h2>
<div class="stack" style="--stack-space: 0">
<a class="event-link" href="https://my.weezevent.com/nouvel-an-kamite" target="_blank" rel="noopener noreferrer">
<span class="event-icon" aria-hidden="true">🎊</span>
<span class="event-info">
<span class="event-title">{{ "associations_event1_title" | t(locale) }}</span>
<span class="event-platform">{{ "associations_event1_organizer" | t(locale) }}</span>
<span>{{ "associations_event1_desc" | t(locale) }}</span>
</span>
</a>
<a class="event-link" href="https://my.weezevent.com/nouvel-an-kamit" target="_blank" rel="noopener noreferrer">
<span class="event-icon" aria-hidden="true">✨</span>
<span class="event-info">
<span class="event-title">{{ "associations_event2_title" | t(locale) }}</span>
<span class="event-platform">{{ "associations_event2_organizer" | t(locale) }}</span>
<span>{{ "associations_event2_desc" | t(locale) }}</span>
</span>
</a>
</div>
</div>
<div class="card">
<h2>{{ "associations_community_title" | t(locale) }}</h2>
<div class="stack" style="--stack-space: 0">
<a class="community-link" href="https://14eme-membre.net/site-web/#événements" target="_blank" rel="noopener noreferrer">
<span class="community-icon" aria-hidden="true">🌍</span>
<span class="community-info">
<span class="community-title">{{ "associations_link1_title" | t(locale) }}</span>
<span>{{ "associations_link1_desc" | t(locale) }}</span>
</span>
</a>
<a class="community-link" href="https://www.facebook.com/people/Mouvement-Politique-Spirituel-MUN" target="_blank" rel="noopener noreferrer">
<span class="community-icon" aria-hidden="true">📘</span>
<span class="community-info">
<span class="community-title">{{ "associations_link2_title" | t(locale) }}</span>
<span>{{ "associations_link2_desc" | t(locale) }}</span>
</span>
</a>
</div>
</div>
</div>
<h2 class="center" style="margin-top: var(--space-7)">{{ "associations_video_title" | t(locale) }}</h2>
<div class="video-frame card" style="margin-top: var(--space-4)">
<iframe
src="https://www.youtube.com/embed/0_8kWlDTf94"
title="{{ 'associations_video_title' | t(locale) }}"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
loading="lazy"
></iframe>
</div>
<p class="associations-note center">{{ "associations_note" | t(locale) }}</p>
</div>
</section>
-185
View File
@@ -1,185 +0,0 @@
<script>
import { onMount } from 'svelte';
import { t } from '../lib/i18n.js';
let visible = false;
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('about');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const features = [
{
icon: '🌟',
title: 'L\'Étoile la Plus Brillante',
description: 'Sirius brille avec une magnitude de -1.46, dominant notre ciel nocturne.'
},
{
icon: '🌅',
title: 'Le Lever Héliaque',
description: 'Sa première apparition à l\'aube après 70 jours d\'invisibilité marque un événement cosmique.'
},
{
icon: '🗿',
title: 'Héritage Ancestral',
description: 'De l\'Égypte ancienne aux Dogons du Mali, Sirius guide l\'humanité depuis des millénaires.'
}
];
</script>
<section id="about" class="about">
<div class="pattern-kente"></div>
<div class="container">
<div class="section-header" class:visible>
<h2>Sirius et Wep Ronpet</h2>
<p class="section-subtitle">
L'Ouverture de l'Année - Le Nouvel An Cosmique des Anciens Égyptiens
</p>
</div>
<div class="about-content" class:visible>
<div class="about-text">
<p>
<strong>Wep Ronpet</strong> - "l'Ouverture de l'Année" en égyptien ancien -
marquait le jour le plus sacré du calendrier égyptien. Ce jour correspondait
au lever héliaque de Sirius, quand l'étoile réapparaissait à l'aube après
70 jours d'invisibilité.
</p>
<p>
Le lever héliaque de Sirius n'est pas qu'un simple phénomène astronomique.
C'est un rendez-vous millénaire entre l'humanité et le cosmos, un moment où
science et spiritualité se rencontrent dans la lumière de l'aube.
</p>
<p>
En Guadeloupe, observer ce phénomène nous connecte directement aux racines
africaines, où Sirius était vénérée comme l'annonciatrice de la vie et du renouveau.
</p>
</div>
<div class="features-grid">
{#each features as feature, i}
<div class="feature-card card" style="animation-delay: {i * 0.2}s">
<div class="feature-icon">{feature.icon}</div>
<h3>{$t(feature.title)}</h3>
<p>{$t(feature.description)}</p>
</div>
{/each}
</div>
</div>
</div>
</section>
<style>
.about {
position: relative;
padding: var(--spacing-xl) 0;
overflow: hidden;
}
.pattern-kente {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.05;
z-index: 0;
}
.section-header {
text-align: center;
margin-bottom: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.section-header.visible {
opacity: 1;
transform: translateY(0);
}
.section-subtitle {
font-size: 1.5rem;
color: var(--color-primary);
font-family: var(--font-heading);
}
.about-content {
position: relative;
z-index: 1;
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.about-content.visible {
opacity: 1;
transform: translateY(0);
}
.about-text {
max-width: 800px;
margin: 0 auto var(--spacing-lg);
text-align: center;
font-size: 1.1rem;
line-height: 1.8;
}
.about-text p {
margin-bottom: 1rem;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-md);
margin-top: var(--spacing-lg);
}
.feature-card {
text-align: center;
opacity: 0;
animation: fadeInUp 0.8s ease-out forwards;
}
.feature-icon {
font-size: 3rem;
margin-bottom: 1rem;
filter: drop-shadow(0 0 20px rgba(255, 215, 0, 0.5));
}
.feature-card h3 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.features-grid {
grid-template-columns: 1fr;
}
}
</style>
-230
View File
@@ -1,230 +0,0 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let selectedCalculation = 'heliacal';
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('calculations');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const calculations = {
heliacal: {
title: 'Lever Héliaque',
description: 'Calcul de la première visibilité de Sirius avant l\'aube'
},
arcus: {
title: 'Arcus Visionis',
description: 'Angle minimal entre le Soleil et Sirius pour la visibilité'
}
};
</script>
<section id="calculations" class="calculations-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Calculs Astronomiques</h2>
<p class="section-subtitle">
La science précise derrière le lever héliaque
</p>
</div>
<div class="calculations-wrapper" class:visible>
<!-- Sélecteur de calcul -->
<div class="calc-selector">
{#each Object.entries(calculations) as [key, calc]}
<button
class="calc-btn"
class:active={selectedCalculation === key}
on:click={() => selectedCalculation = key}
>
<h4>{calc.title}</h4>
<p>{calc.description}</p>
</button>
{/each}
</div>
<!-- Contenu des calculs -->
<div class="calc-content">
{#if selectedCalculation === 'heliacal'}
<div class="calc-panel">
<h3>Calcul du Lever Héliaque de Sirius</h3>
<div class="formula-box">
<h4>Formule Principale</h4>
<div class="formula">
<p><strong>Date du lever héliaque = </strong></p>
<p>Date de conjonction + Arcus Visionis / Vitesse angulaire du Soleil</p>
</div>
</div>
<div class="calculation-steps">
<h4>Étapes du Calcul pour la Guadeloupe</h4>
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h5>Coordonnées de la Guadeloupe</h5>
<code>Latitude: 16.25°N, Longitude: 61.58°O</code>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h5>Position de Sirius</h5>
<code>Ascension droite: 6h 45m 09s</code>
<code>Déclinaison: -16° 42' 58"</code>
</div>
</div>
</div>
</div> {/if}
{#if selectedCalculation === 'arcus'}
<div class="calc-panel">
<h3>Arcus Visionis - L'Arc de Vision</h3>
<div class="explanation">
<p>
L'Arcus Visionis est l'angle minimal entre le Soleil et une étoile
pour que celle-ci devienne visible.
</p>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.calculations-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.calculations-wrapper {
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.calculations-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
.calc-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.calc-btn {
background: rgba(255, 255, 255, 0.05);
border: 2px solid transparent;
border-radius: 15px;
padding: 1.5rem;
cursor: pointer;
transition: all 0.3s ease;
text-align: left;
}
.calc-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--color-primary);
transform: translateY(-2px);
}
.calc-btn.active {
background: rgba(255, 215, 0, 0.1);
border-color: var(--color-primary);
}
.calc-btn h4 {
color: var(--color-primary);
margin: 0 0 0.5rem 0;
}
.calc-btn p {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
.calc-panel {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
}
.formula-box {
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.formula {
font-family: 'Courier New', monospace;
color: var(--color-light);
margin: 1rem 0;
}
.calculation-steps {
margin: 2rem 0;
}
.step {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
align-items: flex-start;
}
.step-number {
background: var(--color-primary);
color: var(--color-dark);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
}
.step-content h5 {
color: var(--color-secondary);
margin: 0 0 0.5rem 0;
}
.step-content code {
display: block;
background: rgba(0, 0, 0, 0.3);
padding: 0.5rem 1rem;
border-radius: 8px;
margin: 0.25rem 0;
font-family: 'Courier New', monospace;
}
@media (max-width: 768px) {
.calc-selector {
grid-template-columns: 1fr;
}
.calc-panel {
padding: 1rem;
}
}
</style>
-252
View File
@@ -1,252 +0,0 @@
<script>
import { onMount } from 'svelte';
let visible = false;
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('culture');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const cultures = [
{
id: 'egypt',
title: 'Égypte : Sothis, la Déesse du Nil',
icon: '🏺',
content: `Pour les anciens Égyptiens, le lever héliaque de Sirius (Sothis)
était l'événement le plus important de l'année. Il annonçait la crue du Nil,
source de vie et de fertilité. Ce "retour" de l'étoile marquait le Nouvel An
et symbolisait la renaissance, associée à la déesse Isis.`
},
{
id: 'dogon',
title: 'Dogon : Les Gardiens du Savoir Stellaire',
icon: '🌍',
content: `Le peuple Dogon du Mali possède un savoir stupéfiant sur Sirius.
Ils connaissaient l'existence de Sirius B, une naine blanche invisible à l'œil nu,
bien avant sa découverte scientifique. Leur cycle rituel de 60 ans, le Sigui,
est directement lié à leur compréhension du système de Sirius.`
},
{
id: 'caribbean',
title: 'Caraïbes : La Navigation Stellaire',
icon: '⛵',
content: `Dans les Caraïbes, Sirius a guidé les navigateurs depuis des siècles.
Les peuples autochtones et les marins africains utilisaient son lever pour
marquer les saisons et naviguer entre les îles. Aujourd'hui encore, elle reste
un repère fondamental pour les pêcheurs traditionnels.`
}
];
</script>
<section id="culture" class="culture">
<div class="pattern-adinkra"></div>
<div class="container">
<div class="section-header" class:visible>
<h2>Sirius, Miroir des Cultures</h2>
<p class="section-subtitle">
Un héritage millénaire qui unit l'Afrique et les Caraïbes
</p>
</div>
<div class="cultures-grid" class:visible>
{#each cultures as culture, i}
<div class="culture-card card" style="animation-delay: {i * 0.2}s">
<div class="culture-icon">{culture.icon}</div>
<h3>{culture.title}</h3>
<p>{culture.content}</p>
</div>
{/each}
</div>
<div class="timeline" class:visible>
<h3>Chronologie du Savoir</h3>
<div class="timeline-items">
<div class="timeline-item">
<div class="timeline-date">3000 BCE</div>
<div class="timeline-content">
<h4>Égypte Ancienne</h4>
<p>Premier calendrier basé sur Sirius</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">XIIIe siècle</div>
<div class="timeline-content">
<h4>Migration Dogon</h4>
<p>Préservation du savoir astronomique</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">XVe-XVIIe siècle</div>
<div class="timeline-content">
<h4>Traversée Atlantique</h4>
<p>Le savoir africain voyage vers les Caraïbes</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">2025</div>
<div class="timeline-content">
<h4>Renaissance</h4>
<p>Redécouverte et célébration en Guadeloupe</p>
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.culture {
position: relative;
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.pattern-adinkra {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.cultures-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-md);
margin-bottom: var(--spacing-xl);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.cultures-grid.visible {
opacity: 1;
transform: translateY(0);
}
.culture-card {
text-align: center;
opacity: 0;
animation: fadeInUp 0.8s ease-out forwards;
}
.culture-icon {
font-size: 4rem;
margin-bottom: 1rem;
filter: drop-shadow(0 0 20px rgba(255, 215, 0, 0.5));
}
.culture-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.timeline {
position: relative;
max-width: 800px;
margin: 0 auto;
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.6s;
}
.timeline.visible {
opacity: 1;
transform: translateY(0);
}
.timeline h3 {
text-align: center;
color: var(--color-primary);
font-size: 2rem;
margin-bottom: var(--spacing-lg);
}
.timeline-items {
position: relative;
padding-left: 40px;
}
.timeline-items::before {
content: '';
position: absolute;
left: 10px;
top: 0;
bottom: 0;
width: 2px;
background: var(--gradient-sunrise);
}
.timeline-item {
position: relative;
margin-bottom: var(--spacing-md);
display: flex;
gap: var(--spacing-md);
}
.timeline-item::before {
content: '';
position: absolute;
left: -34px;
top: 8px;
width: 16px;
height: 16px;
background: var(--color-primary);
border-radius: 50%;
box-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
}
.timeline-date {
font-weight: 600;
color: var(--color-primary);
min-width: 100px;
}
.timeline-content h4 {
color: var(--color-light);
margin-bottom: 0.25rem;
}
.timeline-content p {
color: rgba(255, 255, 255, 0.7);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.timeline-items {
padding-left: 20px;
}
.timeline-item {
flex-direction: column;
gap: 0.5rem;
}
.timeline-item::before {
left: -24px;
}
}
</style>
-587
View File
@@ -1,587 +0,0 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let activeTab = 'cosmogonie';
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('dogon');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const tabs = [
{ id: 'cosmogonie', label: 'Cosmogonie', icon: '🌌' },
{ id: 'sirius', label: 'Système de Sirius', icon: '⭐' },
{ id: 'astronomie', label: 'Astronomie Dogon', icon: '🔭' },
{ id: 'heritage', label: 'Héritage', icon: '🌍' }
];
</script>
<section id="dogon" class="dogon-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Les Dogons et Sirius</h2>
<p class="section-subtitle">
Une connaissance astronomique millénaire
</p>
</div>
<div class="content-wrapper" class:visible>
<!-- Navigation par onglets -->
<div class="tabs-nav">
{#each tabs as tab}
<button
class="tab-btn"
class:active={activeTab === tab.id}
on:click={() => activeTab = tab.id}
>
<span class="tab-icon">{tab.icon}</span>
<span class="tab-label">{tab.label}</span>
</button>
{/each}
</div>
<!-- Contenu des onglets -->
<div class="tabs-content">
{#if activeTab === 'cosmogonie'}
<div class="tab-panel">
<h3>La Cosmogonie Dogon</h3>
<div class="content-grid">
<div class="content-text">
<p>
Les Dogons du Mali possèdent une cosmogonie complexe centrée sur <strong>Sigu Tolo</strong> (Sirius),
l'étoile la plus brillante du ciel. Leur savoir astronomique, transmis oralement depuis des millénaires,
révèle une compréhension sophistiquée du cosmos.
</p>
<p>
Le <strong>Sigui</strong>, cérémonie célébrée tous les 60 ans, marque le renouvellement du monde
et commémore la révélation de la parole aux hommes. Cette périodicité correspond étrangement
à l'orbite de Sirius B autour de Sirius A (50 ans), ajustée par des considérations rituelles.
</p>
<div class="highlight-box">
<h4>Le Nommo</h4>
<p>
Être primordial amphibie venu de Sirius, le Nommo apporta la civilisation
et les connaissances fondamentales aux ancêtres des Dogons.
</p>
</div>
</div>
<div class="content-visual">
<div class="cosmogony-diagram">
<svg viewBox="0 0 400 400" class="diagram-svg">
<!-- Amma (Dieu créateur) au centre -->
<circle cx="200" cy="200" r="30" fill="var(--color-primary)" opacity="0.8"/>
<text x="200" y="205" text-anchor="middle" fill="white" font-weight="bold">Amma</text>
<!-- Oeuf cosmique -->
<ellipse cx="200" cy="200" rx="150" ry="100" fill="none" stroke="var(--color-secondary)" stroke-width="2" stroke-dasharray="5,5" opacity="0.6"/>
<!-- Les 4 éléments -->
<g class="elements">
<circle cx="200" cy="100" r="25" fill="var(--color-accent)"/>
<text x="200" y="105" text-anchor="middle" fill="white" font-size="12">Eau</text>
<circle cx="300" cy="200" r="25" fill="#FF6B35"/>
<text x="300" y="205" text-anchor="middle" fill="white" font-size="12">Feu</text>
<circle cx="200" cy="300" r="25" fill="#4CAF50"/>
<text x="200" y="305" text-anchor="middle" fill="white" font-size="12">Terre</text>
<circle cx="100" cy="200" r="25" fill="#87CEEB"/>
<text x="100" y="205" text-anchor="middle" fill="white" font-size="12">Air</text>
</g>
<!-- Lignes de connexion -->
<line x1="200" y1="230" x2="200" y2="275" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="225" y1="200" x2="275" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="200" y1="175" x2="200" y2="125" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
<line x1="175" y1="200" x2="125" y2="200" stroke="var(--color-light)" stroke-width="1" opacity="0.5"/>
</svg>
</div>
</div>
</div>
</div>
{/if}
{#if activeTab === 'sirius'}
<div class="tab-panel">
<h3>Le Système de Sirius selon les Dogons</h3>
<div class="sirius-system">
<div class="star-info">
<h4>Sigu Tolo (Sirius A)</h4>
<p>L'étoile principale, source de lumière et de vie</p>
<ul>
<li>Associée à la féminité et à la fécondité</li>
<li>Guide des saisons et des récoltes</li>
<li>Centre du système stellaire</li>
</ul>
</div>
<div class="star-info">
<h4>Po Tolo (Sirius B)</h4>
<p>L'étoile compagne invisible à l'œil nu</p>
<ul>
<li>Décrite comme "lourde" et "blanche"</li>
<li>Orbite de 50 ans autour de Sirius A</li>
<li>Découverte par les astronomes occidentaux en 1862</li>
</ul>
</div>
<div class="star-info">
<h4>Emme Ya Tolo (Sirius C ?)</h4>
<p>Troisième étoile hypothétique du système</p>
<ul>
<li>Quatre fois plus légère que Po Tolo</li>
<li>Orbite plus large</li>
<li>Non confirmée par l'astronomie moderne</li>
</ul>
</div>
</div>
<div class="knowledge-mystery">
<h4>Le Mystère de la Connaissance</h4>
<p>
Comment les Dogons connaissaient-ils l'existence de Sirius B,
invisible à l'œil nu et découverte en Occident seulement en 1862 ?
</p>
<div class="theories">
<div class="theory-card">
<h5>Transmission Ancienne</h5>
<p>Héritage d'une civilisation antérieure avancée</p>
</div>
<div class="theory-card">
<h5>Contact Extraterrestre</h5>
<p>Les Nommos venus de Sirius</p>
</div>
<div class="theory-card">
<h5>Observation Minutieuse</h5>
<p>Déduction à partir des perturbations de Sirius A</p>
</div>
</div>
</div>
</div>
{/if}
{#if activeTab === 'astronomie'}
<div class="tab-panel">
<h3>L'Astronomie Dogon</h3>
<div class="astronomy-content">
<div class="concept-grid">
<div class="concept-card">
<h4>📐 Géométrie Sacrée</h4>
<p>
Les Dogons utilisent des figures géométriques complexes pour
représenter les mouvements célestes et les cycles cosmiques.
</p>
</div>
<div class="concept-card">
<h4>🌙 Calendrier Lunaire</h4>
<p>
Un système calendaire sophistiqué basé sur les phases lunaires
et la position de Sirius pour l'agriculture.
</p>
</div>
<div class="concept-card">
<h4>🪐 Connaissance Planétaire</h4>
<p>
Description des anneaux de Saturne et des lunes de Jupiter,
invisibles sans télescope.
</p>
</div>
<div class="concept-card">
<h4>🌌 Structure de l'Univers</h4>
<p>
Concept d'univers infini avec des mondes multiples,
similaire aux théories modernes.
</p>
</div>
</div>
<div class="calculation-example">
<h4>Calcul de la Période Sigui</h4>
<pre><code>Période orbitale Po Tolo : 50 ans
Ajustement rituel : × 1.2
Période Sigui : 60 ans
Correspondance avec Sirius B :
- Orbite réelle : 50.1 ans
- Précision : 99.8%</code></pre>
</div>
</div>
</div>
{/if}
{#if activeTab === 'heritage'}
<div class="tab-panel">
<h3>Héritage et Influence</h3>
<div class="heritage-content">
<div class="timeline">
<div class="timeline-item">
<div class="timeline-date">Antiquité</div>
<div class="timeline-content">
<h4>Origines Égyptiennes</h4>
<p>Liens possibles avec les prêtres de l'Égypte ancienne qui vénéraient Sothis (Sirius)</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">XIIIe siècle</div>
<div class="timeline-content">
<h4>Migration vers le Mali</h4>
<p>Installation dans les falaises de Bandiagara, préservation des traditions</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">1931-1956</div>
<div class="timeline-content">
<h4>Marcel Griaule</h4>
<p>Étude ethnographique révélant les connaissances astronomiques dogons</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-date">Aujourd'hui</div>
<div class="timeline-content">
<h4>Patrimoine Mondial</h4>
<p>Les Dogons inspirent scientifiques et chercheurs du monde entier</p>
</div>
</div>
</div>
<div class="influence-map">
<h4>Influence sur la Caraïbe</h4>
<p>
Les traditions astronomiques africaines, incluant celles des Dogons,
ont traversé l'Atlantique avec la diaspora, enrichissant les pratiques
spirituelles et calendaires des Antilles.
</p>
<ul>
<li>🇭🇹 Haïti : Vaudou et astronomie sacrée</li>
<li>🇬🇵 Guadeloupe : Traditions Gwoka et cycles cosmiques</li>
<li>🇲🇶 Martinique : Bèlè et rythmes stellaires</li>
<li>🇯🇲 Jamaïque : Rastafari et conscience cosmique</li>
</ul>
</div>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.dogon-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-night);
}
.content-wrapper {
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.content-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
/* Onglets */
.tabs-nav {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
justify-content: center;
}
.tab-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.1);
border: 2px solid transparent;
border-radius: 50px;
color: var(--color-light);
cursor: pointer;
transition: all 0.3s ease;
}
.tab-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: var(--color-primary);
}
.tab-btn.active {
background: var(--color-primary);
color: var(--color-dark);
border-color: var(--color-primary);
}
.tab-icon {
font-size: 1.2rem;
}
/* Contenu des onglets */
.tab-panel {
animation: fadeIn 0.5s ease-out;
}
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: start;
}
.highlight-box {
background: rgba(255, 215, 0, 0.1);
border-left: 4px solid var(--color-primary);
padding: 1rem;
margin: 1.5rem 0;
border-radius: 0 8px 8px 0;
}
.highlight-box h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
/* Diagramme cosmogonie */
.cosmogony-diagram {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
}
.diagram-svg {
width: 100%;
height: auto;
}
.elements circle {
animation: pulse 3s ease-in-out infinite;
}
.elements circle:nth-child(2) { animation-delay: 0.5s; }
.elements circle:nth-child(4) { animation-delay: 1s; }
.elements circle:nth-child(6) { animation-delay: 1.5s; }
/* Système de Sirius */
.sirius-system {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.star-info {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.star-info h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.star-info ul {
list-style: none;
padding: 0;
margin-top: 1rem;
}
.star-info li {
padding: 0.25rem 0;
padding-left: 1.5rem;
position: relative;
}
.star-info li::before {
content: "✦";
position: absolute;
left: 0;
color: var(--color-primary);
}
/* Mystère de la connaissance */
.knowledge-mystery {
background: rgba(255, 215, 0, 0.05);
padding: 2rem;
border-radius: 15px;
margin-top: 2rem;
}
.theories {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-top: 1.5rem;
}
.theory-card {
background: rgba(255, 255, 255, 0.1);
padding: 1rem;
border-radius: 10px;
text-align: center;
}
.theory-card h5 {
color: var(--color-secondary);
margin-bottom: 0.5rem;
}
/* Astronomie */
.concept-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.concept-card {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
transition: transform 0.3s ease;
}
.concept-card:hover {
transform: translateY(-5px);
}
.concept-card h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.calculation-example {
background: rgba(0, 0, 0, 0.3);
padding: 1.5rem;
border-radius: 10px;
}
.calculation-example pre {
margin: 0;
color: var(--color-light);
font-family: 'Courier New', monospace;
}
/* Timeline */
.timeline {
position: relative;
padding-left: 3rem;
}
.timeline::before {
content: '';
position: absolute;
left: 1rem;
top: 0;
bottom: 0;
width: 2px;
background: var(--color-primary);
}
.timeline-item {
position: relative;
margin-bottom: 2rem;
}
.timeline-item::before {
content: '';
position: absolute;
left: -2.5rem;
top: 0.5rem;
width: 1rem;
height: 1rem;
background: var(--color-primary);
border-radius: 50%;
}
.timeline-date {
color: var(--color-primary);
font-weight: bold;
margin-bottom: 0.5rem;
}
.timeline-content h4 {
color: var(--color-secondary);
margin-bottom: 0.25rem;
}
/* Carte d'influence */
.influence-map {
background: rgba(255, 255, 255, 0.05);
padding: 1.5rem;
border-radius: 15px;
margin-top: 2rem;
}
.influence-map ul {
list-style: none;
padding: 0;
margin-top: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 0.5rem;
}
.influence-map li {
padding: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.1); opacity: 1; }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.content-grid {
grid-template-columns: 1fr;
}
.tabs-nav {
gap: 0.5rem;
}
.tab-btn {
padding: 0.5rem 1rem;
font-size: 0.9rem;
}
.tab-label {
display: none;
}
.timeline {
padding-left: 2rem;
}
}
</style>
-178
View File
@@ -1,178 +0,0 @@
<script>
import { t } from '../lib/i18n.js';
const currentYear = new Date().getFullYear();
</script>
<footer class="footer">
<div class="footer-pattern pattern-kente"></div>
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3>Sirius en Guadeloupe</h3>
<p>
Un projet célébrant le patrimoine astronomique africain
et caribéen à travers l'observation du lever héliaque de Sirius.
</p>
<div class="social-links">
<a href="#" aria-label="Facebook">📘</a>
<a href="#" aria-label="Twitter">🐦</a>
<a href="#" aria-label="Instagram">📷</a>
</div>
</div>
<div class="footer-section">
<h4>Liens Rapides</h4>
<ul>
<li><a href="#predictions">Calculateur</a></li>
<li><a href="#science">Science</a></li>
<li><a href="#culture">Culture</a></li>
<li><a href="#observatory">Observer</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Ressources</h4>
<ul>
<li><a href="https://stellarium.org" target="_blank" rel="noopener">Stellarium</a></li>
<li><a href="#">Guide PDF</a></li>
<li><a href="#">Références</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Prochaine Observation</h4>
<div class="countdown">
<div class="countdown-item">
<div class="countdown-value">22</div>
<div class="countdown-label">Juillet</div>
</div>
<div class="countdown-item">
<div class="countdown-value">2025</div>
<div class="countdown-label">05:12</div>
</div>
</div>
</div>
</div>
<div class="footer-bottom">
<p>&copy; {currentYear} Sirius Gwada. Levez les yeux vers le ciel.</p>
<p class="footer-credits">
{$t('footer_copyright')}
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
{$t('footer_and')}
<a href="https://o-k-i.net" target="_blank" rel="noopener">
OKI <img src="https://o-k-i.net/assets/img/logo-oki.png" alt="OKI Logo" class="oki-logo">
</a>
</p>
<p class="footer-quote">
"Les étoiles sont les ancêtres qui veillent sur nous" - Proverbe africain
</p>
</div>
</div>
</footer>
<style>
.footer {
position: relative;
background: var(--color-dark);
padding: var(--spacing-xl) 0 var(--spacing-md);
border-top: 2px solid rgba(255, 215, 0, 0.3);
overflow: hidden;
}
.footer-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.03;
z-index: 0;
}
.footer-content {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
.countdown {
display: flex;
gap: 1rem;
}
.countdown-item {
background: rgba(255, 215, 0, 0.1);
padding: 1rem;
border-radius: 10px;
text-align: center;
}
.countdown-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--color-primary);
}
.countdown-label {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
}
.footer-bottom {
text-align: center;
padding-top: var(--spacing-md);
border-top: 1px solid rgba(255, 215, 0, 0.2);
}
.footer-credits {
margin: 0.5rem 0;
color: rgba(255, 255, 255, 0.8);
}
.footer-credits a {
color: var(--color-primary);
text-decoration: none;
transition: opacity 0.3s ease;
}
.footer-credits a:hover {
opacity: 0.8;
}
.oki-logo {
height: 20px;
vertical-align: middle;
margin-left: 5px;
}
.footer-quote {
margin-top: 0.5rem;
font-style: italic;
color: var(--color-primary);
opacity: 0.8;
}
.social-links {
display: flex;
gap: 1rem;
margin-top: 1rem;
font-size: 1.5rem;
}
@media (max-width: 768px) {
.footer-content {
grid-template-columns: 1fr;
text-align: center;
}
.social-links {
justify-content: center;
}
}
</style>
-454
View File
@@ -1,454 +0,0 @@
<script>
import { onMount } from 'svelte';
import { worldData } from '../data/world.js';
let canvas;
let visible = false;
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
if (!canvas.hasAttribute('data-initialized')) {
initGlobe();
canvas.setAttribute('data-initialized', 'true');
}
}
});
});
const section = document.getElementById('globe-section');
if (section) observer.observe(section);
return () => observer.disconnect();
});
function initGlobe() {
const ctx = canvas.getContext('2d');
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = canvas.offsetHeight;
const centerX = width / 2;
const centerY = height / 2;
const radius = Math.min(width, height) * 0.35;
let rotation = 2.5; // Commencer avec l'Atlantique visible
let zoom = 1;
let isDragging = false;
let lastMouseX = 0;
let waveAnimation = 0;
// Données des lieux dans l'ordre chronologique
const locations = [
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, date: '27 Juin', day: 178 },
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, date: '16 Juillet', day: 197 },
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, date: '22 Juillet', day: 203 },
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, date: '23 Juillet', day: 204 },
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, date: '23 Juillet', day: 204 },
{ name: 'Abidjan', lat: 5.3600, lon: -4.0083, date: '28 Juillet', day: 209 },
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, date: '3 Août', day: 215 },
{ name: 'Paris', lat: 48.8566, lon: 2.3522, date: '12 Août', day: 224 }
];
// Gérer le zoom
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
zoom += e.deltaY * -0.001;
zoom = Math.min(Math.max(0.5, zoom), 2);
});
// Gérer la rotation avec la souris
canvas.addEventListener('mousedown', (e) => {
isDragging = true;
lastMouseX = e.clientX;
});
window.addEventListener('mousemove', (e) => {
if (isDragging) {
const deltaX = e.clientX - lastMouseX;
rotation -= deltaX * 0.01; // Inverser pour tourner dans le bon sens
lastMouseX = e.clientX;
}
});
window.addEventListener('mouseup', () => {
isDragging = false;
});
// Support tactile
canvas.addEventListener('touchstart', (e) => {
isDragging = true;
lastMouseX = e.touches[0].clientX;
e.preventDefault();
});
canvas.addEventListener('touchmove', (e) => {
if (isDragging && e.touches.length === 1) {
const deltaX = e.touches[0].clientX - lastMouseX;
rotation -= deltaX * 0.01; // Garder le - pour le tactile aussi
lastMouseX = e.touches[0].clientX;
e.preventDefault();
}
});
canvas.addEventListener('touchend', () => {
isDragging = false;
});
// Dessiner les continents simplifiés
function drawContinents(ctx, centerX, centerY, radius, rotation) {
ctx.save();
ctx.globalAlpha = 0.8; // Plus opaque
ctx.strokeStyle = '#8B7355'; // Marron clair
ctx.fillStyle = '#DEB887'; // Burlywood (beige)
ctx.lineWidth = 0.5;
// Afrique (très simplifiée)
const africa = [
{lat: 37, lon: 10}, {lat: 35, lon: -5}, {lat: 30, lon: -17},
{lat: 15, lon: -17}, {lat: 5, lon: -10}, {lat: -5, lon: 8},
{lat: -15, lon: 15}, {lat: -25, lon: 20}, {lat: -35, lon: 20},
{lat: -35, lon: 30}, {lat: -25, lon: 35}, {lat: -10, lon: 40},
{lat: 5, lon: 45}, {lat: 15, lon: 45}, {lat: 30, lon: 35}, {lat: 37, lon: 10}
];
drawLandmass(africa);
// Amérique du Sud
const southAmerica = [
{lat: 10, lon: -60}, {lat: 5, lon: -50}, {lat: -5, lon: -35},
{lat: -15, lon: -38}, {lat: -25, lon: -48}, {lat: -35, lon: -58},
{lat: -45, lon: -65}, {lat: -55, lon: -70}, {lat: -45, lon: -73},
{lat: -35, lon: -72}, {lat: -25, lon: -70}, {lat: -15, lon: -75},
{lat: -5, lon: -78}, {lat: 5, lon: -77}, {lat: 10, lon: -60}
];
drawLandmass(southAmerica);
// Europe (simplifiée)
const europe = [
{lat: 60, lon: 25}, {lat: 55, lon: 10}, {lat: 50, lon: -5},
{lat: 45, lon: -5}, {lat: 40, lon: -10}, {lat: 36, lon: -6},
{lat: 40, lon: 0}, {lat: 45, lon: 10}, {lat: 50, lon: 20},
{lat: 55, lon: 30}, {lat: 60, lon: 25}
];
drawLandmass(europe);
function drawLandmass(points) {
const coords = points.map(p => {
const phi = (90 - p.lat) * Math.PI / 180;
const theta = (p.lon - rotation * 57.3) * Math.PI / 180;
const x = radius * Math.sin(phi) * Math.cos(theta);
const y = radius * Math.cos(phi);
const z = radius * Math.sin(phi) * Math.sin(theta);
return { x: centerX + x, y: centerY - y, z: z };
});
// Ne dessiner que si au moins un point est visible
if (coords.some(c => c.z > -radius * 0.5)) {
ctx.beginPath();
coords.forEach((c, i) => {
if (i === 0) ctx.moveTo(c.x, c.y);
else ctx.lineTo(c.x, c.y);
});
ctx.closePath();
if (coords[0].z > 0) {
ctx.fill();
}
ctx.stroke();
}
}
ctx.restore();
}
function draw() {
ctx.clearRect(0, 0, width, height);
const currentRadius = radius * zoom;
// Océan (fond du globe)
const oceanGradient = ctx.createRadialGradient(
centerX - currentRadius * 0.3,
centerY - currentRadius * 0.3,
0,
centerX,
centerY,
currentRadius
); oceanGradient.addColorStop(0, '#87CEEB'); // Sky blue clair
oceanGradient.addColorStop(0.7, '#5F9EA0'); // Cadet blue
oceanGradient.addColorStop(1, '#4682B4'); // Steel blue
ctx.beginPath();
ctx.arc(centerX, centerY, currentRadius, 0, Math.PI * 2);
ctx.fillStyle = oceanGradient;
ctx.fill();
// Grille de méridiens et parallèles
ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)'; // Noir transparent
ctx.lineWidth = 0.5;
// Méridiens (longitudes)
for (let lon = -180; lon <= 180; lon += 30) {
ctx.beginPath();
for (let lat = -90; lat <= 90; lat += 5) {
const phi = (90 - lat) * Math.PI / 180;
const theta = (lon - rotation * 57.3) * Math.PI / 180;
const x = currentRadius * Math.sin(phi) * Math.cos(theta);
const y = currentRadius * Math.cos(phi);
const z = currentRadius * Math.sin(phi) * Math.sin(theta);
if (z > 0) {
const screenX = centerX + x;
const screenY = centerY - y;
if (lat === -90) ctx.moveTo(screenX, screenY);
else ctx.lineTo(screenX, screenY);
}
}
ctx.stroke();
}
// Parallèles (latitudes)
for (let lat = -60; lat <= 60; lat += 30) {
ctx.beginPath();
for (let lon = -180; lon <= 180; lon += 5) {
const phi = (90 - lat) * Math.PI / 180;
const theta = (lon - rotation * 57.3) * Math.PI / 180;
const x = currentRadius * Math.sin(phi) * Math.cos(theta);
const y = currentRadius * Math.cos(phi);
const z = currentRadius * Math.sin(phi) * Math.sin(theta);
if (z > 0) {
const screenX = centerX + x;
const screenY = centerY - y;
if (lon === -180) ctx.moveTo(screenX, screenY);
else ctx.lineTo(screenX, screenY);
}
}
ctx.stroke();
}
// Dessiner les continents
drawContinents(ctx, centerX, centerY, currentRadius, rotation);
// Bordure du globe
ctx.beginPath();
ctx.arc(centerX, centerY, currentRadius, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; // Noir semi-transparent
ctx.lineWidth = 2;
ctx.stroke();
// Dessiner la vague de progression
waveAnimation += 0.02;
const waveDay = ((Math.sin(waveAnimation) + 1) / 2) * 46 + 178; // Entre jour 178 et 224
// Dessiner les points et la vague
locations.forEach((loc, i) => {
const phi = (90 - loc.lat) * Math.PI / 180;
const theta = (-loc.lon - rotation * 57.3) * Math.PI / 180; // Remettre le - devant loc.lon
const x = currentRadius * Math.sin(phi) * Math.cos(theta);
const y = currentRadius * Math.cos(phi);
const z = currentRadius * Math.sin(phi) * Math.sin(theta);
if (z > -currentRadius * 0.3) {
const screenX = centerX + x;
const screenY = centerY - y;
// Calculer l'état selon la vague
const isActive = waveDay >= loc.day;
const isNear = Math.abs(waveDay - loc.day) < 5;
// Effet de vague
if (isNear && z > 0) {
const waveSize = 40 * (1 - Math.abs(waveDay - loc.day) / 5);
const waveGradient = ctx.createRadialGradient(screenX, screenY, 0, screenX, screenY, waveSize);
waveGradient.addColorStop(0, 'rgba(255, 215, 0, 0.8)');
waveGradient.addColorStop(0.5, 'rgba(255, 215, 0, 0.3)');
waveGradient.addColorStop(1, 'rgba(255, 215, 0, 0)');
ctx.fillStyle = waveGradient;
ctx.fillRect(screenX - waveSize, screenY - waveSize, waveSize * 2, waveSize * 2);
}
// Point
const opacity = z > 0 ? 1 : 0.3;
ctx.globalAlpha = opacity;
ctx.beginPath();
ctx.arc(screenX, screenY, isActive ? 6 : 4, 0, Math.PI * 2);
ctx.fillStyle = isActive ? '#FFD700' : '#666666';
ctx.fill();
ctx.strokeStyle = isActive ? '#FFFFFF' : '#999999';
ctx.lineWidth = 2;
ctx.stroke();
// Label
if (z > 0) {
ctx.fillStyle = isActive ? '#FFD700' : '#999999';
ctx.font = `${12 * zoom}px Inter`;
ctx.textAlign = 'center';
ctx.fillText(loc.name, screenX, screenY - 12);
ctx.font = `${10 * zoom}px Inter`;
ctx.fillText(loc.date, screenX, screenY + 22);
}
ctx.globalAlpha = 1;
}
});
// Légende
ctx.fillStyle = '#FFD700';
ctx.font = '16px Inter';
ctx.textAlign = 'left';
ctx.fillText('🌟 Vague du Lever Héliaque de Sirius', 20, 30); ctx.font = '12px Inter';
ctx.fillStyle = '#CCCCCC';
const currentDate = locations.find(l => Math.abs(l.day - waveDay) < 2);
if (currentDate) {
ctx.fillText(`Animation : ${currentDate.name} - ${currentDate.date}`, 20, 50);
}
// Rotation automatique lente
if (!isDragging) {
rotation += 0.002;
}
requestAnimationFrame(draw);
}
draw();
}
</script>
<section id="globe-section" class="globe-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Globe Original (Canvas simple)</h2>
<p class="section-subtitle">
Version actuelle avec formes simplifiées
</p>
</div>
<div class="globe-container" class:visible>
<canvas bind:this={canvas} class="globe-canvas"></canvas>
<div class="globe-controls">
<p>🖱️ Glisser pour tourner | 🔍 Molette pour zoomer</p>
</div>
<div class="globe-info">
<div class="info-card card">
<h3>Le Phénomène Global</h3>
<p>
Le lever héliaque de Sirius n'est pas simultané sur Terre.
Il progresse comme une vague dorée, commençant dans l'océan Indien
(La Réunion en juin) pour traverser l'Afrique, les Caraïbes,
puis remonter vers l'Europe (Paris en août).
</p>
<p>
Cette progression suit la géométrie céleste : plus on va vers le nord,
plus le phénomène se produit tard. La Guadeloupe se trouve au cœur
de cette vague en juillet, bénéficiant de conditions idéales.
</p>
<div class="timeline-mini">
<div class="timeline-item-mini">
<span class="date">27 Juin</span>
<span class="location">La Réunion</span>
</div>
<div class="timeline-item-mini">
<span class="date">22-23 Juillet</span>
<span class="location">Guadeloupe & Caraïbes</span>
</div>
<div class="timeline-item-mini">
<span class="date">12 Août</span>
<span class="location">Paris</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.globe-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.globe-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.globe-container.visible {
opacity: 1;
transform: translateY(0);
}
.globe-canvas {
width: 100%;
height: 500px;
border-radius: 20px;
background: radial-gradient(circle at center, #0a192f 0%, #020c1b 100%);
cursor: grab;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
}
.globe-canvas:active {
cursor: grabbing;
}
.globe-controls {
text-align: center;
margin-top: 0.5rem;
font-size: 0.9rem;
color: var(--color-primary);
opacity: 0.7;
}
.info-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.info-card p {
margin-bottom: 1rem;
line-height: 1.8;
}
.timeline-mini {
margin-top: 1.5rem;
padding: 1rem;
background: rgba(255, 215, 0, 0.1);
border-radius: 10px;
}
.timeline-item-mini {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px solid rgba(255, 215, 0, 0.2);
}
.timeline-item-mini:last-child {
border-bottom: none;
}
.timeline-item-mini .date {
font-weight: 600;
color: var(--color-primary);
}
.timeline-item-mini .location {
color: var(--color-light);
}
@media (max-width: 768px) {
.globe-container {
grid-template-columns: 1fr;
}
.globe-canvas {
height: 400px;
}
}
</style>
-539
View File
@@ -1,539 +0,0 @@
<script>
import { onMount } from 'svelte';
import anime from '../lib/anime.js';
let canvas;
let visible = false;
let ctx;
let animationId;
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !ctx) {
visible = true;
initGlobe();
}
});
});
const section = document.getElementById('geojson-section');
if (section) observer.observe(section);
return () => {
observer.disconnect();
if (animationId) cancelAnimationFrame(animationId);
};
});
async function initGlobe() {
ctx = canvas.getContext('2d');
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = canvas.offsetHeight;
const centerX = width / 2;
const centerY = height / 2;
const radius = Math.min(width, height) * 0.35;
let rotation = 2.5;
let zoom = 1;
let isDragging = false;
let lastMouseX = 0;
let waveAnimation = 0;
let waveProgress = 0;
// Animation d'entrée
anime.timeline({
easing: 'easeOutQuad'
})
.add({
targets: canvas,
scale: [0.8, 1],
opacity: [0, 1],
duration: 1200
})
.add({
targets: '.globe-info',
translateX: [50, 0],
opacity: [0, 1],
duration: 1000
}, '-=600');
// Charger les données GeoJSON des continents
let worldData = null;
try {
const response = await fetch('https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json');
worldData = await response.json();
} catch (error) {
console.error('Erreur chargement GeoJSON:', error);
}
// Données des lieux avec animation de vague (juin à septembre)
const locations = [
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, date: '27 Juin', color: '#FF6B35', day: 0 },
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, date: '16 Juillet', color: '#FFA500', day: 19 },
{ name: 'Douala', lat: 4.0511, lon: 9.7679, date: '18 Juillet', color: '#FFA500', day: 21 },
{ name: 'Yaoundé', lat: 3.8480, lon: 11.5021, date: '19 Juillet', color: '#FFA500', day: 22 },
{ name: 'Abidjan', lat: 5.3600, lon: -4.0083, date: '20 Juillet', color: '#FFD700', day: 23 },
{ name: 'Accra', lat: 5.6037, lon: -0.1870, date: '21 Juillet', color: '#FFD700', day: 24 },
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, date: '22 Juillet', color: '#FFD700', day: 25 },
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, date: '23 Juillet', color: '#FFD700', day: 26 },
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, date: '23 Juillet', color: '#FFE44D', day: 26 },
{ name: 'Cayenne', lat: 4.9346, lon: -52.3281, date: '24 Juillet', color: '#FFE44D', day: 27 },
{ name: 'Bamako', lat: 12.6392, lon: -8.0029, date: '25 Juillet', color: '#FFE44D', day: 28 },
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, date: '3 Août', color: '#FFFACD', day: 37 },
{ name: 'Paris', lat: 48.8566, lon: 2.3522, date: '15 Août', color: '#F0F8FF', day: 49 }
];
// Animation de la progression de la vague (juin à septembre)
anime({
targets: { progress: 0 },
progress: 92, // Juin (jour 152) à septembre (jour 244) = 92 jours
duration: 12000, // 12 secondes pour 4 mois
easing: 'linear',
loop: true,
update: function(anim) {
waveProgress = anim.animations[0].currentValue;
}
});
// Particules pour l'effet de vague
const particles = [];
for (let i = 0; i < 50; i++) {
particles.push({
angle: Math.random() * Math.PI * 2,
radius: radius + Math.random() * 50,
size: Math.random() * 3 + 1,
speed: Math.random() * 0.02 + 0.01,
opacity: 0
});
}
// Animation des particules
particles.forEach((particle, i) => {
anime({
targets: particle,
opacity: [0, 0.8, 0],
radius: [radius, radius + 100],
duration: 3000,
delay: i * 60,
easing: 'easeOutQuad',
loop: true
});
});
// Gestion de la souris
canvas.addEventListener('mousedown', (e) => {
isDragging = true;
lastMouseX = e.clientX;
canvas.style.cursor = 'grabbing';
});
canvas.addEventListener('mousemove', (e) => {
if (isDragging) {
const deltaX = e.clientX - lastMouseX;
rotation -= deltaX * 0.01;
lastMouseX = e.clientX;
}
});
window.addEventListener('mouseup', () => {
isDragging = false;
canvas.style.cursor = 'grab';
});
// Zoom avec la molette
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const zoomDelta = e.deltaY > 0 ? 0.9 : 1.1;
const newZoom = zoom * zoomDelta;
anime({
targets: { zoom },
zoom: Math.max(0.5, Math.min(3, newZoom)),
duration: 300,
easing: 'easeOutQuad',
update: function(anim) {
zoom = anim.animations[0].currentValue;
}
});
});
// Projection 3D (lon/lat to screen coordinates)
function project(lon, lat, r) {
const phi = (90 - lat) * Math.PI / 180;
const theta = lon * Math.PI / 180 - rotation;
const x = r * Math.sin(phi) * Math.cos(theta);
const y = r * Math.cos(phi);
const z = r * Math.sin(phi) * Math.sin(theta);
return {
x: centerX - x,
y: centerY - y,
z: z,
visible: z > 0
};
}
// Dessiner le globe
function draw() {
ctx.clearRect(0, 0, width, height);
// Calculer le rayon actuel avec zoom
const currentRadius = radius * zoom;
// Fond avec dégradé
const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, currentRadius);
gradient.addColorStop(0, 'rgba(10, 22, 40, 0.2)');
gradient.addColorStop(1, 'rgba(10, 22, 40, 0.8)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Ombre du globe
ctx.beginPath();
ctx.arc(centerX + 10, centerY + 10, currentRadius, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
ctx.fill();
// Globe principal
ctx.beginPath();
ctx.arc(centerX, centerY, currentRadius, 0, Math.PI * 2);
const globeGradient = ctx.createRadialGradient(
centerX - radius * 0.3 * zoom,
centerY - radius * 0.3 * zoom,
0,
centerX, centerY, currentRadius
);
globeGradient.addColorStop(0, '#1a3a5a');
globeGradient.addColorStop(0.5, '#0f2847');
globeGradient.addColorStop(1, '#0a1628');
ctx.fillStyle = globeGradient;
ctx.fill();
// Grille de latitude/longitude
ctx.strokeStyle = 'rgba(255, 215, 0, 0.1)';
ctx.lineWidth = 1;
// Lignes de latitude
for (let lat = -80; lat <= 80; lat += 20) {
ctx.beginPath();
for (let lon = -180; lon <= 180; lon += 5) {
const p = project(lon, lat, currentRadius);
if (p.visible) {
if (lon === -180) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
}
}
ctx.stroke();
}
// Lignes de longitude
for (let lon = -180; lon <= 180; lon += 30) {
ctx.beginPath();
for (let lat = -90; lat <= 90; lat += 5) {
const p = project(lon, lat, currentRadius);
if (p.visible) {
if (lat === -90) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
}
}
ctx.stroke();
}
// Dessiner les continents
if (worldData) {
ctx.save();
ctx.strokeStyle = '#4CAF50';
ctx.fillStyle = 'rgba(76, 175, 80, 0.3)';
ctx.lineWidth = 2;
worldData.features.forEach(feature => {
if (feature.geometry.type === 'Polygon') {
drawPolygon(feature.geometry.coordinates[0]);
} else if (feature.geometry.type === 'MultiPolygon') {
feature.geometry.coordinates.forEach(polygon => {
drawPolygon(polygon[0]);
});
}
});
ctx.restore();
}
// Dessiner les particules de la vague
particles.forEach(particle => {
const p = project(
particle.angle * 180 / Math.PI - 180,
Math.sin(Date.now() * 0.001 * particle.speed) * 30,
currentRadius + (particle.radius - radius)
);
if (p.visible && particle.opacity > 0) {
ctx.beginPath();
ctx.arc(p.x, p.y, particle.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 215, 0, ${particle.opacity})`;
ctx.fill();
}
});
// Dessiner les marqueurs avec effet de pulsation
locations.forEach((location, index) => {
const p = project(location.lon, location.lat, currentRadius);
if (p.visible) {
// Vérifier si la vague est passée
const isActive = waveProgress >= location.day && waveProgress <= location.day + 10;
const scale = isActive ? 1 + Math.sin(Date.now() * 0.005) * 0.3 : 1;
// Cercle de base
ctx.beginPath();
ctx.arc(p.x, p.y, 8 * scale, 0, Math.PI * 2);
ctx.fillStyle = location.color;
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// Onde de propagation si actif
if (isActive) {
const waveRadius = 20 + (Date.now() * 0.02) % 30;
const waveOpacity = 1 - (waveRadius - 20) / 30;
ctx.beginPath();
ctx.arc(p.x, p.y, waveRadius, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(255, 215, 0, ${waveOpacity * 0.5})`;
ctx.lineWidth = 2;
ctx.stroke();
}
// Label
ctx.fillStyle = '#fff';
ctx.font = 'bold 12px Inter';
ctx.textAlign = 'center';
ctx.fillText(location.name, p.x, p.y - 15);
// Date si proche
if (Math.abs(waveProgress - location.day) < 20) {
ctx.font = '10px Inter';
ctx.fillStyle = location.color;
ctx.fillText(location.date, p.x, p.y + 25);
}
}
});
// Indicateur de progression de la vague
const startDate = new Date(2025, 5, 27); // 27 juin 2025
const currentDate = new Date(startDate);
currentDate.setDate(currentDate.getDate() + Math.floor(waveProgress));
ctx.fillStyle = 'rgba(255, 215, 0, 0.8)';
ctx.font = 'bold 16px Inter';
ctx.textAlign = 'center';
ctx.fillText(
`Progression de la vague : ${currentDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long' })}`,
centerX, height - 30
);
// Rotation automatique
if (!isDragging) {
rotation += 0.002;
}
animationId = requestAnimationFrame(draw);
}
function drawPolygon(coordinates) {
ctx.beginPath();
let firstPoint = true;
const currentRadius = radius * zoom;
coordinates.forEach(coord => {
const p = project(coord[0], coord[1], currentRadius);
if (p.visible) {
if (firstPoint) {
ctx.moveTo(p.x, p.y);
firstPoint = false;
} else {
ctx.lineTo(p.x, p.y);
}
}
});
ctx.closePath();
ctx.fill();
ctx.stroke();
}
draw();
}
</script>
<section id="geojson-section" class="globe-section">
<div class="container">
<div class="section-header" class:visible>
<h2>La Vague Planétaire de Sirius</h2>
<p class="section-subtitle">
Visualisation de la progression du lever héliaque à travers le monde
</p>
</div>
<div class="globe-container" class:visible>
<canvas bind:this={canvas} class="globe-canvas"></canvas>
<div class="globe-controls">
<p>🖱️ Glisser pour tourner | 🔍 Molette pour zoomer</p>
</div>
<div class="globe-info">
<div class="info-card card">
<h3>Le Phénomène de la Vague</h3>
<p>
Le lever héliaque de Sirius ne se produit pas simultanément sur toute la Terre.
Tel une vague cosmique, il progresse d'Est en Ouest, touchant d'abord La Réunion
fin juin, puis traversant l'Afrique, pour finalement atteindre les Caraïbes en juillet.
</p>
<p>
Cette animation montre la progression de cette "vague planétaire" qui suit
la rotation terrestre et la position de Sirius dans le ciel. Chaque lieu
expérimente ce moment sacré à une date précise, créant un calendrier
cosmique naturel qui a guidé les civilisations pendant des millénaires.
</p>
<div class="wave-timeline">
<div class="timeline-entry">
<span class="date">27 Juin</span>
<span class="location">La Réunion</span>
</div>
<div class="timeline-entry">
<span class="date">16-19 Juillet</span>
<span class="location">Kinshasa, Douala, Yaoundé</span>
</div>
<div class="timeline-entry">
<span class="date">20-24 Juillet</span>
<span class="location">Abidjan, Accra, Antilles, Cayenne</span>
</div>
<div class="timeline-entry">
<span class="date">25 Juillet</span>
<span class="location">Bamako, Dakar</span>
</div>
<div class="timeline-entry">
<span class="date">3-15 Août</span>
<span class="location">Le Caire, Paris</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.globe-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.globe-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.globe-container.visible {
opacity: 1;
transform: translateY(0);
}
.globe-canvas {
width: 100%;
height: 500px;
border-radius: 20px;
background: radial-gradient(circle at center, #0a192f 0%, #020c1b 100%);
cursor: grab;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
opacity: 0;
transform: scale(0.8);
}
.globe-canvas:active {
cursor: grabbing;
}
.globe-controls {
text-align: center;
margin-top: 0.5rem;
font-size: 0.9rem;
color: var(--color-primary);
opacity: 0.7;
}
.globe-info {
opacity: 0;
transform: translateX(50px);
}
.info-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.info-card p {
margin-bottom: 1rem;
line-height: 1.6;
}
.wave-timeline {
margin-top: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.timeline-entry {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
background: rgba(255, 215, 0, 0.1);
border-radius: 8px;
border-left: 3px solid var(--color-primary);
position: relative;
overflow: hidden;
}
.timeline-entry::before {
content: '';
position: absolute;
left: -100%;
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 215, 0, 0.3), transparent);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
.timeline-entry .date {
font-weight: 600;
color: var(--color-primary);
}
.timeline-entry .location {
color: var(--color-light);
}
@media (max-width: 768px) {
.globe-container {
grid-template-columns: 1fr;
}
.globe-canvas {
height: 400px;
}
}
</style>
-217
View File
@@ -1,217 +0,0 @@
<script>
import { onMount } from 'svelte';
let container;
let visible = false;
let globeInstance;
onMount(async () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !globeInstance) {
visible = true;
initGlobe();
}
});
});
const section = document.getElementById('openglobus-section');
if (section) observer.observe(section);
return () => {
observer.disconnect();
if (globeInstance) {
globeInstance.destroy();
}
};
});
async function initGlobe() {
// Charger OpenGlobus depuis CDN
const script = document.createElement('script');
script.src = 'https://unpkg.com/@openglobus/og@0.10.0/dist/og.umd.js';
script.onload = () => {
const { Globe, XYZ, Vector, Entity, Label } = window.og;
// Créer le globe
globeInstance = new Globe({
target: container,
name: "Earth",
terrain: null,
layers: [
new XYZ("OSM", {
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
visibility: true,
attribution: 'OpenStreetMap'
})
]
});
// Données des lieux
const locations = [
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, date: '27 Juin', color: '#FF6B35' },
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, date: '16 Juillet', color: '#FFA500' },
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, date: '22 Juillet', color: '#FFD700' },
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, date: '23 Juillet', color: '#FFD700' },
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, date: '23 Juillet', color: '#FFE44D' },
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, date: '3 Août', color: '#FFFACD' }
];
// Ajouter une couche de vecteurs pour les marqueurs
const markerLayer = new Vector("Markers", {
entities: locations.map(loc => new Entity({
name: loc.name,
lonlat: [loc.lon, loc.lat],
label: {
text: `${loc.name}\n${loc.date}`,
outline: 0.77,
outlineColor: "rgba(0,0,0,.75)",
size: 14,
color: "white",
offset: [0, -35]
},
billboard: {
src: createMarkerCanvas(loc.color),
width: 32,
height: 32,
offset: [0, -16]
}
}))
});
globeInstance.planet.addLayer(markerLayer);
// Positionner la vue sur l'Atlantique
globeInstance.planet.viewExtentArr([-80, -30, 50, 50]);
// Rotation automatique
let autoRotate = setInterval(() => {
if (globeInstance && globeInstance.planet) {
const cam = globeInstance.planet.camera;
cam.rotateHorizontal(0.2);
}
}, 50);
// Nettoyage
container.addEventListener('mousedown', () => {
clearInterval(autoRotate);
});
};
document.head.appendChild(script);
}
function createMarkerCanvas(color) {
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');
// Cercle avec couleur
ctx.beginPath();
ctx.arc(16, 16, 12, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
// Point central
ctx.beginPath();
ctx.arc(16, 16, 4, 0, Math.PI * 2);
ctx.fillStyle = '#fff';
ctx.fill();
return canvas.toDataURL();
}
</script>
<section id="openglobus-section" class="globe-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Globe avec OpenGlobus</h2>
<p class="section-subtitle">
Bibliothèque 3D professionnelle avec textures réalistes
</p>
</div>
<div class="globe-container openglobus" class:visible>
<div bind:this={container} class="openglobus-container"></div>
<div class="globe-info">
<div class="info-card card">
<h3>OpenGlobus</h3>
<p><strong>Avantages :</strong></p>
<ul>
<li>✅ Textures satellite réalistes</li>
<li>✅ Performance WebGL optimisée</li>
<li>✅ Zoom et navigation fluides</li>
<li>✅ Support natif des marqueurs</li>
</ul>
<p><strong>Inconvénients :</strong></p>
<ul>
<li>❌ Plus lourd (~500KB)</li>
<li>❌ Nécessite connexion internet pour les tuiles</li>
<li>❌ Personnalisation plus complexe</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<style>
.globe-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.globe-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.globe-container.visible {
opacity: 1;
transform: translateY(0);
}
.openglobus-container {
width: 100%;
height: 500px;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
}
.info-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.info-card ul {
list-style: none;
padding: 0;
margin: 0.5rem 0;
}
.info-card li {
padding: 0.25rem 0;
}
@media (max-width: 768px) {
.globe-container {
grid-template-columns: 1fr;
}
.openglobus-container {
height: 400px;
}
}
</style>
-271
View File
@@ -1,271 +0,0 @@
<script>
import { onMount } from 'svelte';
let container;
let visible = false;
let animationId;
let scene, camera, renderer, globe, markers;
onMount(async () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !scene) {
visible = true;
initGlobe();
}
});
});
const section = document.getElementById('threejs-section');
if (section) observer.observe(section);
return () => {
observer.disconnect();
if (animationId) {
cancelAnimationFrame(animationId);
}
if (renderer) {
renderer.dispose();
}
};
});
async function initGlobe() {
// Charger Three.js depuis CDN
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js';
script.onload = () => {
createThreeJSGlobe();
};
document.head.appendChild(script);
}
function createThreeJSGlobe() {
const THREE = window.THREE;
// Scène
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1628);
// Caméra
camera = new THREE.PerspectiveCamera(
45,
container.clientWidth / container.clientHeight,
0.1,
1000
);
camera.position.z = 2.5;
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
// Lumières
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);
// Globe
const geometry = new THREE.SphereGeometry(1, 64, 64);
// Texture de la Terre
const textureLoader = new THREE.TextureLoader();
const earthTexture = textureLoader.load(
'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg',
() => { renderer.render(scene, camera); }
);
const material = new THREE.MeshPhongMaterial({
map: earthTexture,
bumpScale: 0.05,
specular: new THREE.Color('grey'),
shininess: 10
});
globe = new THREE.Mesh(geometry, material);
scene.add(globe);
// Atmosphère
const atmosphereGeometry = new THREE.SphereGeometry(1.1, 64, 64);
const atmosphereMaterial = new THREE.MeshPhongMaterial({
color: 0x4444ff,
transparent: true,
opacity: 0.1,
side: THREE.BackSide
});
const atmosphere = new THREE.Mesh(atmosphereGeometry, atmosphereMaterial);
scene.add(atmosphere);
// Marqueurs
markers = new THREE.Group();
scene.add(markers);
const locations = [
{ name: 'La Réunion', lat: -21.1151, lon: 55.5364, color: 0xFF6B35 },
{ name: 'Kinshasa', lat: -4.4419, lon: 15.2663, color: 0xFFA500 },
{ name: 'Guadeloupe', lat: 16.2650, lon: -61.5510, color: 0xFFD700 },
{ name: 'Martinique', lat: 14.6415, lon: -61.0242, color: 0xFFD700 },
{ name: 'Dakar', lat: 14.7167, lon: -17.4677, color: 0xFFE44D },
{ name: 'Le Caire', lat: 30.0444, lon: 31.2357, color: 0xFFFACD }
];
locations.forEach(loc => {
const markerGeometry = new THREE.SphereGeometry(0.02, 16, 16);
const markerMaterial = new THREE.MeshBasicMaterial({ color: loc.color });
const marker = new THREE.Mesh(markerGeometry, markerMaterial);
// Convertir lat/lon en position 3D
const phi = (90 - loc.lat) * (Math.PI / 180);
const theta = (loc.lon + 180) * (Math.PI / 180);
marker.position.x = -1.05 * Math.sin(phi) * Math.cos(theta);
marker.position.y = 1.05 * Math.cos(phi);
marker.position.z = 1.05 * Math.sin(phi) * Math.sin(theta);
markers.add(marker);
});
// Contrôles de la souris
let mouseX = 0, mouseY = 0;
let isMouseDown = false;
container.addEventListener('mousedown', () => { isMouseDown = true; });
container.addEventListener('mouseup', () => { isMouseDown = false; });
container.addEventListener('mouseleave', () => { isMouseDown = false; });
container.addEventListener('mousemove', (e) => {
if (isMouseDown) {
mouseX = (e.clientX - container.offsetLeft) / container.clientWidth * 2 - 1;
mouseY = -(e.clientY - container.offsetTop) / container.clientHeight * 2 + 1;
}
});
// Animation
function animate() {
animationId = requestAnimationFrame(animate);
// Rotation automatique
if (!isMouseDown) {
globe.rotation.y += 0.002;
markers.rotation.y += 0.002;
} else {
// Rotation manuelle
globe.rotation.y += mouseX * 0.05;
globe.rotation.x = mouseY * 0.5;
markers.rotation.y = globe.rotation.y;
markers.rotation.x = globe.rotation.x;
}
renderer.render(scene, camera);
}
animate();
// Responsive
window.addEventListener('resize', () => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
});
}
</script>
<section id="threejs-section" class="globe-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Globe avec Three.js</h2>
<p class="section-subtitle">
Rendu 3D WebGL avec textures réalistes
</p>
</div>
<div class="globe-container threejs" class:visible>
<div bind:this={container} class="threejs-container"></div>
<div class="globe-info">
<div class="info-card card">
<h3>Three.js</h3>
<p><strong>Avantages :</strong></p>
<ul>
<li>✅ Rendu 3D réaliste</li>
<li>✅ Textures haute qualité</li>
<li>✅ Effets atmosphériques</li>
<li>✅ Très bien documenté</li>
</ul>
<p><strong>Inconvénients :</strong></p>
<ul>
<li>❌ Plus lourd (~150KB)</li>
<li>❌ Nécessite WebGL</li>
<li>❌ Plus complexe à personnaliser</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<style>
.globe-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.globe-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
align-items: center;
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.globe-container.visible {
opacity: 1;
transform: translateY(0);
}
.threejs-container {
width: 100%;
height: 500px;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
cursor: grab;
}
.threejs-container:active {
cursor: grabbing;
}
.info-card h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.info-card ul {
list-style: none;
padding: 0;
margin: 0.5rem 0;
}
.info-card li {
padding: 0.25rem 0;
}
@media (max-width: 768px) {
.globe-container {
grid-template-columns: 1fr;
}
.threejs-container {
height: 400px;
}
}
</style>
-441
View File
@@ -1,441 +0,0 @@
<script>
import { onMount } from 'svelte';
import { t } from '../lib/i18n.js';
import anime from '../lib/anime.js';
let starsCanvas;
let mouseX = 0.5;
let mouseY = 0.5;
let heroSection;
onMount(() => {
const ctx = starsCanvas.getContext('2d');
const stars = [];
const starCount = 150;
const shootingStars = [];
// Animation d'entrée avec AnimeJS
anime.timeline({
easing: 'easeOutQuad'
})
.add({
targets: '.title-line',
translateY: [50, 0],
opacity: [0, 1],
duration: 1200,
delay: anime.stagger(200)
})
.add({
targets: '.hero-subtitle',
translateY: [30, 0],
opacity: [0, 1],
duration: 800
}, '-=600')
.add({
targets: '.hero-description',
translateY: [30, 0],
opacity: [0, 1],
duration: 800
}, '-=400')
.add({
targets: '.hero-date',
translateY: [30, 0],
opacity: [0, 1],
duration: 800
}, '-=400')
.add({
targets: '.hero-cta .btn',
scale: [0.8, 1],
opacity: [0, 1],
duration: 600,
delay: anime.stagger(100)
}, '-=400');
// Animation des patterns africains
anime({
targets: '.pattern-kente',
translateX: [0, 200],
duration: 30000,
easing: 'linear',
loop: true
});
anime({
targets: '.pattern-adinkra',
rotate: 360,
duration: 40000,
easing: 'linear',
loop: true
});
anime({
targets: '.pattern-bogolan',
scale: [1, 1.2, 1],
opacity: [0.05, 0.15, 0.05],
duration: 20000,
easing: 'easeInOutSine',
loop: true
});
// Attendre que le DOM soit prêt
setTimeout(() => {
// Redimensionner le canvas
const resize = () => {
const hero = starsCanvas.parentElement;
starsCanvas.width = hero.offsetWidth;
starsCanvas.height = hero.offsetHeight;
// Réinitialiser les étoiles après redimensionnement
if (stars.length === 0) {
for (let i = 0; i < starCount; i++) {
stars.push({
x: Math.random() * starsCanvas.width,
y: Math.random() * starsCanvas.height,
size: Math.random() * 2,
brightness: Math.random(),
speed: 0.1 + Math.random() * 0.5,
pulseSpeed: 2000 + Math.random() * 3000
});
}
// Animer les étoiles avec AnimeJS
stars.forEach((star, i) => {
anime({
targets: star,
brightness: [0.1, 1, 0.1],
duration: star.pulseSpeed,
easing: 'easeInOutSine',
loop: true,
delay: i * 20
});
});
}
};
resize();
window.addEventListener('resize', resize);
// Animation des étoiles
const animate = () => {
ctx.clearRect(0, 0, starsCanvas.width, starsCanvas.height);
// Effet de parallaxe avec la souris
const offsetX = (mouseX - 0.5) * 20;
const offsetY = (mouseY - 0.5) * 20;
// Dessiner les étoiles
stars.forEach(star => {
// Mouvement subtil
star.x += Math.sin(Date.now() * 0.0001 * star.speed) * 0.5;
star.y += Math.cos(Date.now() * 0.0001 * star.speed) * 0.3;
// Garder dans les limites
if (star.x < 0) star.x = starsCanvas.width;
if (star.x > starsCanvas.width) star.x = 0;
if (star.y < 0) star.y = starsCanvas.height;
if (star.y > starsCanvas.height) star.y = 0;
ctx.beginPath();
ctx.arc(
star.x + offsetX,
star.y + offsetY,
star.size,
0,
Math.PI * 2
);
ctx.fillStyle = `rgba(255, 215, 0, ${star.brightness})`;
ctx.fill();
});
// Dessiner les étoiles filantes
shootingStars.forEach((star, index) => {
if (star.opacity <= 0) {
shootingStars.splice(index, 1);
return;
}
ctx.beginPath();
ctx.moveTo(star.x, star.y);
ctx.lineTo(star.x - star.vx * 20, star.y - star.vy * 20);
ctx.strokeStyle = `rgba(255, 215, 0, ${star.opacity})`;
ctx.lineWidth = star.size;
ctx.stroke();
star.x += star.vx;
star.y += star.vy;
star.opacity -= 0.02;
});
// Créer occasionnellement une étoile filante
if (Math.random() < 0.01 && shootingStars.length < 3) {
shootingStars.push({
x: Math.random() * starsCanvas.width,
y: Math.random() * starsCanvas.height * 0.5,
vx: 5 + Math.random() * 10,
vy: 2 + Math.random() * 5,
size: 1 + Math.random() * 2,
opacity: 1
});
}
requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener('resize', resize);
};
}, 100);
});
const handleMouseMove = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = (e.clientY - rect.top) / rect.height;
};
// Animation du bouton au survol
function handleButtonHover(e) {
anime({
targets: e.currentTarget,
scale: 1.05,
duration: 300,
easing: 'easeOutElastic(1, .5)'
});
}
function handleButtonLeave(e) {
anime({
targets: e.currentTarget,
scale: 1,
duration: 300,
easing: 'easeOutElastic(1, .5)'
});
}
</script>
<section id="home" class="hero" on:mousemove={handleMouseMove} bind:this={heroSection}>
<canvas bind:this={starsCanvas} class="stars-canvas"></canvas>
<div class="hero-pattern">
<!-- Patterns africains visibles -->
<div class="pattern-overlay pattern-kente"></div>
<div class="pattern-overlay pattern-adinkra"></div>
<div class="pattern-overlay pattern-bogolan"></div>
</div>
<div class="hero-content">
<div class="container">
<div class="hero-text">
<h1 class="hero-title">
<span class="title-line">{$t('hero_title1')}</span>
<span class="title-line">{$t('hero_title2')}</span>
</h1>
<p class="hero-subtitle">
{$t('hero_subtitle')}
</p>
<p class="hero-description">
{$t('hero_description')}
</p>
<p class="hero-date">
{$t('hero_date')}<strong>{$t('hero_date_value')}</strong>
</p>
<div class="hero-cta">
<a
href="#predictions"
class="btn btn-primary"
on:mouseenter={handleButtonHover}
on:mouseleave={handleButtonLeave}
>
{$t('hero_cta_predictions')}
</a>
<a
href="#about"
class="btn btn-outline"
on:mouseenter={handleButtonHover}
on:mouseleave={handleButtonLeave}
>
{$t('hero_cta_learn')}
</a>
</div>
</div>
</div>
</div>
</section>
<style>
.hero {
position: relative;
min-height: 100vh;
background: var(--gradient-dawn);
display: flex;
align-items: center;
overflow: hidden;
}
.stars-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.hero-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.pattern-overlay {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.1;
background-size: 200px 200px;
mix-blend-mode: overlay;
}
.pattern-kente {
background-image:
repeating-linear-gradient(
45deg,
var(--color-primary) 0px,
var(--color-primary) 10px,
transparent 10px,
transparent 20px,
var(--color-secondary) 20px,
var(--color-secondary) 30px,
transparent 30px,
transparent 40px
),
repeating-linear-gradient(
-45deg,
var(--color-accent) 0px,
var(--color-accent) 10px,
transparent 10px,
transparent 20px
);
}
.pattern-adinkra {
background-image:
radial-gradient(circle at 20% 20%, var(--color-primary) 2px, transparent 2px),
radial-gradient(circle at 80% 80%, var(--color-secondary) 2px, transparent 2px),
radial-gradient(circle at 50% 50%, var(--color-accent) 3px, transparent 3px);
background-size: 50px 50px;
}
.pattern-bogolan {
background-image:
repeating-conic-gradient(
from 0deg at 50% 50%,
var(--color-primary) 0deg 10deg,
transparent 10deg 20deg
);
background-size: 100px 100px;
opacity: 0.05;
}
.hero-content {
position: relative;
z-index: 10;
width: 100%;
padding: var(--spacing-xl) 0;
}
.hero-text {
max-width: 600px;
text-align: center;
margin: 0 auto;
}
.hero-title {
font-size: clamp(3rem, 8vw, 5rem);
font-weight: 700;
line-height: 1.1;
margin-bottom: 1rem;
color: var(--color-light);
}
.title-line {
display: block;
opacity: 0;
}
.title-line:nth-child(2) {
color: var(--color-primary);
}
.hero-subtitle {
font-size: clamp(1.2rem, 3vw, 1.5rem);
color: var(--color-primary);
font-family: var(--font-heading);
margin-bottom: 0.5rem;
opacity: 0;
}
.hero-description {
font-size: clamp(1rem, 2.5vw, 1.2rem);
color: var(--color-light);
margin-bottom: 1rem;
opacity: 0;
}
.hero-date {
font-size: 1.2rem;
margin-bottom: 2rem;
opacity: 0;
}
.hero-date strong {
color: var(--color-primary);
font-weight: 700;
}
.hero-cta {
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
}
.btn {
opacity: 0;
transform-origin: center;
}
.btn-outline {
background: transparent;
border: 2px solid var(--color-primary);
color: var(--color-primary);
}
.btn-outline:hover {
background: var(--color-primary);
color: var(--color-dark);
}
@media (max-width: 768px) {
.hero-title {
font-size: clamp(2.5rem, 10vw, 4rem);
}
.hero-cta {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 300px;
}
}
</style>
-150
View File
@@ -1,150 +0,0 @@
<script>
import { currentLanguage, availableLanguages, setLanguage } from '../lib/i18n.js';
let isOpen = false;
function toggleDropdown() {
isOpen = !isOpen;
}
function selectLanguage(lang) {
setLanguage(lang);
isOpen = false;
}
// Fermer le dropdown si on clique ailleurs
function handleClickOutside(event) {
if (!event.target.closest('.language-selector')) {
isOpen = false;
}
}
</script>
<svelte:window on:click={handleClickOutside} />
<div class="language-selector">
<button class="language-button" on:click={toggleDropdown}>
<span class="flag">
{availableLanguages.find(l => l.code === $currentLanguage)?.flag}
</span>
<span class="language-name">
{availableLanguages.find(l => l.code === $currentLanguage)?.name}
</span>
<span class="arrow" class:open={isOpen}>▼</span>
</button>
{#if isOpen}
<div class="language-dropdown">
{#each availableLanguages as lang}
<button
class="language-option"
class:active={$currentLanguage === lang.code}
on:click={() => selectLanguage(lang.code)}
>
<span class="flag">{lang.flag}</span>
<span>{lang.name}</span>
</button>
{/each}
</div>
{/if}
</div>
<style>
.language-selector {
position: relative;
}
.language-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: var(--color-light);
cursor: pointer;
transition: all 0.3s ease;
}
.language-button:hover {
background: rgba(255, 255, 255, 0.15);
border-color: var(--color-primary);
}
.flag {
font-size: 1.2rem;
}
.language-name {
font-size: 0.9rem;
}
.arrow {
font-size: 0.7rem;
margin-left: 0.5rem;
transition: transform 0.3s ease;
}
.arrow.open {
transform: rotate(180deg);
}
.language-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: var(--color-dark);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 1000;
animation: slideDown 0.3s ease-out;
}
.language-option {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.75rem 1rem;
background: transparent;
border: none;
color: var(--color-light);
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
}
.language-option:hover {
background: rgba(255, 255, 255, 0.1);
}
.language-option.active {
background: rgba(255, 215, 0, 0.2);
color: var(--color-primary);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.language-name {
display: none;
}
.language-button {
padding: 0.5rem;
}
}
</style>
-141
View File
@@ -1,141 +0,0 @@
<script>
import { onMount } from 'svelte';
import anime from '../lib/anime.js';
let loading = true;
let progress = 0;
onMount(() => {
// Animation de la barre de progression
anime({
targets: { value: 0 },
value: 100,
duration: 2000,
easing: 'easeInOutQuad',
update: function(anim) {
progress = Math.round(anim.animations[0].currentValue);
},
complete: function() {
setTimeout(() => {
loading = false;
// Animation de sortie
anime({
targets: '.loader',
opacity: [1, 0],
duration: 600,
easing: 'easeOutQuad'
});
}, 300);
}
});
// Animation des étoiles du loader
anime({
targets: '.loader-star',
rotate: 360,
duration: 2000,
easing: 'linear',
loop: true
});
// Animation du texte
anime({
targets: '.loader-text span',
translateY: [-20, 0],
opacity: [0, 1],
duration: 800,
delay: anime.stagger(100),
easing: 'easeOutElastic(1, .5)'
});
});
</script>
{#if loading}
<div class="loader">
<div class="loader-content">
<div class="loader-star"></div>
<div class="loader-text">
<span>S</span>
<span>i</span>
<span>r</span>
<span>i</span>
<span>u</span>
<span>s</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: {progress}%"></div>
</div>
<div class="progress-text">{progress}%</div>
</div>
</div>
{/if}
<style>
.loader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--color-dark);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
pointer-events: none;
}
.loader-content {
text-align: center;
}
.loader-star {
font-size: 4rem;
margin-bottom: 1rem;
display: inline-block;
}
.loader-text {
font-size: 3rem;
font-weight: 700;
color: var(--color-primary);
margin-bottom: 2rem;
letter-spacing: 0.5rem;
}
.loader-text span {
display: inline-block;
opacity: 0;
}
.progress-bar {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
margin: 0 auto 1rem;
}
.progress-fill {
height: 100%;
background: var(--color-primary);
transition: width 0.3s ease;
}
.progress-text {
color: var(--color-light);
font-size: 0.9rem;
opacity: 0.7;
}
@media (max-width: 768px) {
.loader-text {
font-size: 2rem;
}
.loader-star {
font-size: 3rem;
}
}
</style>
-269
View File
@@ -1,269 +0,0 @@
<script>
import { t } from '../lib/i18n.js';
import LanguageSelector from './LanguageSelector.svelte';
import anime from '../lib/anime.js';
import { onMount } from 'svelte';
export let activeSection = 'home';
let isMenuOpen = false;
let navbar;
const toggleMenu = () => {
isMenuOpen = !isMenuOpen;
if (isMenuOpen) {
anime({
targets: '.nav-menu',
translateX: [300, 0],
opacity: [0, 1],
duration: 400,
easing: 'easeOutQuad'
});
anime({
targets: '.nav-menu li',
translateX: [50, 0],
opacity: [0, 1],
duration: 600,
delay: anime.stagger(50),
easing: 'easeOutQuad'
});
}
};
onMount(() => {
// Animation d'entrée de la navbar
anime({
targets: navbar,
translateY: [-100, 0],
opacity: [0, 1],
duration: 800,
easing: 'easeOutQuad'
});
// Animation du logo
anime({
targets: '.logo-icon',
rotate: [0, 360],
duration: 2000,
delay: 500,
easing: 'easeInOutQuad'
});
});
const menuItems = [
{ id: 'home', label: 'nav_home', icon: '🌟' },
{ id: 'about', label: 'nav_about', icon: '✨' },
{ id: 'predictions', label: 'nav_predictions', icon: '📅' },
{ id: 'science', label: 'nav_science', icon: '🔭' },
{ id: 'culture', label: 'nav_culture', icon: '🌍' },
{ id: 'observatory', label: 'nav_observatory', icon: '👁️' }
];
const scrollToSection = (id) => {
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' });
isMenuOpen = false;
};
</script>
<nav class="navbar" class:scrolled={activeSection !== 'home'} bind:this={navbar}>
<div class="container">
<div class="nav-content">
<a href="#home" class="logo" on:click|preventDefault={() => scrollToSection('home')}>
<span class="logo-icon"></span>
<span class="logo-text">Sirius Gwada</span>
</a>
<button class="menu-toggle" on:click={toggleMenu} aria-label="Menu">
<span class="hamburger" class:active={isMenuOpen}></span>
</button>
<ul class="nav-menu" class:open={isMenuOpen}>
{#each menuItems as item}
<li>
<a
href="#{item.id}"
class:active={activeSection === item.id}
on:click|preventDefault={() => scrollToSection(item.id)}
>
<span class="nav-icon">{item.icon}</span>
<span class="nav-label">{$t(item.label)}</span>
</a>
</li>
{/each}
</ul>
<LanguageSelector />
</div>
</div>
</nav>
<style>
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(26, 26, 46, 0.95);
backdrop-filter: blur(10px);
transition: var(--transition-base);
border-bottom: 1px solid transparent;
}
.navbar.scrolled {
border-bottom-color: rgba(255, 215, 0, 0.2);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.nav-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: var(--color-primary);
font-family: var(--font-display);
font-size: 1.5rem;
transition: var(--transition-base);
}
.logo:hover {
transform: scale(1.05);
}
.logo-icon {
font-size: 2rem;
animation: rotate 20s linear infinite;
}
.menu-toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
}
.hamburger {
display: block;
width: 25px;
height: 2px;
background: var(--color-primary);
position: relative;
transition: var(--transition-base);
}
.hamburger::before,
.hamburger::after {
content: '';
position: absolute;
left: 0;
width: 100%;
height: 2px;
background: var(--color-primary);
transition: var(--transition-base);
}
.hamburger::before {
top: -8px;
}
.hamburger::after {
bottom: -8px;
}
.hamburger.active {
background: transparent;
}
.hamburger.active::before {
transform: rotate(45deg);
top: 0;
}
.hamburger.active::after {
transform: rotate(-45deg);
bottom: 0;
}
.nav-menu {
display: flex;
list-style: none;
gap: 2rem;
margin: 0;
padding: 0;
}
.nav-menu a {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: var(--color-light);
transition: var(--transition-base);
position: relative;
}
.nav-menu a:hover,
.nav-menu a.active {
color: var(--color-primary);
}
.nav-menu a::after {
content: '';
position: absolute;
bottom: -5px;
left: 0;
right: 0;
height: 2px;
background: var(--color-primary);
transform: scaleX(0);
transition: var(--transition-base);
}
.nav-menu a.active::after {
transform: scaleX(1);
}
.nav-icon {
font-size: 1.2rem;
}
/* Mobile */
@media (max-width: 768px) {
.menu-toggle {
display: block;
}
.nav-menu {
position: fixed;
top: 70px;
left: 0;
right: 0;
background: rgba(26, 26, 46, 0.98);
flex-direction: column;
padding: 2rem;
transform: translateY(-100%);
opacity: 0;
transition: var(--transition-base);
border-bottom: 2px solid var(--color-primary);
}
.nav-menu.open {
transform: translateY(0);
opacity: 1;
}
.nav-menu a {
padding: 0.5rem 0;
}
}
</style>
-661
View File
@@ -1,661 +0,0 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let selectedSite = null;
let mapCanvas;
let ctx;
let guadeloupeGeoJson = null;
const sites = [
{
id: 'moule',
name: 'Le Moule',
lat: 16.3333,
lng: -61.3472,
description: 'Plage de l\'Autre Bord - Vue dégagée sur l\'Est',
features: ['Plage isolée', 'Horizon Est dégagé', 'Peu de pollution lumineuse'],
bestTime: '5h00 - 5h30',
image: '🏖️'
},
{
id: 'deshaies',
name: 'Deshaies',
lat: 16.3039,
lng: -61.7966,
description: 'Plage de Grande Anse - Cadre idyllique',
features: ['Plage populaire', 'Montagnes en arrière-plan', 'Accès facile'],
bestTime: '5h15 - 5h45',
image: '🌅'
},
{
id: 'sainte-anne',
name: 'Sainte-Anne',
lat: 16.2262,
lng: -61.3809,
description: 'Plage de Bois Jolan - Tranquillité garantie',
features: ['Plage sauvage', 'Très peu fréquentée', 'Nature préservée'],
bestTime: '5h00 - 5h30',
image: '🌴'
},
{
id: 'port-louis',
name: 'Port-Louis',
lat: 16.4180,
lng: -61.5312,
description: 'Plage du Souffleur - Vue panoramique',
features: ['Vue à 180°', 'Site historique', 'Calme au petit matin'],
bestTime: '5h00 - 5h30',
image: '🏛️'
},
{
id: 'marie-galante',
name: 'Marie-Galante',
lat: 15.9500,
lng: -61.2667,
description: 'Plage de la Feuillère - Horizon infini',
features: ['Île préservée', 'Ciel très noir', 'Conditions optimales'],
bestTime: '4h45 - 5h15',
image: '🏝️'
},
{
id: 'soufriere',
name: 'Route de la Soufrière',
lat: 16.0439,
lng: -61.6639,
description: 'Parking de Savane à Mulets - Altitude',
features: ['1000m altitude', 'Air pur', 'Vue dégagée', 'Nuits fraîches'],
bestTime: '5h00 - 5h30',
image: '🌋'
},
{
id: 'petite-terre',
name: 'Petite-Terre',
lat: 16.1700,
lng: -61.1200,
description: 'Réserve naturelle - Conditions parfaites',
features: ['Aucune pollution lumineuse', 'Réserve protégée', 'Accès réglementé'],
bestTime: '4h45 - 5h15',
image: '🦎'
}
];
onMount(async () => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !ctx) {
visible = true;
initMap();
}
});
});
const section = document.getElementById('observation-map');
if (section) observer.observe(section);
return () => observer.disconnect();
});
async function initMap() {
ctx = mapCanvas.getContext('2d');
// Charger les données GeoJSON de la Guadeloupe
try {
const response = await fetch('https://raw.githubusercontent.com/gregoiredavid/france-geojson/master/regions/guadeloupe/departements-guadeloupe.geojson');
guadeloupeGeoJson = await response.json();
// Redimensionner le canvas
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Dessiner la carte
drawMap();
} catch (error) {
console.error('Erreur de chargement GeoJSON:', error);
// Fallback : dessiner une carte simple
drawSimpleMap();
}
}
function resizeCanvas() {
const container = mapCanvas.parentElement;
mapCanvas.width = container.offsetWidth;
mapCanvas.height = container.offsetWidth * 0.75; // Ratio 4:3
if (guadeloupeGeoJson) drawMap();
}
function drawMap() {
if (!ctx || !guadeloupeGeoJson) return;
// Effacer le canvas
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
// Fond océan
ctx.fillStyle = '#1a3a52';
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
// Calculer les limites et l'échelle
const bounds = calculateBounds(guadeloupeGeoJson);
const scale = calculateScale(bounds, mapCanvas.width, mapCanvas.height);
// Dessiner chaque feature
guadeloupeGeoJson.features.forEach(feature => {
drawFeature(feature, bounds, scale);
});
// Dessiner les sites
drawSites(bounds, scale);
}
function drawSimpleMap() {
if (!ctx) return;
// Effacer le canvas
ctx.clearRect(0, 0, mapCanvas.width, mapCanvas.height);
// Fond océan
ctx.fillStyle = '#1a3a52';
ctx.fillRect(0, 0, mapCanvas.width, mapCanvas.height);
// Dessiner une forme approximative de la Guadeloupe
const centerX = mapCanvas.width / 2;
const centerY = mapCanvas.height / 2;
const scale = mapCanvas.width / 4;
// Grande-Terre
ctx.fillStyle = '#2d5a3d';
ctx.strokeStyle = '#4CAF50';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(centerX + scale * 0.5, centerY - scale * 0.2, scale * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Basse-Terre
ctx.beginPath();
ctx.arc(centerX - scale * 0.3, centerY, scale * 0.5, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Marie-Galante
ctx.beginPath();
ctx.arc(centerX + scale * 0.2, centerY + scale * 0.8, scale * 0.25, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Dessiner les sites avec des positions approximatives
const bounds = { minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
const simpleScale = {
x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
y: mapCanvas.height / (bounds.maxLat - bounds.minLat)
};
drawSites(bounds, simpleScale);
}
function calculateBounds(geoJson) {
let minLng = Infinity, maxLng = -Infinity;
let minLat = Infinity, maxLat = -Infinity;
function processBounds(coords) {
if (Array.isArray(coords[0])) {
coords.forEach(processBounds);
} else {
minLng = Math.min(minLng, coords[0]);
maxLng = Math.max(maxLng, coords[0]);
minLat = Math.min(minLat, coords[1]);
maxLat = Math.max(maxLat, coords[1]);
}
}
geoJson.features.forEach(feature => {
if (feature.geometry && feature.geometry.coordinates) {
processBounds(feature.geometry.coordinates);
}
});
// Ajouter une marge
const margin = 0.1;
return {
minLng: minLng - margin,
maxLng: maxLng + margin,
minLat: minLat - margin,
maxLat: maxLat + margin
};
}
function calculateScale(bounds, width, height) {
return {
x: width / (bounds.maxLng - bounds.minLng),
y: height / (bounds.maxLat - bounds.minLat)
};
}
function projectPoint(lng, lat, bounds, scale) {
return {
x: (lng - bounds.minLng) * scale.x,
y: mapCanvas.height - (lat - bounds.minLat) * scale.y
};
}
function drawFeature(feature, bounds, scale) {
ctx.fillStyle = '#2d5a3d';
ctx.strokeStyle = '#4CAF50';
ctx.lineWidth = 2;
function drawCoordinates(coords) {
if (!coords || coords.length === 0) return;
if (Array.isArray(coords[0][0])) {
coords.forEach(drawCoordinates);
return;
}
ctx.beginPath();
coords.forEach((coord, i) => {
const point = projectPoint(coord[0], coord[1], bounds, scale);
if (i === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
ctx.closePath();
ctx.fill();
ctx.stroke();
}
if (feature.geometry && feature.geometry.coordinates) {
drawCoordinates(feature.geometry.coordinates);
}
}
function drawSites(bounds, scale) {
sites.forEach(site => {
const point = projectPoint(site.lng, site.lat, bounds, scale);
// Cercle du marqueur
ctx.beginPath();
ctx.arc(point.x, point.y, selectedSite?.id === site.id ? 15 : 10, 0, Math.PI * 2);
ctx.fillStyle = selectedSite?.id === site.id ? '#FFD700' : '#FF6B35';
ctx.fill();
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.stroke();
// Icône du site
ctx.fillStyle = 'white';
ctx.font = selectedSite?.id === site.id ? '18px sans-serif' : '14px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(site.image, point.x, point.y);
// Nom du site si sélectionné
if (selectedSite?.id === site.id) {
ctx.fillStyle = 'white';
ctx.font = 'bold 14px sans-serif';
ctx.fillText(site.name, point.x, point.y + 25);
}
});
}
function handleMapClick(event) {
const rect = mapCanvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
// Vérifier si on clique sur un site
const bounds = guadeloupeGeoJson ?
calculateBounds(guadeloupeGeoJson) :
{ minLng: -62, maxLng: -61, minLat: 15.8, maxLat: 16.5 };
const scale = guadeloupeGeoJson ?
calculateScale(bounds, mapCanvas.width, mapCanvas.height) :
{ x: mapCanvas.width / (bounds.maxLng - bounds.minLng),
y: mapCanvas.height / (bounds.maxLat - bounds.minLat) };
let clickedSite = null;
sites.forEach(site => {
const point = projectPoint(site.lng, site.lat, bounds, scale);
const distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
if (distance < 20) {
clickedSite = site;
}
});
if (clickedSite) {
selectedSite = selectedSite?.id === clickedSite.id ? null : clickedSite;
drawMap();
}
}
function selectSite(site) {
selectedSite = selectedSite?.id === site.id ? null : site;
if (guadeloupeGeoJson) {
drawMap();
} else {
drawSimpleMap();
}
}
</script>
<section id="observation-map" class="map-section">
<div class="container">
<div class="section-header" class:visible>
<h2>Carte des Sites d'Observation</h2>
<p class="section-subtitle">
Les meilleurs endroits en Guadeloupe pour observer le lever héliaque
</p>
</div>
<div class="map-wrapper" class:visible>
<!-- Carte Canvas de la Guadeloupe -->
<div class="map-container">
<canvas
bind:this={mapCanvas}
class="guadeloupe-map"
on:click={handleMapClick}
></canvas>
</div>
<!-- Liste des sites -->
<div class="sites-list">
<h3>Sites d'Observation</h3>
{#each sites as site}
<div
class="site-card"
class:active={selectedSite?.id === site.id}
on:click={() => selectSite(site)}
>
<div class="site-header">
<span class="site-icon">{site.image}</span>
<h4>{site.name}</h4>
</div>
<p class="site-description">{site.description}</p>
{#if selectedSite?.id === site.id}
<div class="site-details">
<div class="features">
<h5>Caractéristiques :</h5>
<ul>
{#each site.features as feature}
<li>{feature}</li>
{/each}
</ul>
</div>
<div class="observation-time">
<h5>🕐 Meilleur créneau :</h5>
<p>{site.bestTime}</p>
</div>
<div class="coordinates">
<h5>📍 Coordonnées GPS :</h5>
<p>{site.lat}°N, {Math.abs(site.lng)}°O</p>
</div>
</div>
{/if}
</div>
{/each}
</div>
</div>
<!-- Conseils d'observation -->
<div class="observation-tips" class:visible>
<h3>Conseils pour l'Observation</h3>
<div class="tips-grid">
<div class="tip-card">
<span class="tip-icon">🌙</span>
<h4>Phase Lunaire</h4>
<p>Privilégiez les nuits sans Lune ou avec un fin croissant</p>
</div>
<div class="tip-card">
<span class="tip-icon">☁️</span>
<h4>Météo</h4>
<p>Vérifiez l'absence de nuages à l'Est, surtout près de l'horizon</p>
</div>
<div class="tip-card">
<span class="tip-icon"></span>
<h4>Horaire</h4>
<p>Arrivez 30 minutes avant pour habituer vos yeux à l'obscurité</p>
</div>
<div class="tip-card">
<span class="tip-icon">🔦</span>
<h4>Équipement</h4>
<p>Lampe rouge, jumelles optionnelles, vêtements chauds</p>
</div>
</div>
</div>
</div>
</section>
<style>
.map-section {
padding: var(--spacing-xl) 0;
background: var(--gradient-night);
}
.map-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
margin-top: var(--spacing-lg);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out;
}
.map-wrapper.visible {
opacity: 1;
transform: translateY(0);
}
/* Carte Canvas */
.map-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
position: relative;
}
.guadeloupe-map {
width: 100%;
height: auto;
cursor: pointer;
border-radius: 10px;
}
/* Liste des sites */
.sites-list {
max-height: 600px;
overflow-y: auto;
padding-right: 1rem;
}
.sites-list h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.site-card {
background: rgba(255, 255, 255, 0.05);
border: 2px solid transparent;
border-radius: 15px;
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.site-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: var(--color-primary);
}
.site-card.active {
background: rgba(255, 215, 0, 0.1);
border-color: var(--color-primary);
}
.site-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.site-icon {
font-size: 1.5rem;
}
.site-header h4 {
color: var(--color-primary);
margin: 0;
}
.site-description {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
.site-details {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
animation: slideDown 0.3s ease-out;
}
.site-details h5 {
color: var(--color-secondary);
margin: 0.5rem 0;
font-size: 0.9rem;
}
.site-details ul {
list-style: none;
padding: 0;
margin: 0;
}
.site-details li {
padding: 0.25rem 0;
padding-left: 1rem;
position: relative;
font-size: 0.85rem;
}
.site-details li::before {
content: "•";
position: absolute;
left: 0;
color: var(--color-primary);
}
.observation-time, .coordinates {
margin-top: 0.5rem;
}
.observation-time p, .coordinates p {
margin: 0;
color: var(--color-light);
font-size: 0.9rem;
}
/* Conseils d'observation */
.observation-tips {
margin-top: var(--spacing-xl);
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.2s;
}
.observation-tips.visible {
opacity: 1;
transform: translateY(0);
}
.observation-tips h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
}
.tips-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.tip-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 1.5rem;
text-align: center;
transition: transform 0.3s ease;
}
.tip-card:hover {
transform: translateY(-5px);
}
.tip-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 1rem;
}
.tip-card h4 {
color: var(--color-primary);
margin: 0 0 0.5rem 0;
}
.tip-card p {
color: var(--color-light);
margin: 0;
font-size: 0.9rem;
}
/* Scrollbar personnalisée */
.sites-list::-webkit-scrollbar {
width: 8px;
}
.sites-list::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
.sites-list::-webkit-scrollbar-thumb {
background: var(--color-primary);
border-radius: 4px;
}
@keyframes slideDown {
from {
opacity: 0;
max-height: 0;
}
to {
opacity: 1;
max-height: 300px;
}
}
@media (max-width: 768px) {
.map-wrapper {
grid-template-columns: 1fr;
}
.map-container {
padding: 1rem;
}
.sites-list {
max-height: none;
}
.tips-grid {
grid-template-columns: 1fr 1fr;
}
}
</style>
-435
View File
@@ -1,435 +0,0 @@
<script>
import { onMount } from 'svelte';
import anime from '../lib/anime.js';
let visible = false;
let activePhase = 0;
const observationPhases = [
{
time: '04h30',
title: 'Préparation',
description: 'Arrivez sur site, laissez vos yeux s\'adapter',
icon: '🌙'
},
{
time: '05h00',
title: 'Recherche',
description: 'Cherchez vers l\'Est-Sud-Est, près de l\'horizon',
icon: '🔭'
},
{
time: '05h15',
title: 'Apparition',
description: 'Sirius émerge de la lueur de l\'aube',
icon: '⭐'
},
{
time: '05h30',
title: 'Observation',
description: 'Moment optimal pour contempler',
icon: '✨'
}
];
const equipment = [
{ item: 'Jumelles', essential: false, description: '7x50 ou 10x50 recommandées' },
{ item: 'Lampe rouge', essential: true, description: 'Préserve la vision nocturne' },
{ item: 'Boussole', essential: true, description: 'Pour trouver l\'Est' },
{ item: 'Vêtements chauds', essential: true, description: 'Les matins sont frais' },
{ item: 'Carnet de notes', essential: false, description: 'Pour documenter l\'observation' }
];
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
// Animation d'entrée
anime.timeline({
easing: 'easeOutQuad'
})
.add({
targets: '.section-header',
translateY: [30, 0],
opacity: [0, 1],
duration: 800
})
.add({
targets: '.timeline-container',
translateX: [-50, 0],
opacity: [0, 1],
duration: 1000
}, '-=400')
.add({
targets: '.timeline-phase',
scale: [0.8, 1],
opacity: [0, 1],
duration: 600,
delay: anime.stagger(150)
}, '-=600')
.add({
targets: '.equipment-grid',
translateY: [50, 0],
opacity: [0, 1],
duration: 800
}, '-=400');
// Animation de la timeline automatique
startTimelineAnimation();
}
});
});
const section = document.getElementById('observatory');
if (section) observer.observe(section);
return () => observer.disconnect();
});
function startTimelineAnimation() {
// Progression automatique de la timeline
const timeline = anime.timeline({
loop: true,
autoplay: true
});
observationPhases.forEach((phase, index) => {
timeline.add({
targets: {},
duration: 3000,
changeBegin: () => {
activePhase = index;
animatePhaseChange(index);
}
});
});
}
function animatePhaseChange(index) {
// Animation de l'indicateur actif
anime({
targets: '.timeline-indicator',
left: `${index * 25}%`,
duration: 800,
easing: 'easeInOutQuad'
});
// Pulsation de la phase active
anime({
targets: `.timeline-phase:nth-child(${index + 1}) .phase-icon`,
scale: [1, 1.2, 1],
duration: 600,
easing: 'easeInOutQuad'
});
}
</script>
<section id="observatory" class="observatory">
<div class="container">
<div class="section-header" class:visible>
<h2>Guide d'Observation</h2>
<p class="section-subtitle">
Tout ce qu'il faut savoir pour observer le lever héliaque
</p>
</div>
<div class="observatory-content" class:visible>
<!-- Timeline d'observation -->
<div class="timeline-container">
<h3>Chronologie de l'Observation</h3>
<div class="timeline">
<div class="timeline-track"></div>
<div class="timeline-indicator"></div>
{#each observationPhases as phase, i}
<div class="timeline-phase" class:active={activePhase === i}>
<div class="phase-icon">{phase.icon}</div>
<div class="phase-time">{phase.time}</div>
<div class="phase-info">
<h4>{phase.title}</h4>
<p>{phase.description}</p>
</div>
</div>
{/each}
</div>
</div>
<!-- Équipement recommandé -->
<div class="equipment-section">
<h3>Équipement Recommandé</h3>
<div class="equipment-grid">
{#each equipment as item, i}
<div
class="equipment-card"
class:essential={item.essential}
style="animation-delay: {i * 0.1}s"
>
<div class="equipment-header">
<span class="equipment-name">{item.item}</span>
{#if item.essential}
<span class="essential-badge">Essentiel</span>
{/if}
</div>
<p class="equipment-desc">{item.description}</p>
</div>
{/each}
</div>
</div>
<!-- Conseils pratiques -->
<div class="tips-section">
<h3>Conseils Pratiques</h3>
<div class="tips-grid">
<div class="tip-card">
<div class="tip-icon">🌤️</div>
<h4>Météo Idéale</h4>
<p>Ciel dégagé à l'Est, pas de nuages bas, humidité modérée</p>
</div>
<div class="tip-card">
<div class="tip-icon">🌑</div>
<h4>Phase Lunaire</h4>
<p>Nouvelle lune ou croissant fin pour un ciel plus sombre</p>
</div>
<div class="tip-card">
<div class="tip-icon">📱</div>
<h4>Applications Utiles</h4>
<p>Stellarium, SkySafari, Star Walk pour repérer les constellations</p>
</div>
<div class="tip-card">
<div class="tip-icon">👥</div>
<h4>En Groupe</h4>
<p>Partagez ce moment unique avec famille et amis</p>
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.observatory {
padding: var(--spacing-xl) 0;
background: var(--gradient-dawn);
}
.observatory-content {
margin-top: var(--spacing-lg);
}
/* Timeline */
.timeline-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
margin-bottom: 3rem;
opacity: 0;
}
.timeline-container h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
}
.timeline {
position: relative;
display: flex;
justify-content: space-between;
padding: 2rem 0;
}
.timeline-track {
position: absolute;
top: 3rem;
left: 5%;
right: 5%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
}
.timeline-indicator {
position: absolute;
top: 3rem;
left: 0;
width: 25%;
height: 4px;
background: var(--color-primary);
border-radius: 2px;
transition: left 0.8s ease-in-out;
}
.timeline-phase {
flex: 1;
text-align: center;
position: relative;
opacity: 0;
transform: scale(0.8);
}
.phase-icon {
font-size: 2.5rem;
margin-bottom: 0.5rem;
filter: grayscale(1);
opacity: 0.5;
transition: all 0.3s ease;
}
.timeline-phase.active .phase-icon {
filter: grayscale(0);
opacity: 1;
}
.phase-time {
font-weight: 600;
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.phase-info h4 {
color: var(--color-light);
margin: 0.5rem 0;
font-size: 1.1rem;
}
.phase-info p {
font-size: 0.9rem;
opacity: 0.8;
}
/* Équipement */
.equipment-section {
margin-bottom: 3rem;
}
.equipment-section h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
}
.equipment-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
opacity: 0;
}
.equipment-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 1.5rem;
border: 2px solid transparent;
transition: all 0.3s ease;
animation: fadeInUp 0.8s ease-out forwards;
}
.equipment-card.essential {
border-color: var(--color-primary);
background: rgba(255, 215, 0, 0.05);
}
.equipment-card:hover {
transform: translateY(-5px);
background: rgba(255, 255, 255, 0.08);
}
.equipment-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.equipment-name {
font-weight: 600;
color: var(--color-light);
}
.essential-badge {
background: var(--color-primary);
color: var(--color-dark);
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
}
.equipment-desc {
font-size: 0.9rem;
opacity: 0.8;
}
/* Conseils */
.tips-section h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
}
.tips-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
.tip-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
padding: 1.5rem;
text-align: center;
transition: all 0.3s ease;
animation: fadeInUp 0.8s ease-out forwards;
}
.tip-card:hover {
transform: translateY(-5px);
background: rgba(255, 255, 255, 0.08);
}
.tip-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.tip-card h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
.tip-card p {
font-size: 0.9rem;
opacity: 0.8;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.timeline {
flex-direction: column;
gap: 2rem;
}
.timeline-track {
display: none;
}
.timeline-indicator {
display: none;
}
.equipment-grid, .tips-grid {
grid-template-columns: 1fr;
}
}
</style>
-430
View File
@@ -1,430 +0,0 @@
<script>
import { onMount } from 'svelte';
import { t } from '../lib/i18n.js';
import anime from '../lib/anime.js';
let visible = false;
let selectedLocation = 'moule';
let calculationInProgress = false;
let results = null;
const locations = [
{ id: 'moule', name: 'Le Moule', lat: 16.3333, lng: -61.3472 },
{ id: 'deshaies', name: 'Deshaies', lat: 16.3039, lng: -61.7966 },
{ id: 'sainte-anne', name: 'Sainte-Anne', lat: 16.2262, lng: -61.3809 },
{ id: 'port-louis', name: 'Port-Louis', lat: 16.4180, lng: -61.5312 },
{ id: 'marie-galante', name: 'Marie-Galante', lat: 15.9500, lng: -61.2667 },
{ id: 'soufriere', name: 'Route de la Soufrière', lat: 16.0439, lng: -61.6639 },
{ id: 'petite-terre', name: 'Petite-Terre', lat: 16.1700, lng: -61.1200 }
];
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
// Animation d'entrée
anime.timeline({
easing: 'easeOutQuad'
})
.add({
targets: '.section-header',
translateY: [30, 0],
opacity: [0, 1],
duration: 800
})
.add({
targets: '.calculator-container',
translateY: [50, 0],
opacity: [0, 1],
duration: 1000
}, '-=400')
.add({
targets: '.location-option',
translateX: [-20, 0],
opacity: [0, 1],
duration: 600,
delay: anime.stagger(50)
}, '-=600');
}
});
});
const section = document.getElementById('predictions');
if (section) observer.observe(section);
return () => observer.disconnect();
});
function selectLocation(id) {
selectedLocation = id;
calculate();
// Animation de sélection
anime({
targets: `.location-option`,
scale: 1,
duration: 300
});
anime({
targets: `[data-location="${id}"]`,
scale: 1.05,
duration: 300,
easing: 'easeOutElastic(1, .5)'
});
}
function calculate() {
calculationInProgress = true;
results = null;
// Animation du calcul
anime({
targets: '.calculating-indicator',
opacity: [0, 1],
duration: 300
});
// Animation des étoiles pendant le calcul
anime({
targets: '.star-indicator',
rotate: 360,
duration: 2000,
easing: 'linear',
loop: true
});
setTimeout(() => {
const location = locations.find(l => l.id === selectedLocation);
// Calculs simplifiés
const baseDate = new Date('2025-07-22');
const latitudeAdjustment = (location.lat - 16.25) * 0.5;
const heliacalDate = new Date(baseDate);
heliacalDate.setDate(baseDate.getDate() + Math.round(latitudeAdjustment));
results = {
location: location.name,
heliacalDate: heliacalDate.toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric'
}),
bestTime: '05h00 - 05h30',
duration: '15-20 minutes',
azimuth: '117° (ESE)',
altitude: '57.1°',
magnitude: '-1.46'
};
calculationInProgress = false;
// Animation des résultats
anime.timeline({
easing: 'easeOutQuad'
})
.add({
targets: '.calculating-indicator',
opacity: [1, 0],
duration: 300
})
.add({
targets: '.results-container',
translateY: [20, 0],
opacity: [0, 1],
duration: 600
})
.add({
targets: '.result-item',
translateX: [20, 0],
opacity: [0, 1],
duration: 400,
delay: anime.stagger(100)
}, '-=400')
.add({
targets: '.result-value',
scale: [0.8, 1],
duration: 600,
delay: anime.stagger(100),
easing: 'easeOutElastic(1, .5)'
}, '-=600');
}, 1500);
}
// Calculer automatiquement au chargement
onMount(() => {
setTimeout(() => {
if (visible) calculate();
}, 1000);
});
</script>
<section id="predictions" class="predictions">
<div class="container">
<div class="section-header" class:visible>
<h2>{$t('predictions_title')}</h2>
<p class="section-subtitle">
{$t('predictions_subtitle')}
</p>
</div>
<div class="calculator-container" class:visible>
<div class="location-selector">
<h3>{$t('predictions_select_location')}</h3>
<div class="locations-grid">
{#each locations as location}
<button
class="location-option"
class:active={selectedLocation === location.id}
data-location={location.id}
on:click={() => selectLocation(location.id)}
>
<span class="location-name">{location.name}</span>
<span class="location-coords">{location.lat}°N</span>
</button>
{/each}
</div>
</div>
<div class="results-section">
{#if calculationInProgress}
<div class="calculating-indicator">
<div class="star-indicator"></div>
<p>Calcul en cours...</p>
</div>
{/if}
{#if results && !calculationInProgress}
<div class="results-container">
<h3>{$t('predictions_results_for')} {results.location}</h3>
<div class="results-grid">
<div class="result-item highlight">
<div class="result-icon">🌅</div>
<div class="result-label">{$t('predictions_heliacal_rising')}</div>
<div class="result-value">{results.heliacalDate}</div>
</div>
<div class="result-item">
<div class="result-icon"></div>
<div class="result-label">{$t('predictions_best_observation')}</div>
<div class="result-value">{results.bestTime}</div>
</div>
<div class="result-item">
<div class="result-icon">⏱️</div>
<div class="result-label">{$t('predictions_visibility_duration')}</div>
<div class="result-value">{results.duration}</div>
</div>
<div class="result-item">
<div class="result-icon">🧭</div>
<div class="result-label">{$t('predictions_azimuth')}</div>
<div class="result-value">{results.azimuth}</div>
</div>
<div class="result-item">
<div class="result-icon">📐</div>
<div class="result-label">{$t('predictions_max_altitude')}</div>
<div class="result-value">{results.altitude}</div>
</div>
<div class="result-item">
<div class="result-icon"></div>
<div class="result-label">{$t('predictions_magnitude')}</div>
<div class="result-value">{results.magnitude}</div>
</div>
</div>
<div class="observation-tips">
<h4>💡 Conseils d'observation</h4>
<ul>
<li>Arrivez 30 minutes avant pour habituer vos yeux</li>
<li>Cherchez vers l'Est-Sud-Est (ESE)</li>
<li>Sirius apparaîtra comme l'étoile la plus brillante</li>
<li>Conditions idéales : ciel dégagé, sans Lune</li>
</ul>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.predictions {
padding: var(--spacing-xl) 0;
background: var(--gradient-night);
position: relative;
overflow: hidden;
}
.calculator-container {
opacity: 0;
margin-top: var(--spacing-lg);
}
.location-selector {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
margin-bottom: 2rem;
}
.location-selector h3 {
color: var(--color-primary);
margin-bottom: 1.5rem;
text-align: center;
}
.locations-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.location-option {
background: rgba(255, 255, 255, 0.05);
border: 2px solid transparent;
border-radius: 15px;
padding: 1rem;
cursor: pointer;
transition: all 0.3s ease;
opacity: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.location-option:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--color-primary);
}
.location-option.active {
background: rgba(255, 215, 0, 0.1);
border-color: var(--color-primary);
}
.location-name {
font-weight: 600;
color: var(--color-light);
}
.location-coords {
font-size: 0.9rem;
color: var(--color-primary);
opacity: 0.8;
}
.calculating-indicator {
text-align: center;
padding: 3rem;
opacity: 0;
}
.star-indicator {
font-size: 3rem;
display: inline-block;
margin-bottom: 1rem;
}
.results-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
}
.results-container h3 {
color: var(--color-primary);
text-align: center;
margin-bottom: 2rem;
font-size: 1.5rem;
}
.results-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.result-item {
background: rgba(0, 0, 0, 0.3);
border-radius: 15px;
padding: 1.5rem;
text-align: center;
}
.result-item.highlight {
background: rgba(255, 215, 0, 0.1);
border: 2px solid var(--color-primary);
}
.result-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.result-label {
font-size: 0.9rem;
color: var(--color-light);
opacity: 0.8;
margin-bottom: 0.5rem;
}
.result-value {
font-size: 1.2rem;
font-weight: 700;
color: var(--color-primary);
}
.observation-tips {
background: rgba(255, 215, 0, 0.05);
border-radius: 15px;
padding: 1.5rem;
margin-top: 2rem;
}
.observation-tips h4 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.observation-tips ul {
list-style: none;
padding: 0;
}
.observation-tips li {
padding: 0.5rem 0;
padding-left: 1.5rem;
position: relative;
}
.observation-tips li::before {
content: "✦";
position: absolute;
left: 0;
color: var(--color-primary);
}
@media (max-width: 768px) {
.locations-grid {
grid-template-columns: 1fr;
}
.results-grid {
grid-template-columns: 1fr;
}
.location-selector, .results-container {
padding: 1.5rem;
}
}
</style>
-375
View File
@@ -1,375 +0,0 @@
<script>
import { onMount } from 'svelte';
let visible = false;
let activeTab = 'mechanics';
onMount(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
visible = true;
}
});
});
const section = document.getElementById('science');
if (section) observer.observe(section);
return () => observer.disconnect();
});
const tabs = [
{ id: 'mechanics', label: 'Mécanique Céleste', icon: '🌌' },
{ id: 'arcus', label: 'Arcus Visionis', icon: '📐' },
{ id: 'factors', label: 'Facteurs d\'Influence', icon: '🌡️' }
];
</script>
<section id="science" class="science">
<div class="container">
<div class="section-header" class:visible>
<h2>La Science du Lever Héliaque</h2>
<p class="section-subtitle">
Comprendre la danse cosmique entre Terre, Soleil et Sirius
</p>
</div>
<div class="science-content" class:visible>
<div class="tabs">
{#each tabs as tab}
<button
class="tab-button"
class:active={activeTab === tab.id}
on:click={() => activeTab = tab.id}
>
<span class="tab-icon">{tab.icon}</span>
<span class="tab-label">{tab.label}</span>
</button>
{/each}
</div>
<div class="tab-content">
{#if activeTab === 'mechanics'}
<div class="content-section">
<h3>Le Ballet Cosmique</h3>
<p>
Le lever héliaque est le fruit d'une chorégraphie céleste précise.
Pendant environ 70 jours, Sirius reste invisible, cachée par l'éclat du Soleil.
Son retour à l'aube marque un moment unique où l'étoile se lève juste avant
le Soleil, visible quelques instants dans les lueurs du crépuscule matinal.
</p>
<div class="diagram card">
<div class="celestial-body sun">
<span>☀️</span>
<label>Soleil</label>
</div>
<div class="orbit-path"></div>
<div class="celestial-body earth">
<span>🌍</span>
<label>Terre</label>
</div>
<div class="celestial-body sirius">
<span></span>
<label>Sirius</label>
</div>
</div>
<p>
La Terre, dans son mouvement orbital annuel, modifie notre perspective
sur les étoiles. Quand le Soleil se trouve entre nous et Sirius,
l'étoile devient invisible. C'est seulement quand l'angle devient
favorable que Sirius réapparaît.
</p>
</div>
{/if}
{#if activeTab === 'arcus'}
<div class="content-section">
<h3>L'Arc de Visibilité</h3>
<p>
L'Arcus Visionis est l'angle critique : le Soleil doit être à environ
9-10° sous l'horizon pour que Sirius devienne visible. C'est un équilibre
délicat entre la brillance de l'étoile et la luminosité du ciel.
</p>
<div class="arcus-diagram card">
<div class="horizon-line">
<span>Horizon</span>
</div>
<div class="angle-indicator">
<div class="angle-arc"></div>
<span class="angle-value">9-10°</span>
</div>
<div class="sirius-position">
<span>⭐ Sirius</span>
</div>
<div class="sun-position">
<span>☀️ Soleil</span>
</div>
</div>
<p>
Pour Sirius, avec sa magnitude exceptionnelle de -1.46, cet angle
est relativement faible comparé à d'autres étoiles moins brillantes.
C'est pourquoi elle est l'une des premières à être visible à l'aube.
</p>
</div>
{/if}
{#if activeTab === 'factors'}
<div class="content-section">
<h3>Les Variables en Jeu</h3>
<div class="factors-grid">
<div class="factor-card card">
<div class="factor-icon">🌟</div>
<h4>Magnitude</h4>
<p>
Sirius brille à -1.46, la rendant visible même dans des conditions
difficiles. Plus une étoile est brillante, plus tôt elle perce
les lueurs de l'aube.
</p>
</div>
<div class="factor-card card">
<div class="factor-icon">🗺️</div>
<h4>Latitude</h4>
<p>
À 16°N en Guadeloupe, les astres se lèvent presque verticalement.
Cette géométrie tropicale influence la durée du crépuscule et
la visibilité.
</p>
</div>
<div class="factor-card card">
<div class="factor-icon">💨</div>
<h4>Atmosphère</h4>
<p>
L'humidité caribéenne et les brumes de sable peuvent retarder
la visibilité de plusieurs jours. Un ciel sec et clair est idéal.
</p>
</div>
</div>
</div>
{/if}
</div>
</div>
</div>
</section>
<style>
.science {
padding: var(--spacing-xl) 0;
background: linear-gradient(135deg, #0a0e27 0%, #1a1a2e 100%);
}
.science-content {
opacity: 0;
transform: translateY(30px);
transition: all 0.8s ease-out 0.3s;
}
.science-content.visible {
opacity: 1;
transform: translateY(0);
}
.tabs {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: var(--spacing-lg);
flex-wrap: wrap;
}
.tab-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 215, 0, 0.3);
border-radius: 50px;
color: var(--color-light);
cursor: pointer;
transition: var(--transition-base);
}
.tab-button:hover,
.tab-button.active {
background: rgba(255, 215, 0, 0.2);
border-color: var(--color-primary);
color: var(--color-primary);
}
.tab-icon {
font-size: 1.5rem;
}
.content-section {
max-width: 800px;
margin: 0 auto;
}
.content-section h3 {
color: var(--color-primary);
margin-bottom: 1rem;
}
.content-section p {
margin-bottom: 1.5rem;
line-height: 1.8;
}
/* Diagramme orbital */
.diagram {
position: relative;
height: 300px;
margin: 2rem 0;
background: rgba(0, 0, 0, 0.3);
}
.celestial-body {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.celestial-body span {
font-size: 3rem;
}
.celestial-body label {
font-size: 0.9rem;
color: var(--color-primary);
}
.sun {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.earth {
top: 20%;
left: 20%;
}
.sirius {
top: 20%;
right: 20%;
}
.orbit-path {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 200px;
border: 2px dashed rgba(255, 215, 0, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
}
/* Diagramme Arcus */
.arcus-diagram {
position: relative;
height: 250px;
margin: 2rem 0;
overflow: hidden;
}
.horizon-line {
position: absolute;
bottom: 50%;
left: 0;
right: 0;
height: 2px;
background: var(--color-primary);
display: flex;
align-items: center;
justify-content: center;
}
.horizon-line span {
background: var(--color-dark);
padding: 0 1rem;
color: var(--color-primary);
}
.sirius-position {
position: absolute;
top: 30%;
left: 30%;
}
.sun-position {
position: absolute;
bottom: 20%;
right: 30%;
}
.angle-indicator {
position: absolute;
bottom: 50%;
right: 40%;
}
.angle-arc {
width: 60px;
height: 60px;
border: 2px solid var(--color-accent);
border-radius: 50%;
border-bottom: none;
border-left: none;
}
.angle-value {
position: absolute;
top: -10px;
right: -30px;
color: var(--color-accent);
font-weight: 600;
}
/* Grille des facteurs */
.factors-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-md);
margin-top: 2rem;
}
.factor-card {
text-align: center;
}
.factor-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.factor-card h4 {
color: var(--color-primary);
margin-bottom: 0.5rem;
}
@media (max-width: 768px) {
.tabs {
flex-direction: column;
}
.tab-button {
width: 100%;
justify-content: center;
}
.diagram {
height: 250px;
}
}
</style>
+192
View File
@@ -0,0 +1,192 @@
---
pagination:
data: locales
size: 1
alias: locale
permalink: "{% if locale == baseLocale %}/culture-savoirs/index.html{% else %}/{{ locale }}/culture-savoirs/index.html{% endif %}"
layout: layouts/base.njk
pageKey: culture
slug: "culture-savoirs/"
eleventyComputed:
pageTitle: "{{ 'culture_title' | t(locale) }}"
---
<section class="section">
<div class="container">
<div class="section-header center">
<p class="section-eyebrow">Sirius Gwada</p>
<h1>{{ "culture_title" | t(locale) }}</h1>
<p class="pattern-strip adinkra" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "culture_subtitle" | t(locale) }}</p>
</div>
<div class="grid-auto">
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">🏺</div>
<h3>{{ "culture_egypt_title" | t(locale) }}</h3>
<p>{{ "culture_egypt_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">🌍</div>
<h3>{{ "culture_dogon_title" | t(locale) }}</h3>
<p>{{ "culture_dogon_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">⛵</div>
<h3>{{ "culture_caribbean_title" | t(locale) }}</h3>
<p>{{ "culture_caribbean_desc" | t(locale) }}</p>
</div>
</div>
<h2 class="center" style="margin-top: var(--space-7)">{{ "culture_timeline_title" | t(locale) }}</h2>
<div class="timeline-v" style="margin-top: var(--space-5)">
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "culture_timeline_1_date" | t(locale) }}</p>
<h4>{{ "culture_timeline_1_title" | t(locale) }}</h4>
<p>{{ "culture_timeline_1_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "culture_timeline_2_date" | t(locale) }}</p>
<h4>{{ "culture_timeline_2_title" | t(locale) }}</h4>
<p>{{ "culture_timeline_2_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "culture_timeline_3_date" | t(locale) }}</p>
<h4>{{ "culture_timeline_3_title" | t(locale) }}</h4>
<p>{{ "culture_timeline_3_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "culture_timeline_4_date" | t(locale) }}</p>
<h4>{{ "culture_timeline_4_title" | t(locale) }}</h4>
<p>{{ "culture_timeline_4_desc" | t(locale) }}</p>
</div>
</div>
</div>
</section>
<section class="section" style="background: var(--surface-sunk)">
<div class="container">
<div class="section-header center">
<h2>{{ "dogon_title" | t(locale) }}</h2>
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "dogon_intro" | t(locale) }}</p>
</div>
<fieldset class="tabs">
<legend class="visually-hidden">{{ "dogon_title" | t(locale) }}</legend>
<input type="radio" name="dogon-tab" id="dogon-tab-cosmogony" class="tab-input" checked>
<input type="radio" name="dogon-tab" id="dogon-tab-system" class="tab-input">
<input type="radio" name="dogon-tab" id="dogon-tab-astronomy" class="tab-input">
<input type="radio" name="dogon-tab" id="dogon-tab-heritage" class="tab-input">
<div class="tab-list" role="tablist">
<label class="tab-label" for="dogon-tab-cosmogony">{{ "dogon_tab_cosmogony" | t(locale) }}</label>
<label class="tab-label" for="dogon-tab-system">{{ "dogon_tab_system" | t(locale) }}</label>
<label class="tab-label" for="dogon-tab-astronomy">{{ "dogon_tab_astronomy" | t(locale) }}</label>
<label class="tab-label" for="dogon-tab-heritage">{{ "dogon_tab_heritage" | t(locale) }}</label>
</div>
<div class="tab-panels">
<div class="tab-panel prose center">
<p>{{ "dogon_cosmogony_p1" | t(locale) }}</p>
<p>{{ "dogon_cosmogony_p2" | t(locale) }}</p>
<div class="card">
<h3>{{ "dogon_cosmogony_nommo_title" | t(locale) }}</h3>
<p>{{ "dogon_cosmogony_nommo_desc" | t(locale) }}</p>
</div>
</div>
<div class="tab-panel">
<div class="grid-auto">
<div class="info-card">
<h3>{{ "dogon_system_sigutolo_title" | t(locale) }}</h3>
<p>{{ "dogon_system_sigutolo_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "dogon_system_potolo_title" | t(locale) }}</h3>
<p>{{ "dogon_system_potolo_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "dogon_system_emmeya_title" | t(locale) }}</h3>
<p>{{ "dogon_system_emmeya_desc" | t(locale) }}</p>
</div>
</div>
<div class="card prose center" style="margin-top: var(--space-4)">
<h3>{{ "dogon_mystery_title" | t(locale) }}</h3>
<div class="grid-auto">
<div>
<h4>{{ "dogon_mystery_theory1_title" | t(locale) }}</h4>
<p>{{ "dogon_mystery_theory1_desc" | t(locale) }}</p>
</div>
<div>
<h4>{{ "dogon_mystery_theory2_title" | t(locale) }}</h4>
<p>{{ "dogon_mystery_theory2_desc" | t(locale) }}</p>
</div>
<div>
<h4>{{ "dogon_mystery_theory3_title" | t(locale) }}</h4>
<p>{{ "dogon_mystery_theory3_desc" | t(locale) }}</p>
</div>
</div>
</div>
<div class="card prose center" style="margin-top: var(--space-4)">
<h3>{{ "dogon_calc_example_title" | t(locale) }}</h3>
<p>{{ "dogon_calc_example_text" | t(locale) }}</p>
</div>
</div>
<div class="tab-panel">
<div class="grid-auto">
<div class="info-card">
<h3>{{ "dogon_astronomy_geometry_title" | t(locale) }}</h3>
<p>{{ "dogon_astronomy_geometry_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "dogon_astronomy_calendar_title" | t(locale) }}</h3>
<p>{{ "dogon_astronomy_calendar_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "dogon_astronomy_planets_title" | t(locale) }}</h3>
<p>{{ "dogon_astronomy_planets_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "dogon_astronomy_universe_title" | t(locale) }}</h3>
<p>{{ "dogon_astronomy_universe_desc" | t(locale) }}</p>
</div>
</div>
</div>
<div class="tab-panel">
<div class="timeline-v">
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "dogon_heritage_1_date" | t(locale) }}</p>
<p>{{ "dogon_heritage_1_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "dogon_heritage_2_date" | t(locale) }}</p>
<p>{{ "dogon_heritage_2_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "dogon_heritage_3_date" | t(locale) }}</p>
<p>{{ "dogon_heritage_3_desc" | t(locale) }}</p>
</div>
<div class="timeline-v-item">
<p class="timeline-v-date">{{ "dogon_heritage_4_date" | t(locale) }}</p>
<p>{{ "dogon_heritage_4_desc" | t(locale) }}</p>
</div>
</div>
<div class="card prose center" style="margin-top: var(--space-5)">
<h3>{{ "dogon_heritage_diaspora_title" | t(locale) }}</h3>
<p>{{ "dogon_heritage_diaspora_desc" | t(locale) }}</p>
<div class="grid-auto" style="margin-top: var(--space-3); text-align: left">
<p>🇭🇹 {{ "dogon_heritage_diaspora_haiti" | t(locale) }}</p>
<p>🇬🇵 {{ "dogon_heritage_diaspora_guadeloupe" | t(locale) }}</p>
<p>🇲🇶 {{ "dogon_heritage_diaspora_martinique" | t(locale) }}</p>
<p>🇯🇲 {{ "dogon_heritage_diaspora_jamaica" | t(locale) }}</p>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</section>
-90
View File
@@ -1,90 +0,0 @@
// Données des prédictions du lever héliaque
export const predictionsData = {
"Pointe des Châteaux": {
coords: { lat: 16.2476, lon: -61.1771, alt: 30 },
description: "Point de vue élevé avec panorama dégagé vers l'Est sur l'Atlantique",
predictions: {
"2025": { date: "22 juillet", time: "05:12" },
"2026": { date: "22 juillet", time: "05:12" },
"2027": { date: "22 juillet", time: "05:13" },
"2028": { date: "21 juillet", time: "05:13" },
"2029": { date: "22 juillet", time: "05:14" }
}
},
"Pointe de la Grande Vigie": {
coords: { lat: 16.5098, lon: -61.4666, alt: 81 },
description: "Point le plus septentrional de la Guadeloupe, horizon marin parfait",
predictions: {
"2025": { date: "22 juillet", time: "05:11" },
"2026": { date: "22 juillet", time: "05:12" },
"2027": { date: "22 juillet", time: "05:12" },
"2028": { date: "21 juillet", time: "05:12" },
"2029": { date: "22 juillet", time: "05:13" }
}
},
"Duzer (Sainte-Rose)": {
coords: { lat: 16.3361, lon: -61.7430, alt: 20 },
description: "Site nécessitant un point dégagé vers l'Est sur les mornes",
predictions: {
"2025": { date: "22 juillet", time: "05:13" },
"2026": { date: "22 juillet", time: "05:14" },
"2027": { date: "22 juillet", time: "05:14" },
"2028": { date: "21 juillet", time: "05:14" },
"2029": { date: "22 juillet", time: "05:15" }
}
},
"Saint-Félix (Le Gosier)": {
coords: { lat: 16.2005, lon: -61.4605, alt: 5 },
description: "Plage avec observation au niveau de la mer",
predictions: {
"2025": { date: "22 juillet", time: "05:13" },
"2026": { date: "22 juillet", time: "05:13" },
"2027": { date: "22 juillet", time: "05:14" },
"2028": { date: "21 juillet", time: "05:14" },
"2029": { date: "22 juillet", time: "05:15" }
}
},
"Grande Pointe (Trois-Rivières)": {
coords: { lat: 15.9696, lon: -61.6308, alt: 42 },
description: "Site côtier avec altitude intéressante pour l'observation",
predictions: {
"2025": { date: "22 juillet", time: "05:13" },
"2026": { date: "22 juillet", time: "05:14" },
"2027": { date: "22 juillet", time: "05:14" },
"2028": { date: "21 juillet", time: "05:15" },
"2029": { date: "22 juillet", time: "05:15" }
}
},
"Petit-Pérou": {
coords: { lat: 16.0517, lon: -61.5567, alt: 10 },
description: "Site des roches gravées, observation depuis la côte",
predictions: {
"2025": { date: "22 juillet", time: "05:14" },
"2026": { date: "22 juillet", time: "05:14" },
"2027": { date: "22 juillet", time: "05:15" },
"2028": { date: "21 juillet", time: "05:15" },
"2029": { date: "22 juillet", time: "05:16" }
}
},
"Pointe du Vieux-Fort": {
coords: { lat: 15.9483, lon: -61.7072, alt: 20 },
description: "Point le plus méridional de Basse-Terre, vue sur le canal des Saintes",
predictions: {
"2025": { date: "22 juillet", time: "05:14" },
"2026": { date: "22 juillet", time: "05:14" },
"2027": { date: "22 juillet", time: "05:15" },
"2028": { date: "21 juillet", time: "05:15" },
"2029": { date: "22 juillet", time: "05:16" }
}
}
};
export const years = ["2025", "2026", "2027", "2028", "2029"];
export function calculateObservationWindow(dateStr) {
const day = parseInt(dateStr.split(' ')[0]);
const month = dateStr.split(' ')[1];
const startDay = day - 2;
const endDay = day + 2;
return `${startDay} au ${endDay} ${month}`;
}
-85
View File
@@ -1,85 +0,0 @@
// Données simplifiées des continents en GeoJSON
// Source: Natural Earth (simplified)
export const worldData = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": { "name": "Africa" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-17.5, 35], [-5, 36], [10, 37], [35, 30], [42, 15],
[51, 12], [51, -2], [41, -12], [35, -25], [28, -33],
[20, -35], [18, -35], [12, -18], [10, -5], [8, 5],
[-5, 5], [-17, 15], [-17.5, 35]
]]
}
},
{
"type": "Feature",
"properties": { "name": "Europe" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-10, 36], [-5, 43], [0, 50], [10, 54], [20, 59],
[30, 60], [40, 55], [30, 45], [20, 40], [10, 45],
[0, 40], [-10, 36]
]]
}
},
{
"type": "Feature",
"properties": { "name": "Asia" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[26, 40], [40, 45], [60, 50], [80, 55], [100, 60],
[120, 60], [140, 50], [145, 40], [135, 30], [120, 20],
[100, 10], [80, 15], [70, 25], [60, 30], [45, 35],
[26, 40]
]]
}
},
{
"type": "Feature",
"properties": { "name": "North America" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-170, 65], [-165, 68], [-150, 70], [-130, 70], [-100, 70],
[-80, 68], [-65, 60], [-55, 50], [-65, 45], [-75, 40],
[-80, 35], [-85, 30], [-90, 25], [-95, 20], [-105, 20],
[-110, 25], [-115, 30], [-120, 35], [-125, 40], [-130, 45],
[-135, 50], [-140, 55], [-150, 58], [-160, 60], [-170, 65]
]]
}
},
{
"type": "Feature",
"properties": { "name": "South America" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[-35, -5], [-38, -15], [-40, -20], [-48, -25], [-58, -35],
[-65, -45], [-70, -55], [-68, -55], [-73, -45], [-72, -35],
[-70, -25], [-75, -15], [-78, -5], [-80, 0], [-77, 5],
[-75, 10], [-70, 12], [-60, 8], [-50, 5], [-35, -5]
]]
}
},
{
"type": "Feature",
"properties": { "name": "Australia" },
"geometry": {
"type": "Polygon",
"coordinates": [[
[113, -22], [130, -12], [135, -12], [140, -18], [145, -20],
[150, -25], [153, -30], [150, -35], [145, -38], [140, -38],
[135, -35], [130, -32], [125, -32], [120, -35], [115, -35],
[113, -30], [113, -22]
]]
}
}
]
};
+93
View File
@@ -0,0 +1,93 @@
---
pagination:
data: locales
size: 1
alias: locale
permalink: "{% if locale == baseLocale %}/index.html{% else %}/{{ locale }}/index.html{% endif %}"
layout: layouts/base.njk
pageKey: home
slug: ""
eleventyComputed:
pageTitle: "{{ 'hero_title1' | t(locale) }} {{ 'hero_title2' | t(locale) }}"
---
<section class="hero">
<div class="hero-orbits" aria-hidden="true">
<div class="sun"></div>
<div class="orbit" style="width:5rem;height:5rem;animation-duration:6s"><span></span></div>
<div class="orbit" style="width:8rem;height:8rem;animation-duration:10s"><span></span></div>
<div class="orbit" style="width:11rem;height:11rem;animation-duration:14s"><span></span></div>
<div class="orbit" style="width:14rem;height:14rem;animation-duration:18s"><span></span></div>
<div class="orbit sirius" style="width:18rem;height:18rem;animation-duration:26s"><span></span></div>
</div>
<div class="container center hero-content">
<p class="section-eyebrow">{{ "hero_eyebrow" | t(locale) }}</p>
<h1 class="hero-title">
<span>{{ "hero_title1" | t(locale) }}</span>
<span class="accent">{{ "hero_title2" | t(locale) }}</span>
</h1>
<p class="hero-subtitle">{{ "hero_subtitle" | t(locale) }}</p>
<p class="hero-description">{{ "hero_description" | t(locale) }}</p>
<p class="hero-date">
{{ "hero_date_label" | t(locale) }}
<strong>{{ nextRising.date | formatDate(locale) }} · {{ nextRising.time }}</strong>
</p>
<div class="cluster hero-cta">
<a class="btn btn-primary comet-ring" href="{{ '/observer/' | localeHref(locale) }}">{{ "hero_cta_predictions" | t(locale) }}</a>
<a class="btn btn-outline" href="{{ '/sirius-science/' | localeHref(locale) }}">{{ "hero_cta_learn" | t(locale) }}</a>
</div>
</div>
</section>
<section class="section" id="intro">
<div class="container">
<div class="section-header center">
<h2>{{ "home_intro_title" | t(locale) }}</h2>
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "home_intro_subtitle" | t(locale) }}</p>
</div>
<div class="prose center stack" style="--stack-space: var(--space-3)">
<p>{{ "home_intro_p1" | t(locale) | safe }}</p>
<p>{{ "home_intro_p2" | t(locale) }}</p>
<p>{{ "home_intro_p3" | t(locale) }}</p>
</div>
<div class="grid-auto" style="margin-top: var(--space-6)">
<div class="feature-card">
<h3>{{ "feature_brightest_title" | t(locale) }}</h3>
<p>{{ "feature_brightest_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<h3>{{ "feature_rising_title" | t(locale) }}</h3>
<p>{{ "feature_rising_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<h3>{{ "feature_heritage_title" | t(locale) }}</h3>
<p>{{ "feature_heritage_desc" | t(locale) }}</p>
</div>
</div>
</div>
</section>
<section class="section" style="background: var(--surface-sunk)">
<div class="container">
<div class="section-header center">
<h2>{{ "wave_showcase_title" | t(locale) }}</h2>
<p class="section-subtitle">{{ "wave_showcase_subtitle" | t(locale) }}</p>
</div>
<ul class="wave-badges">
{% for city in waveCities %}
<li class="wave-badge wave-badge-{{ loop.index0 % 3 }}">
<span class="wave-badge-name">{{ city.name }}</span>
<span class="wave-badge-date">{{ city.date }}</span>
</li>
{% endfor %}
</ul>
<p class="center" style="margin-top: var(--space-5)">
<a class="btn btn-outline" href="{{ '/observer/' | localeHref(locale) }}">{{ "wave_showcase_cta" | t(locale) }}</a>
</p>
</div>
</section>
+81
View File
@@ -0,0 +1,81 @@
<script>
import { onMount, onDestroy } from "svelte";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import { waveCities } from "./world.js";
let mapContainer = $state();
let map;
function markerIcon(active) {
return L.divIcon({
className: "wave-marker" + (active ? " wave-marker-active" : ""),
iconSize: active ? [20, 20] : [14, 14],
iconAnchor: active ? [10, 10] : [7, 7],
});
}
onMount(() => {
map = L.map(mapContainer, { scrollWheelZoom: false });
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 18,
}).addTo(map);
const markers = waveCities.map((city) => {
const isCaribbean = city.name === "Guadeloupe" || city.name === "Martinique";
const marker = L.marker([city.lat, city.lon], {
icon: markerIcon(isCaribbean),
}).addTo(map);
marker.bindPopup(`<strong>${city.name}</strong><br>${city.date}`);
marker.on("popupopen", () => marker.setIcon(markerIcon(true)));
marker.on("popupclose", () => marker.setIcon(markerIcon(isCaribbean)));
return marker;
});
const group = L.featureGroup(markers);
map.fitBounds(group.getBounds().pad(0.15));
});
onDestroy(() => {
map?.remove();
});
</script>
<div
bind:this={mapContainer}
class="wave-map"
role="group"
aria-label="Carte de la progression du lever héliaque à travers l'Afrique et les Caraïbes"
></div>
<style>
.wave-map {
width: 100%;
height: 22rem;
border-radius: var(--radius-md);
overflow: hidden;
}
:global(.wave-marker) {
background: var(--caribbean-teal);
border: 2px solid oklch(20% 0.03 265);
border-radius: 50%;
box-shadow: var(--shadow-sm);
}
:global(.wave-marker-active) {
background: var(--gold-surface);
}
:global(.leaflet-popup-content-wrapper) {
background: var(--surface-raised);
color: var(--ink);
border-radius: var(--radius-sm);
}
:global(.leaflet-popup-tip) {
background: var(--surface-raised);
}
</style>
+24
View File
@@ -0,0 +1,24 @@
const container = document.querySelector('[data-island="globe"]');
if (container && "IntersectionObserver" in window) {
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
observer.disconnect();
Promise.all([
import("svelte"),
import("./SiriusGlobe.svelte"),
]).then(([{ mount }, { default: SiriusGlobe }]) => {
const target = container.querySelector(".island-app");
mount(SiriusGlobe, { target });
container.classList.add("is-enhanced");
});
}
},
{ rootMargin: "200px" }
);
observer.observe(container);
}
+1
View File
@@ -0,0 +1 @@
export { default as waveCities } from "../../_data/waveCities.js";
+81
View File
@@ -0,0 +1,81 @@
<script>
import { onMount, onDestroy } from "svelte";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
let { sites, locale } = $props();
let mapContainer = $state();
let map;
function markerIcon(active) {
return L.divIcon({
className: "site-marker" + (active ? " site-marker-active" : ""),
iconSize: active ? [22, 22] : [16, 16],
iconAnchor: active ? [11, 11] : [8, 8],
});
}
onMount(() => {
map = L.map(mapContainer, {
scrollWheelZoom: false,
});
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 18,
}).addTo(map);
const markers = sites.map((site) => {
const marker = L.marker([site.lat, site.lon], { icon: markerIcon(false) }).addTo(map);
const desc = site[`description_${locale}`] ?? site.description_fr;
marker.bindPopup(
`<strong>${site.name}</strong><br>${desc}<br><small style="color: var(--ink-dim)">${site.lat}°N, ${site.lon}°O — ${site.alt} m</small>`
);
marker.on("popupopen", () => marker.setIcon(markerIcon(true)));
marker.on("popupclose", () => marker.setIcon(markerIcon(false)));
return marker;
});
const group = L.featureGroup(markers);
map.fitBounds(group.getBounds().pad(0.2));
});
onDestroy(() => {
map?.remove();
});
</script>
<div class="observation-map">
<div bind:this={mapContainer} class="leaflet-container-host" role="group" aria-label="Carte des sites d'observation de Sirius en Guadeloupe"></div>
</div>
<style>
.leaflet-container-host {
width: 100%;
height: 22rem;
border-radius: var(--radius-md);
overflow: hidden;
}
:global(.site-marker) {
background: var(--gold-surface);
border: 2px solid oklch(20% 0.03 265);
border-radius: 50%;
box-shadow: var(--shadow-sm);
}
:global(.site-marker-active) {
background: var(--caribbean-teal);
}
:global(.leaflet-popup-content-wrapper) {
background: var(--surface-raised);
color: var(--ink);
border-radius: var(--radius-sm);
}
:global(.leaflet-popup-tip) {
background: var(--surface-raised);
}
</style>
+31
View File
@@ -0,0 +1,31 @@
const container = document.querySelector('[data-island="map"]');
if (container && "IntersectionObserver" in window) {
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
observer.disconnect();
Promise.all([
import("svelte"),
import("./ObservationMap.svelte"),
]).then(([{ mount }, { default: ObservationMap }]) => {
const target = container.querySelector(".island-app");
const sitesData = JSON.parse(document.getElementById("sites-data").textContent);
const locale = document.documentElement.lang;
mount(ObservationMap, {
target,
props: { sites: sitesData, locale },
});
container.classList.add("is-enhanced");
});
}
},
{ rootMargin: "200px" }
);
observer.observe(container);
}
@@ -0,0 +1,92 @@
<script>
import { untrack } from "svelte";
let { sites, year, locale, labels } = $props();
let selectedId = $state(untrack(() => sites[0].id));
let selected = $derived(sites.find((s) => s.id === selectedId));
let prediction = $derived(selected.predictions[year]);
function formatDate(iso) {
const [y, m, d] = iso.split("-").map(Number);
return new Date(y, m - 1, d).toLocaleDateString(locale === "ht" ? "fr" : locale, {
day: "numeric",
month: "long",
year: "numeric",
});
}
function windowLabel(iso) {
const [y, m, d] = iso.split("-").map(Number);
const start = new Date(y, m - 1, d - 2);
const end = new Date(y, m - 1, d + 2);
const fmt = (date) => date.toLocaleDateString(locale === "ht" ? "fr" : locale, { day: "numeric", month: "short" });
return `${fmt(start)} ${fmt(end)}`;
}
</script>
<div class="calculator card">
<label class="calculator-label" for="site-select">{labels.select}</label>
<select id="site-select" bind:value={selectedId} class="calculator-select">
{#each sites as site}
<option value={site.id}>{site.name}</option>
{/each}
</select>
{#if prediction}
<div class="calculator-result">
<p class="calculator-result-label">{labels.heliacal}</p>
<p class="calculator-result-value">{formatDate(prediction.date)} · {prediction.time}</p>
<p class="calculator-result-label">{labels.window}</p>
<p class="calculator-result-window">{windowLabel(prediction.date)}</p>
</div>
{/if}
</div>
<style>
.calculator {
max-width: 28rem;
margin-inline: auto;
}
.calculator-label {
display: block;
font-weight: 700;
margin-bottom: var(--space-2);
}
.calculator-select {
width: 100%;
min-height: var(--tap-target);
padding-inline: var(--space-3);
border-radius: var(--radius-sm);
border: 2px solid var(--border);
background: var(--surface);
color: var(--ink);
font-size: var(--text-1);
}
.calculator-result {
margin-top: var(--space-4);
padding-top: var(--space-4);
border-top: 1px solid var(--border);
text-align: center;
}
.calculator-result-label {
font-size: var(--text-0);
color: var(--ink-soft);
margin-top: var(--space-2);
}
.calculator-result-value {
font-family: var(--font-display);
font-size: var(--text-3);
color: var(--gold);
}
.calculator-result-window {
font-weight: 600;
}
</style>
+24
View File
@@ -0,0 +1,24 @@
import { mount } from "svelte";
import PredictionsCalculator from "./PredictionsCalculator.svelte";
const container = document.querySelector('[data-island="predictions"]');
if (container) {
const target = container.querySelector(".island-app");
const sitesData = JSON.parse(document.getElementById("sites-data").textContent);
mount(PredictionsCalculator, {
target,
props: {
sites: sitesData,
year: Number(target.dataset.year),
locale: target.dataset.locale,
labels: {
select: target.dataset.selectLabel,
heliacal: target.dataset.heliacalLabel,
window: target.dataset.windowLabel,
},
},
});
container.classList.add("is-enhanced");
}
-4
View File
@@ -1,4 +0,0 @@
// Import anime.js v3 for Vite
import anime from 'animejs/lib/anime.es.js';
export default anime;
-232
View File
@@ -1,232 +0,0 @@
import { writable, derived } from 'svelte/store';
// Store pour la langue actuelle
export const currentLanguage = writable('fr');
// Traductions
const translations = {
fr: {
// Navigation
nav_home: 'Accueil',
nav_about: 'À Propos',
nav_predictions: 'Prédictions',
nav_science: 'Science',
nav_culture: 'Culture',
nav_observatory: 'Observatoire',
// Hero
hero_title1: 'Sirius',
hero_title2: 'en Guadeloupe',
hero_subtitle: 'Wep Ronpet - L\'Ouverture de l\'Année Cosmique',
hero_description: 'Le rendez-vous millénaire entre Ciel, Science et Culture',
hero_date: 'Lever héliaque : ',
hero_date_value: '22 Juillet 2025',
hero_cta_predictions: 'Découvrir les Prédictions',
hero_cta_learn: 'En Savoir Plus',
// About
about_title: 'Le Lever Héliaque de Sirius',
about_subtitle: 'Un phénomène astronomique millénaire',
about_intro: 'Le lever héliaque de Sirius est un événement céleste qui a façonné les civilisations depuis des millénaires.',
// Features
feature_astronomy_title: 'Astronomie',
feature_astronomy_desc: 'Sirius, l\'étoile la plus brillante, guide de navigation céleste',
feature_culture_title: 'Culture',
feature_culture_desc: 'De l\'Égypte aux Dogons, un héritage universel',
feature_spirituality_title: 'Spiritualité',
feature_spirituality_desc: 'Renaissance, renouveau et connexion cosmique',
// Predictions
predictions_title: 'Prédictions 2025',
predictions_subtitle: 'Calculez la date exacte pour votre lieu d\'observation',
predictions_select_location: 'Sélectionnez votre lieu',
predictions_results_for: 'Résultats pour',
predictions_heliacal_rising: 'Lever héliaque',
predictions_best_observation: 'Meilleure observation',
predictions_visibility_duration: 'Durée de visibilité',
predictions_azimuth: 'Azimut au lever',
predictions_max_altitude: 'Altitude maximale',
predictions_magnitude: 'Magnitude apparente',
// Science
science_title: 'La Science du Lever Héliaque',
science_subtitle: 'Comprendre le phénomène astronomique',
// Culture
culture_title: 'Héritage Culturel',
culture_subtitle: 'Sirius à travers les civilisations',
culture_timeline_title: 'Chronologie du Savoir',
// Observatory
observatory_title: 'Guide d\'Observation',
observatory_subtitle: 'Tout pour observer Sirius en Guadeloupe',
// Footer
footer_copyright: 'Projet réalisé avec ❤️ par',
footer_and: 'et'
},
en: {
// Navigation
nav_home: 'Home',
nav_about: 'About',
nav_predictions: 'Predictions',
nav_science: 'Science',
nav_culture: 'Culture',
nav_observatory: 'Observatory',
// Hero
hero_title1: 'Sirius',
hero_title2: 'in Guadeloupe',
hero_subtitle: 'Wep Ronpet - The Opening of the Cosmic Year',
hero_description: 'The millennial meeting between Sky, Science and Culture',
hero_date: 'Heliacal rising: ',
hero_date_value: 'July 22, 2025',
hero_cta_predictions: 'Discover Predictions',
hero_cta_learn: 'Learn More',
// About about_title: 'The Heliacal Rising of Sirius',
about_subtitle: 'A millennial astronomical phenomenon',
about_intro: 'The heliacal rising of Sirius is a celestial event that has shaped civilizations for millennia.',
// Features
feature_astronomy_title: 'Astronomy',
feature_astronomy_desc: 'Sirius, the brightest star, celestial navigation guide',
feature_culture_title: 'Culture',
feature_culture_desc: 'From Egypt to the Dogon, a universal heritage',
feature_spirituality_title: 'Spirituality',
feature_spirituality_desc: 'Renaissance, renewal and cosmic connection',
// Predictions
predictions_title: 'Predictions 2025',
predictions_subtitle: 'Calculate the exact date for your observation location',
predictions_select_location: 'Select your location',
predictions_results_for: 'Results for',
predictions_heliacal_rising: 'Heliacal rising',
predictions_best_observation: 'Best observation',
predictions_visibility_duration: 'Visibility duration',
predictions_azimuth: 'Azimuth at rising',
predictions_max_altitude: 'Maximum altitude',
predictions_magnitude: 'Apparent magnitude',
// Science
science_title: 'The Science of Heliacal Rising',
science_subtitle: 'Understanding the astronomical phenomenon',
// Culture
culture_title: 'Cultural Heritage',
culture_subtitle: 'Sirius through civilizations',
culture_timeline_title: 'Timeline of Knowledge',
// Observatory
observatory_title: 'Observation Guide',
observatory_subtitle: 'Everything to observe Sirius in Guadeloupe',
// Footer
footer_copyright: 'Project made with ❤️ by',
footer_and: 'and'
},
ht: {
// Navigation
nav_home: 'Akèy',
nav_about: 'Konsènan',
nav_predictions: 'Prediksyon',
nav_science: 'Syans',
nav_culture: 'Kilti',
nav_observatory: 'Obsèvatwa',
// Hero
hero_title1: 'Sirius',
hero_title2: 'nan Gwadloup',
hero_subtitle: 'Wep Ronpet - Ouvèti Ane Kosmik la',
hero_description: 'Randevou milenè ant Syèl, Syans ak Kilti',
hero_date: 'Leve elyak: ',
hero_date_value: '22 Jiyè 2025',
hero_cta_predictions: 'Dekouvri Prediksyon yo', hero_cta_learn: 'Aprann Plis',
// About
about_title: 'Leve Elyak Sirius la',
about_subtitle: 'Yon fenomèn astwonomik milenè',
about_intro: 'Leve elyak Sirius se yon evènman selès ki fòme sivilizasyon yo depi plizyè milye ane.',
// Features
feature_astronomy_title: 'Astwonomi',
feature_astronomy_desc: 'Sirius, zetwal ki pi klere a, gid navigasyon selès',
feature_culture_title: 'Kilti',
feature_culture_desc: 'Depi Lejip rive Dogon yo, yon eritaj inivèsèl',
feature_spirituality_title: 'Espiritwalite',
feature_spirituality_desc: 'Renesans, renouvèlman ak koneksyon kosmik',
// Predictions
predictions_title: 'Prediksyon 2025',
predictions_subtitle: 'Kalkile dat egzak pou kote w ap obsève a',
predictions_select_location: 'Chwazi kote ou ye a',
predictions_results_for: 'Rezilta pou',
predictions_heliacal_rising: 'Leve elyak',
predictions_best_observation: 'Pi bon obsèvasyon',
predictions_visibility_duration: 'Dire vizibilite',
predictions_azimuth: 'Azimit nan leve',
predictions_max_altitude: 'Altitid maksimòm',
predictions_magnitude: 'Mayitid aparan',
// Science
science_title: 'Syans Leve Elyak la',
science_subtitle: 'Konprann fenomèn astwonomik la',
// Culture
culture_title: 'Eritaj Kiltirèl',
culture_subtitle: 'Sirius atravè sivilizasyon yo',
culture_timeline_title: 'Kwonoloji Konesans lan',
// Observatory
observatory_title: 'Gid Obsèvasyon',
observatory_subtitle: 'Tout bagay pou obsève Sirius nan Gwadloup',
// Footer
footer_copyright: 'Pwojè realize avèk ❤️ pa',
footer_and: 'ak'
}
};
// Store dérivé pour obtenir les traductions de la langue actuelle
export const t = derived(
currentLanguage,
$currentLanguage => key => {
return translations[$currentLanguage]?.[key] || translations['fr'][key] || key;
}
);
// Fonction pour changer de langue
export function setLanguage(lang) {
if (translations[lang]) {
currentLanguage.set(lang);
if (typeof localStorage !== 'undefined') {
localStorage.setItem('preferred-language', lang);
}
}
}
// Initialiser avec la langue sauvegardée ou détecter la langue du navigateur
export function initLanguage() {
if (typeof localStorage !== 'undefined') {
const saved = localStorage.getItem('preferred-language');
if (saved && translations[saved]) {
currentLanguage.set(saved);
return;
}
}
if (typeof navigator !== 'undefined') {
const browserLang = navigator.language.slice(0, 2);
if (translations[browserLang]) {
currentLanguage.set(browserLang);
}
}
}
// Liste des langues disponibles
export const availableLanguages = [
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'en', name: 'English', flag: '🇬🇧' },
{ code: 'ht', name: 'Kreyòl Ayisyen', flag: '🇭🇹' }
];
-8
View File
@@ -1,8 +0,0 @@
import './app.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app'),
})
export default app
+180
View File
@@ -0,0 +1,180 @@
---
pagination:
data: locales
size: 1
alias: locale
permalink: "{% if locale == baseLocale %}/observer/index.html{% else %}/{{ locale }}/observer/index.html{% endif %}"
layout: layouts/base.njk
pageKey: observer
slug: "observer/"
eleventyComputed:
pageTitle: "{{ 'observer_title' | t(locale) }}"
---
<section class="section">
<div class="container">
<div class="section-header center">
<p class="section-eyebrow">Sirius Gwada</p>
<h1>{{ "observer_title" | t(locale) }}</h1>
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "observer_subtitle" | t(locale) }}</p>
</div>
<script type="application/json" id="sites-data">{{ sites | dump | safe }}</script>
<div id="predictions-island" data-island="predictions">
<h2 class="center">{{ "observer_calculator_title" | t(locale) }}</h2>
<p class="section-subtitle center">{{ "observer_calculator_subtitle" | t(locale) }}</p>
<div class="island-app" data-year="{{ nextRising.year }}" data-locale="{{ locale }}"
data-select-label="{{ 'observer_calculator_select_label' | t(locale) }}"
data-heliacal-label="{{ 'observer_calculator_result_heliacal' | t(locale) }}"
data-window-label="{{ 'observer_calculator_result_window' | t(locale) }}"
style="margin-top: var(--space-4)"></div>
<div class="island-fallback card" style="overflow-x: auto; margin-top: var(--space-4)">
<table style="width: 100%; border-collapse: collapse">
<caption class="visually-hidden">{{ "observer_calculator_title" | t(locale) }}</caption>
<thead>
<tr style="text-align: left; border-bottom: 2px solid var(--border)">
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_site" | t(locale) }}</th>
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_date" | t(locale) }}</th>
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_time" | t(locale) }}</th>
</tr>
</thead>
<tbody>
{% for site in sites %}
<tr style="border-bottom: 1px solid var(--border)">
<th scope="row" style="padding: var(--space-2); font-weight: 600; text-align: left">
{{ site.name }}
<div style="font-size: var(--text-0); color: var(--ink-soft); font-weight: 400">
{{ site["description_" + locale] or site.description_fr }}
</div>
</th>
<td style="padding: var(--space-2)">{{ site.predictions[nextRising.year].date | formatDate(locale) }}</td>
<td style="padding: var(--space-2)">{{ site.predictions[nextRising.year].time }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div id="map-island" data-island="map" style="margin-top: var(--space-7)">
<h2 class="center">{{ "observer_map_title" | t(locale) }}</h2>
<p class="section-subtitle center">{{ "observer_map_subtitle" | t(locale) }}</p>
<div class="island-app" style="margin-top: var(--space-4)"></div>
<ul class="island-fallback grid-auto" style="margin-top: var(--space-4); list-style: none">
{% for site in sites %}
<li class="card">
<h3>{{ site.name }}</h3>
<p>{{ site["description_" + locale] or site.description_fr }}</p>
<p style="font-size: var(--text-0); color: var(--ink-dim)">{{ site.lat }}°N, {{ site.lon }}°O — {{ site.alt }} m</p>
</li>
{% endfor %}
</ul>
</div>
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_timeline_title" | t(locale) }}</h2>
<div class="timeline-h card" style="margin-top: var(--space-4)">
<div class="timeline-h-step">
<div class="timeline-h-icon" aria-hidden="true">🌙</div>
<p class="timeline-h-time">{{ "observer_phase1_time" | t(locale) }}</p>
<h4>{{ "observer_phase1_title" | t(locale) }}</h4>
<p>{{ "observer_phase1_desc" | t(locale) }}</p>
</div>
<div class="timeline-h-step">
<div class="timeline-h-icon" aria-hidden="true">🔭</div>
<p class="timeline-h-time">{{ "observer_phase2_time" | t(locale) }}</p>
<h4>{{ "observer_phase2_title" | t(locale) }}</h4>
<p>{{ "observer_phase2_desc" | t(locale) }}</p>
</div>
<div class="timeline-h-step">
<div class="timeline-h-icon" aria-hidden="true">⭐</div>
<p class="timeline-h-time">{{ "observer_phase3_time" | t(locale) }}</p>
<h4>{{ "observer_phase3_title" | t(locale) }}</h4>
<p>{{ "observer_phase3_desc" | t(locale) }}</p>
</div>
<div class="timeline-h-step">
<div class="timeline-h-icon" aria-hidden="true">✨</div>
<p class="timeline-h-time">{{ "observer_phase4_time" | t(locale) }}</p>
<h4>{{ "observer_phase4_title" | t(locale) }}</h4>
<p>{{ "observer_phase4_desc" | t(locale) }}</p>
</div>
</div>
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_equipment_title" | t(locale) }}</h2>
<div class="grid-auto" style="margin-top: var(--space-4)">
<div class="card info-card essential">
<div class="info-card-header">
<h3>{{ "observer_equipment_lamp_name" | t(locale) }}</h3>
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
</div>
<p>{{ "observer_equipment_lamp_desc" | t(locale) }}</p>
</div>
<div class="card info-card essential">
<div class="info-card-header">
<h3>{{ "observer_equipment_compass_name" | t(locale) }}</h3>
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
</div>
<p>{{ "observer_equipment_compass_desc" | t(locale) }}</p>
</div>
<div class="card info-card essential">
<div class="info-card-header">
<h3>{{ "observer_equipment_clothes_name" | t(locale) }}</h3>
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
</div>
<p>{{ "observer_equipment_clothes_desc" | t(locale) }}</p>
</div>
<div class="card info-card">
<h3>{{ "observer_equipment_binoculars_name" | t(locale) }}</h3>
<p>{{ "observer_equipment_binoculars_desc" | t(locale) }}</p>
</div>
<div class="card info-card">
<h3>{{ "observer_equipment_notebook_name" | t(locale) }}</h3>
<p>{{ "observer_equipment_notebook_desc" | t(locale) }}</p>
</div>
</div>
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_tips_title" | t(locale) }}</h2>
<div class="grid-auto" style="margin-top: var(--space-4)">
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">🌤️</div>
<h3>{{ "observer_tip_weather_title" | t(locale) }}</h3>
<p>{{ "observer_tip_weather_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">🌑</div>
<h3>{{ "observer_tip_moon_title" | t(locale) }}</h3>
<p>{{ "observer_tip_moon_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">📱</div>
<h3>{{ "observer_tip_apps_title" | t(locale) }}</h3>
<p>{{ "observer_tip_apps_desc" | t(locale) }}</p>
</div>
<div class="feature-card">
<div style="font-size: var(--text-4)" aria-hidden="true">👥</div>
<h3>{{ "observer_tip_group_title" | t(locale) }}</h3>
<p>{{ "observer_tip_group_desc" | t(locale) }}</p>
</div>
</div>
</div>
</section>
<section class="section" id="globe-island" data-island="globe" style="background: var(--surface-sunk)">
<div class="container">
<div class="section-header center">
<h2>{{ "globe_title" | t(locale) }}</h2>
<p class="section-subtitle">{{ "globe_p1" | t(locale) }}</p>
</div>
<p class="prose center">{{ "globe_p2" | t(locale) }}</p>
<div class="island-app" style="margin-top: var(--space-4)"></div>
<p class="center" style="color: var(--ink-soft); font-size: var(--text-0)">{{ "globe_controls_hint" | t(locale) }}</p>
</div>
</section>
<script type="module" src="/src/islands/predictions/mount.js"></script>
<script type="module" src="/src/islands/map/mount.js"></script>
<script type="module" src="/src/islands/globe/mount.js"></script>
+114
View File
@@ -0,0 +1,114 @@
---
pagination:
data: locales
size: 1
alias: locale
permalink: "{% if locale == baseLocale %}/sirius-science/index.html{% else %}/{{ locale }}/sirius-science/index.html{% endif %}"
layout: layouts/base.njk
pageKey: science
slug: "sirius-science/"
eleventyComputed:
pageTitle: "{{ 'science_title' | t(locale) }}"
---
<section class="section">
<div class="container">
<div class="section-header center">
<p class="section-eyebrow">Sirius Gwada</p>
<h1>{{ "science_title" | t(locale) }}</h1>
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
<p class="section-subtitle">{{ "science_subtitle" | t(locale) }}</p>
</div>
<fieldset class="tabs">
<legend class="visually-hidden">{{ "science_title" | t(locale) }}</legend>
<input type="radio" name="science-tab" id="tab-mechanics" class="tab-input" checked>
<input type="radio" name="science-tab" id="tab-arcus" class="tab-input">
<input type="radio" name="science-tab" id="tab-factors" class="tab-input">
<input type="radio" name="science-tab" id="tab-calc" class="tab-input">
<div class="tab-list" role="tablist">
<label class="tab-label" for="tab-mechanics">{{ "science_tab_mechanics" | t(locale) }}</label>
<label class="tab-label" for="tab-arcus">{{ "science_tab_arcus" | t(locale) }}</label>
<label class="tab-label" for="tab-factors">{{ "science_tab_factors" | t(locale) }}</label>
<label class="tab-label" for="tab-calc">{{ "science_tab_calc" | t(locale) }}</label>
</div>
<div class="tab-panels">
<div class="tab-panel prose center">
<h2>{{ "science_mechanics_title" | t(locale) }}</h2>
<p>{{ "science_mechanics_p1" | t(locale) }}</p>
<div class="diagram-orbit card" aria-hidden="true">
<div class="ring"></div>
<div class="body sun">☀️<span>{{ "science_diagram_sun" | t(locale) }}</span></div>
<div class="body earth">🌍<span>{{ "science_diagram_earth" | t(locale) }}</span></div>
<div class="body sirius">⭐<span>{{ "science_diagram_sirius" | t(locale) }}</span></div>
</div>
<p>{{ "science_mechanics_p2" | t(locale) }}</p>
</div>
<div class="tab-panel prose center">
<h2>{{ "science_arcus_title" | t(locale) }}</h2>
<p>{{ "science_arcus_p1" | t(locale) }}</p>
<div class="diagram-arcus card" aria-hidden="true">
<span class="horizon-label">{{ "science_arcus_horizon_label" | t(locale) }}</span>
<div class="angle"></div>
<span class="angle-label">9-10°</span>
<span class="sirius-mark">⭐</span>
<span class="sun-mark">☀️</span>
</div>
<p>{{ "science_arcus_p2" | t(locale) }}</p>
</div>
<div class="tab-panel">
<div class="grid-auto">
<div class="info-card">
<h3>{{ "science_factor_magnitude_title" | t(locale) }}</h3>
<p>{{ "science_factor_magnitude_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "science_factor_latitude_title" | t(locale) }}</h3>
<p>{{ "science_factor_latitude_desc" | t(locale) }}</p>
</div>
<div class="info-card">
<h3>{{ "science_factor_atmosphere_title" | t(locale) }}</h3>
<p>{{ "science_factor_atmosphere_desc" | t(locale) }}</p>
</div>
</div>
</div>
<div class="tab-panel prose center">
<h2>{{ "science_calc_title" | t(locale) }}</h2>
<div class="card">
<h3>{{ "science_calc_formula_label" | t(locale) }}</h3>
<p style="font-family: ui-monospace, monospace">{{ "science_calc_formula" | t(locale) }}</p>
</div>
<div class="card" style="margin-top: var(--space-3)">
<h3>{{ "science_calc_step1_title" | t(locale) }}</h3>
<p style="font-family: ui-monospace, monospace">{{ "science_calc_step1_value" | t(locale) }}</p>
</div>
<div class="card" style="margin-top: var(--space-3)">
<h3>{{ "science_calc_step2_title" | t(locale) }}</h3>
<p style="font-family: ui-monospace, monospace">
{{ "science_calc_step2_ra" | t(locale) }}<br>
{{ "science_calc_step2_dec" | t(locale) }}
</p>
</div>
<div class="card" style="margin-top: var(--space-3)">
<h3>{{ "science_calc_arcus_title" | t(locale) }}</h3>
<p>{{ "science_calc_arcus_desc" | t(locale) }}</p>
</div>
<div class="card" style="margin-top: var(--space-3)">
<h3>{{ "science_calc_sources_title" | t(locale) }}</h3>
<p>{{ "science_calc_sources_text" | t(locale) }}</p>
</div>
</div>
</div>
</fieldset>
</div>
</section>
+20
View File
@@ -0,0 +1,20 @@
@layer reset, tokens, base, layouts, components, utilities;
@import "./reset.css" layer(reset);
@import "./tokens.css" layer(tokens);
@import "./themes/oki.css" layer(tokens);
@import "./base.css" layer(base);
@import "./layouts.css" layer(layouts);
@import "./patterns.css" layer(components);
@import "./components/nav.css" layer(components);
@import "./components/footer.css" layer(components);
@import "./components/buttons.css" layer(components);
@import "./components/hero.css" layer(components);
@import "./components/cards.css" layer(components);
@import "./components/tabs.css" layer(components);
@import "./components/timeline.css" layer(components);
@import "./components/diagrams.css" layer(components);
@import "./components/associations.css" layer(components);
@import "./components/islands.css" layer(components);
@import "./components/wave-badges.css" layer(components);
@import "./components/comet-ring.css" layer(components);
+88
View File
@@ -0,0 +1,88 @@
@font-face {
font-family: "Fraunces Variable";
src: url("/assets/fonts/fraunces-variable.woff2") format("woff2-variations");
font-weight: 300 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Atkinson Hyperlegible";
src: url("/assets/fonts/atkinson-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Atkinson Hyperlegible";
src: url("/assets/fonts/atkinson-700.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
html {
font-size: 100%;
}
body {
background: var(--surface);
color: var(--ink);
font-family: var(--font-body);
font-size: var(--text-1);
line-height: var(--leading-normal);
}
h1, h2, h3, h4 {
font-family: var(--font-display);
line-height: var(--leading-tight);
letter-spacing: var(--tracking-tight);
text-wrap: balance;
}
h1 { font-size: var(--text-6); }
h2 { font-size: var(--text-5); }
h3 { font-size: var(--text-3); letter-spacing: -0.01em; }
h4 { font-size: var(--text-2); letter-spacing: 0; }
p {
text-wrap: pretty;
}
a {
text-underline-offset: 0.2em;
}
:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
border-radius: var(--radius-sm);
}
.skip-link {
position: absolute;
top: var(--space-2);
left: var(--space-2);
z-index: 100;
padding: var(--space-2) var(--space-3);
background: var(--surface-raised);
color: var(--ink);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-md);
transform: translateY(-150%);
transition: transform var(--duration-fast) var(--ease-out);
}
.skip-link:focus-visible {
transform: translateY(0);
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
+60
View File
@@ -0,0 +1,60 @@
.event-link,
.community-link {
display: flex;
gap: var(--space-3);
align-items: flex-start;
padding-block: var(--space-3);
border-bottom: 1px solid var(--border);
text-decoration: none;
color: var(--ink);
}
.event-link:last-child,
.community-link:last-child {
border-bottom: none;
}
.event-icon,
.community-icon {
font-size: var(--text-3);
flex-shrink: 0;
}
.event-info,
.community-info {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.event-title,
.community-title {
font-weight: 700;
color: var(--gold);
}
.event-platform {
font-size: var(--text-0);
color: var(--ink-soft);
}
.video-frame {
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--radius-md);
overflow: hidden;
}
.video-frame iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
.associations-note {
font-size: var(--text-0);
color: var(--ink-soft);
margin-top: var(--space-4);
}
+45
View File
@@ -0,0 +1,45 @@
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: var(--tap-target);
padding-inline: var(--space-5);
border-radius: var(--radius-pill);
font-weight: 700;
text-decoration: none;
box-shadow: var(--shadow-sm);
transition: transform var(--duration-fast) var(--ease-out),
box-shadow var(--duration-fast) var(--ease-out),
background var(--duration-fast) var(--ease-out);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn:active {
transform: translateY(0);
}
.btn-primary {
background: var(--gold-surface);
color: oklch(20% 0.03 265);
}
.btn-primary:hover {
background: var(--coral-surface);
}
.btn-outline {
background: transparent;
border: 2px solid var(--gold);
color: var(--gold);
box-shadow: none;
}
.btn-outline:hover {
border-color: var(--coral);
color: var(--coral);
box-shadow: none;
}
+38
View File
@@ -0,0 +1,38 @@
/* Contenu éditorial : icône + titre + texte, fluide (pas de boîte) sauf
quand .card est explicitement combiné (regroupements fonctionnels comme
l'équipement d'observation). */
.feature-card,
.info-card {
text-align: center;
padding-block: var(--space-2);
}
.feature-card h3,
.info-card h3 {
color: var(--gold);
margin-bottom: var(--space-2);
}
.card.info-card.essential {
box-shadow: 0 0 0 2px var(--gold), var(--glow-soft), var(--shadow-sm);
background: var(--gold-wash);
}
.info-card-header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--space-2);
margin-bottom: var(--space-1);
}
.badge {
display: inline-flex;
align-items: center;
padding-inline: var(--space-2);
border-radius: var(--radius-pill);
background: var(--gold-surface);
color: oklch(20% 0.03 265);
font-size: var(--text-0);
font-weight: 700;
}
+40
View File
@@ -0,0 +1,40 @@
/* Anneau comète — bordure en conic-gradient tournante, technique d'unabyss.com
(masque XOR pour ne garder que l'anneau). Respecte prefers-reduced-motion
via le kill-switch global de tokens.css (figé, anneau statique). */
@property --comet-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.comet-ring {
position: relative;
isolation: isolate;
}
.comet-ring::before {
content: "";
position: absolute;
inset: -3px;
border-radius: inherit;
padding: 2px;
background: conic-gradient(
from var(--comet-angle),
transparent 0deg,
transparent 250deg,
var(--gold-surface) 300deg,
var(--coral-surface) 330deg,
var(--gold-surface) 350deg,
transparent 360deg
);
-webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
animation: comet-spin 7s linear infinite;
pointer-events: none;
z-index: -1;
}
@keyframes comet-spin {
to { --comet-angle: 360deg; }
}
+80
View File
@@ -0,0 +1,80 @@
.diagram-orbit {
position: relative;
height: 16rem;
margin-block: var(--space-4);
display: flex;
align-items: center;
justify-content: center;
}
.diagram-orbit .ring {
position: absolute;
width: 12rem;
height: 12rem;
border: 2px dashed var(--border);
border-radius: 50%;
}
.diagram-orbit .body {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-1);
font-size: var(--text-4);
}
.diagram-orbit .body span {
font-size: var(--text-0);
font-weight: 700;
color: var(--gold);
}
.diagram-orbit .sun { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.diagram-orbit .earth { top: 15%; left: 15%; }
.diagram-orbit .sirius { top: 15%; right: 15%; }
.diagram-arcus {
position: relative;
height: 12rem;
margin-block: var(--space-4);
border-bottom: 2px solid var(--gold);
}
.diagram-arcus .horizon-label {
position: absolute;
bottom: -1.6rem;
left: 50%;
transform: translateX(-50%);
color: var(--gold);
font-weight: 700;
}
.diagram-arcus .angle {
position: absolute;
bottom: 0;
left: 30%;
width: 4rem;
height: 4rem;
border: 2px solid var(--caribbean-teal);
border-bottom: none;
border-left: none;
border-radius: 0 100% 0 0;
}
.diagram-arcus .angle-label {
position: absolute;
bottom: 4.2rem;
left: calc(30% + 4.2rem);
color: var(--caribbean-teal);
font-weight: 700;
}
.diagram-arcus .sirius-mark,
.diagram-arcus .sun-mark {
position: absolute;
bottom: var(--space-2);
}
.diagram-arcus .sirius-mark { left: 25%; }
.diagram-arcus .sun-mark { right: 25%; bottom: -0.6rem; }
+80
View File
@@ -0,0 +1,80 @@
.site-footer {
background: var(--surface-sunk);
border-top: 1px solid var(--border);
padding-block: var(--space-6) var(--space-4);
margin-top: var(--space-7);
}
.footer-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
gap: var(--space-5);
margin-bottom: var(--space-5);
}
.footer-section h2 {
font-size: var(--text-2);
margin-bottom: var(--space-2);
}
.footer-section h3 {
font-size: var(--text-1);
color: var(--gold);
margin-bottom: var(--space-2);
}
.footer-section ul {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.footer-section a {
text-decoration: none;
color: var(--ink-soft);
}
.footer-section a:hover {
color: var(--gold);
}
.countdown-item {
display: inline-flex;
flex-direction: column;
background: var(--surface-raised);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-3);
}
.countdown-value {
font-family: var(--font-display);
font-size: var(--text-2);
color: var(--gold);
}
.countdown-label {
color: var(--ink-soft);
font-size: var(--text-0);
}
.footer-bottom {
text-align: center;
border-top: 1px solid var(--border);
padding-top: var(--space-4);
}
.footer-bottom .pattern-strip {
margin-inline: auto;
}
.footer-credits a {
color: var(--gold);
text-decoration: none;
}
.footer-quote {
margin-top: var(--space-2);
font-style: italic;
color: var(--ink-soft);
}
+109
View File
@@ -0,0 +1,109 @@
.hero {
position: relative;
overflow: hidden;
padding-block: var(--space-8) var(--space-7);
background: light-dark(
linear-gradient(180deg, oklch(96% 0.02 85), oklch(92% 0.03 60)),
linear-gradient(180deg, oklch(9% 0.015 265), oklch(6% 0.01 265))
);
}
.hero-content {
position: relative;
z-index: 1;
}
.hero-title {
display: flex;
flex-direction: column;
gap: 0.1em;
}
.hero-title .accent {
color: var(--gold);
text-shadow: var(--glow-soft);
}
.hero-subtitle {
font-family: var(--font-display);
font-size: var(--text-3);
color: var(--gold);
margin-top: var(--space-2);
}
.hero-description {
font-size: var(--text-2);
color: var(--ink-soft);
margin-top: var(--space-2);
}
.hero-date {
margin-top: var(--space-4);
font-size: var(--text-1);
}
.hero-date strong {
display: block;
font-family: var(--font-display);
font-size: var(--text-3);
color: var(--gold);
text-shadow: var(--glow-soft);
}
.hero-cta {
justify-content: center;
margin-top: var(--space-5);
}
/* Système solaire décoratif, en CSS pure — masqué aux lecteurs d'écran */
.hero-orbits {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.5;
pointer-events: none;
}
.hero-orbits .sun {
position: absolute;
width: 1rem;
height: 1rem;
border-radius: 50%;
background: var(--gold-surface);
box-shadow: 0 0 40px 10px var(--gold-surface);
}
.hero-orbits .orbit {
position: absolute;
border: 1px solid light-dark(oklch(50% 0.05 85 / 30%), oklch(80% 0.05 85 / 20%));
border-radius: 50%;
animation: orbit-spin linear infinite;
}
.hero-orbits .orbit span {
position: absolute;
top: -3px;
left: 50%;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--caribbean-teal);
transform: translateX(-50%);
}
.hero-orbits .orbit.sirius span {
width: 9px;
height: 9px;
background: var(--gold-surface);
box-shadow: 0 0 12px var(--gold-surface);
}
@keyframes orbit-spin {
to { transform: rotate(360deg); }
}
@media (max-width: 47.99em) {
.hero-orbits { opacity: 0.25; }
}
+10
View File
@@ -0,0 +1,10 @@
/* Îlots Svelte : le contenu statique (.island-fallback) est toujours présent
dans le HTML ; s'il s'hydrate, le script de montage ajoute la classe
.is-enhanced sur le conteneur [data-island], qui masque alors le repli. */
.island-app:empty {
display: none;
}
[data-island].is-enhanced .island-fallback {
display: none;
}
+149
View File
@@ -0,0 +1,149 @@
.nav {
position: sticky;
top: 0;
z-index: 50;
background: light-dark(oklch(96% 0.015 85 / 90%), oklch(16% 0.03 265 / 88%));
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
}
.nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
min-height: var(--tap-target);
padding-block: var(--space-2);
}
.nav-brand {
font-family: var(--font-display);
font-weight: 600;
font-size: var(--text-2);
color: var(--ink);
text-decoration: none;
white-space: nowrap;
}
.nav-brand span {
color: var(--gold);
}
.nav-disclosure {
display: contents;
}
.nav-toggle {
display: flex;
align-items: center;
justify-content: center;
width: var(--tap-target);
height: var(--tap-target);
border-radius: var(--radius-sm);
cursor: pointer;
list-style: none;
}
.nav-toggle::-webkit-details-marker {
display: none;
}
.nav-toggle-icon,
.nav-toggle-icon::before,
.nav-toggle-icon::after {
display: block;
width: 22px;
height: 2px;
background: var(--ink);
border-radius: var(--radius-pill);
position: relative;
transition: transform var(--duration-fast) var(--ease-out);
}
.nav-toggle-icon::before,
.nav-toggle-icon::after {
content: "";
position: absolute;
}
.nav-toggle-icon::before { top: -7px; }
.nav-toggle-icon::after { top: 7px; }
.nav-disclosure[open] .nav-toggle-icon {
background: transparent;
}
.nav-disclosure[open] .nav-toggle-icon::before {
transform: translateY(7px) rotate(45deg);
}
.nav-disclosure[open] .nav-toggle-icon::after {
transform: translateY(-7px) rotate(-45deg);
}
.nav-menu {
display: flex;
flex-direction: column;
gap: 0;
position: absolute;
inset-inline: 0;
top: 100%;
background: var(--surface);
border-bottom: 1px solid var(--border);
padding: var(--space-2) var(--space-4) var(--space-4);
}
.nav-menu a {
display: flex;
align-items: center;
min-height: var(--tap-target);
text-decoration: none;
color: var(--ink);
border-bottom: 1px solid var(--border);
font-weight: 600;
}
.nav-menu a[aria-current="page"] {
color: var(--gold);
}
.nav-lang {
display: flex;
gap: var(--space-2);
font-size: var(--text-0);
}
.nav-lang a {
display: inline-flex;
align-items: center;
min-height: var(--tap-target);
padding-inline: var(--space-2);
border-radius: var(--radius-pill);
text-decoration: none;
color: var(--ink-soft);
font-weight: 600;
}
.nav-lang a.active {
background: var(--surface-sunk);
color: var(--gold);
}
@media (min-width: 60em) {
.nav-toggle { display: none; }
.nav-menu {
position: static;
flex-direction: row;
gap: var(--space-4);
border-bottom: none;
padding: 0;
background: transparent;
}
.nav-menu a {
min-height: auto;
border-bottom: none;
padding-block: var(--space-1);
}
}
+70
View File
@@ -0,0 +1,70 @@
/* Onglets 100% CSS (radio + label), sans JavaScript.
Structure attendue, tous enfants directs de fieldset.tabs, dans cet ordre :
input.tab-input (un par onglet, id unique)
.tab-list > label[for=...] (un par onglet)
.tab-panels > [data-panel="..."] (un par onglet)
Chaque input:checked révèle le panneau dont data-panel correspond à son id
et met en avant le label associé déclaré une fois par page appelante
via un attribut [data-tab-id] passé en contexte (voir pages). */
.tabs {
border: none;
padding: 0;
}
.tab-input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.tab-list {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
margin-bottom: var(--space-5);
}
.tab-label {
display: inline-flex;
align-items: center;
min-height: var(--tap-target);
padding-inline: var(--space-3);
border-radius: var(--radius-pill);
border: 2px solid var(--border);
cursor: pointer;
font-weight: 700;
text-align: center;
}
.tab-panel {
display: none;
}
/* Sélection par position (1er input coché -> 1er panneau/label), générique :
fonctionne quels que soient les id choisis, tant que l'ordre DOM est
input*, puis .tab-list, puis .tab-panels. */
.tab-input:nth-of-type(1):checked ~ .tab-panels .tab-panel:nth-of-type(1),
.tab-input:nth-of-type(2):checked ~ .tab-panels .tab-panel:nth-of-type(2),
.tab-input:nth-of-type(3):checked ~ .tab-panels .tab-panel:nth-of-type(3),
.tab-input:nth-of-type(4):checked ~ .tab-panels .tab-panel:nth-of-type(4) {
display: block;
}
.tab-input:nth-of-type(1):checked ~ .tab-list .tab-label:nth-of-type(1),
.tab-input:nth-of-type(2):checked ~ .tab-list .tab-label:nth-of-type(2),
.tab-input:nth-of-type(3):checked ~ .tab-list .tab-label:nth-of-type(3),
.tab-input:nth-of-type(4):checked ~ .tab-list .tab-label:nth-of-type(4) {
background: var(--gold-surface);
border-color: var(--gold-surface);
color: oklch(20% 0.03 265);
box-shadow: var(--glow-strong);
}
@media (max-width: 47.99em) {
.tab-list {
justify-content: center;
}
}
+68
View File
@@ -0,0 +1,68 @@
/* Timeline verticale (Culture, Dogon, héritage diaspora) */
.timeline-v {
position: relative;
max-width: var(--measure);
margin-inline: auto;
padding-left: var(--space-5);
}
.timeline-v::before {
content: "";
position: absolute;
left: 0.4rem;
top: 0.4rem;
bottom: 0.4rem;
width: 2px;
background: var(--gold-surface);
opacity: 0.4;
}
.timeline-v-item {
position: relative;
padding-bottom: var(--space-4);
}
.timeline-v-item::before {
content: "";
position: absolute;
left: -1.65rem;
top: 0.3rem;
width: 0.8rem;
height: 0.8rem;
border-radius: 50%;
background: var(--gold-surface);
}
.timeline-v-date {
font-weight: 700;
color: var(--gold);
}
/* Timeline horizontale à 4 étapes (Observer) */
.timeline-h {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-3);
counter-reset: step;
}
.timeline-h-step {
text-align: center;
padding-top: var(--space-3);
border-top: 4px solid var(--gold-surface);
}
.timeline-h-icon {
font-size: var(--text-4);
}
.timeline-h-time {
font-weight: 700;
color: var(--gold);
}
@media (max-width: 47.99em) {
.timeline-h {
grid-template-columns: 1fr;
}
}
+32
View File
@@ -0,0 +1,32 @@
/* Vitrine dense de badges écho panafricain à une grille d'intégrations :
sobre en soi (fond neutre), colorée par petites touches. */
.wave-badges {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--space-2);
margin-top: var(--space-6);
list-style: none;
}
.wave-badge {
display: flex;
align-items: baseline;
gap: var(--space-2);
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-pill);
background: var(--surface-raised);
font-size: var(--text-0);
}
.wave-badge-name {
font-weight: 700;
}
.wave-badge-date {
color: var(--ink-soft);
}
.wave-badge-0 .wave-badge-name { color: var(--gold); }
.wave-badge-1 .wave-badge-name { color: var(--coral); }
.wave-badge-2 .wave-badge-name { color: var(--caribbean-teal); }
+94
View File
@@ -0,0 +1,94 @@
/* Primitives de composition — inspirées de Every Layout (every-layout.dev) */
.container {
width: 100%;
max-width: 75rem;
margin-inline: auto;
padding-inline: var(--space-4);
}
.prose {
max-width: var(--measure);
margin-inline: auto;
}
.stack {
display: flex;
flex-direction: column;
}
.stack > * + * {
margin-block-start: var(--stack-space, var(--space-4));
}
.cluster {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--cluster-space, var(--space-3));
}
.center {
max-width: var(--center-measure, 65ch);
margin-inline: auto;
text-align: center;
}
.sidebar {
display: flex;
flex-wrap: wrap;
gap: var(--space-4);
}
.sidebar > :last-child {
flex-basis: 0;
flex-grow: 999;
min-width: 60%;
}
.grid-auto {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--grid-min, 16rem)), 1fr));
gap: var(--space-4);
}
.section {
padding-block: var(--space-8);
container-type: inline-size;
}
.section-header {
margin-bottom: var(--space-6);
}
.hairline {
border: none;
border-top: 1px solid var(--border);
margin: 0;
}
.section-eyebrow {
display: inline-block;
font-family: var(--font-body);
font-weight: 700;
font-size: var(--text-0);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--gold);
margin-bottom: var(--space-2);
}
.section-subtitle {
color: var(--ink-soft);
font-size: var(--text-2);
margin-top: var(--space-2);
}
/* Réservé aux regroupements fonctionnels (calculateur, carte, onglets,
équipement) le contenu purement éditorial reste fluide, sans boîte. */
.card {
background: var(--surface-raised);
border-radius: var(--radius-md);
padding: var(--space-4);
box-shadow: var(--shadow-sm);
}
+33
View File
@@ -0,0 +1,33 @@
/* Motifs panafricains utilisés en accents ponctuels (un par section, effet
Von Restorff), jamais en texture de fond généralisée. */
.pattern-strip {
display: block;
height: 0.5rem;
width: 100%;
max-width: 8rem;
border-radius: var(--radius-pill);
margin-block: var(--space-3);
}
.pattern-strip.kente {
background: repeating-linear-gradient(
45deg,
var(--gold-surface) 0 10px,
var(--kente-red) 10px 20px
);
}
.pattern-strip.bogolan {
background-image: radial-gradient(circle, var(--gold-surface) 30%, transparent 31%);
background-size: 12px 12px;
background-color: var(--bogolan-green);
}
.pattern-strip.adinkra {
background: repeating-conic-gradient(
var(--gold-surface) 0deg 90deg,
var(--caribbean-teal) 90deg 180deg
);
background-size: 14px 14px;
}
+47
View File
@@ -0,0 +1,47 @@
*, *::before, *::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html {
-webkit-text-size-adjust: 100%;
hanging-punctuation: first last;
}
body {
min-height: 100svh;
}
img, picture, svg, canvas {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
color: inherit;
}
button {
background: none;
border: none;
cursor: pointer;
}
a {
color: inherit;
}
ul[class], ol[class] {
list-style: none;
padding: 0;
}
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}
+6
View File
@@ -0,0 +1,6 @@
/* Thème OKI identité par défaut de Gwada-Sirius.
Un fichier de thème ne redéfinit que des tokens (jamais de sélecteurs de
composants), pour rester échangeable avec d'autres identités visuelles OKI. */
[data-theme="oki"] {
--brand-name: "Sirius Gwada";
}
+96
View File
@@ -0,0 +1,96 @@
:root {
color-scheme: light dark;
/* === Couleur oklch, contraste d'abord (méthode modus-vivendi) ===
Deux thèmes conceptuellement liés au sujet : Aube (clair) / Nuit (sombre).
Le noir de nuit est volontairement presque neutre (chroma minimal)
un "premium near-black" plutôt qu'un bleu nuit saturé. */
/* Hiérarchie de texte à 4 niveaux (inspirée de la granularité observée
chez unabyss.com text/secondary/tertiary/muted...) : primaire, atténué,
discret (métadonnées : coordonnées GPS, légendes), fantôme (à peine ). */
--ink: light-dark(oklch(23% 0.03 265), oklch(96% 0.01 85));
--ink-soft: light-dark(oklch(38% 0.03 265), oklch(78% 0.015 85));
--ink-dim: light-dark(oklch(52% 0.02 265), oklch(60% 0.015 85));
--ink-ghost: light-dark(oklch(68% 0.015 265), oklch(42% 0.015 85));
--surface: light-dark(oklch(96% 0.015 85), oklch(11% 0.012 265));
--surface-raised: light-dark(oklch(99% 0.008 85), oklch(15% 0.015 265));
--surface-sunk: light-dark(oklch(91% 0.02 85), oklch(8% 0.012 265));
--border: light-dark(oklch(85% 0.02 85), oklch(26% 0.02 265));
/* Or de Sirius : variante lisible (texte/liens) + variante décorative (glows) */
--gold: light-dark(oklch(45% 0.15 75), oklch(82% 0.15 87));
--gold-surface: oklch(80% 0.16 85);
/* Lavis dorés pré-mélangés : fonds/bordures teintés sans color-mix() ad hoc
dispersé dans chaque fichier deux intensités suffisent à nos usages. */
--gold-wash-subtle: light-dark(oklch(94% 0.025 85), oklch(24% 0.03 85));
--gold-wash: light-dark(oklch(90% 0.05 85), oklch(28% 0.05 85));
/* Vocabulaire de lueur, distinct d'une ombre générique : douce pour un
texte/accent au repos, forte pour un état actif/sélectionné. */
--glow-soft: 0 0 20px light-dark(oklch(80% 0.15 87 / 20%), oklch(82% 0.15 87 / 30%));
--glow-strong: 0 0 30px light-dark(oklch(80% 0.15 87 / 35%), oklch(82% 0.15 87 / 55%));
/* Corail (héritage Caraïbe du v1 du projet) : accent secondaire chaud,
à côté de l'or plutôt qu'à sa place inspiré du ton "burnt orange"
d'unabyss.com. */
--coral: light-dark(oklch(50% 0.17 45), oklch(72% 0.17 45));
--coral-surface: oklch(70% 0.18 45);
--kente-red: light-dark(oklch(46% 0.18 25), oklch(70% 0.19 25));
--bogolan-green: light-dark(oklch(40% 0.09 145), oklch(68% 0.12 145));
--caribbean-teal: light-dark(oklch(43% 0.09 195), oklch(74% 0.11 195));
--focus-ring: var(--caribbean-teal);
/* === Typographie === */
--font-display: "Fraunces Variable", "Fraunces", ui-serif, Georgia, serif;
--font-body: "Atkinson Hyperlegible", ui-sans-serif, system-ui, sans-serif;
--text-0: clamp(0.8rem, 0.78rem + 0.1vw, 0.875rem);
--text-1: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--text-2: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
--text-3: clamp(1.375rem, 1.2rem + 0.75vw, 1.75rem);
--text-4: clamp(1.85rem, 1.5rem + 1.6vw, 2.75rem);
--text-5: clamp(2.5rem, 1.8rem + 3vw, 4rem);
--text-6: clamp(3rem, 1.9rem + 5.5vw, 6.5rem);
--tracking-tight: -0.02em;
--leading-tight: 1.1;
--leading-normal: 1.65;
--measure: 65ch;
/* === Espacement (échelle modulaire) — rythme généreux === */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
--space-4: 1.5rem;
--space-5: 2.5rem;
--space-6: 4rem;
--space-7: 7rem;
--space-8: 9rem;
/* === Formes, ombres, mouvement === */
--radius-sm: 0.5rem;
--radius-md: 1rem;
--radius-lg: 1.5rem;
--radius-pill: 999px;
--shadow-sm: 0 1px 3px light-dark(oklch(23% 0.03 265 / 12%), oklch(0% 0 0 / 35%));
--shadow-md: 0 12px 40px light-dark(oklch(23% 0.03 265 / 14%), oklch(0% 0 0 / 55%));
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--duration-fast: 200ms;
--duration-base: 400ms;
--duration-slow: 700ms;
/* Fitts : cible tactile minimale */
--tap-target: 48px;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
+2 -3
View File
@@ -3,18 +3,17 @@
echo "🌟 Sirius en Guadeloupe - Démarrage du projet"
echo "============================================="
# Vérifier si node_modules existe
if [ ! -d "node_modules" ]; then
echo "📦 Installation des dépendances..."
npm install
fi
echo "🚀 Lancement du serveur de développement..."
echo "📱 L'application sera accessible sur http://localhost:5173"
echo "📱 L'application sera accessible sur http://localhost:8080"
echo ""
echo "Commandes disponibles:"
echo " - Ctrl+C : Arrêter le serveur"
echo " - npm run build : Créer une version de production"
echo " - npm run build : Créer une version de production (11ty + Vite -> _site/)"
echo " - npm run preview : Prévisualiser la version de production"
echo ""
-7
View File
@@ -1,7 +0,0 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
base: './', // Utiliser des chemins relatifs
})