all updates + fix bug

This commit is contained in:
KIRITO_KMA 2026-04-07 01:51:50 +02:00
parent 78dc2e6e7f
commit f17ca2527c
44 changed files with 1087 additions and 521 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
# Ignore all Markdown files
# *.md
.**claude**

477
README.md
View file

@ -1,291 +1,222 @@
# OmégaKube — Site Web
Site vitrine officiel d'OmégaKube — plateforme de création d'expériences Role Play immersives avec bots Discord personnalisés et maps Fortnite.
## 📋 Vue d'ensemble du projet
Le projet **OmégaKube Website** est un site web moderne et responsive présentant les services de l'entreprise. Il s'agit d'une application web statique basée sur HTML, CSS et JavaScript vanilla, avec un système de composants réutilisables.
### Services présentés
- **Bots Discord personnalisés** — Création de bots RP avancés
- **Maps Fortnite immersives** — Design d'expériences gaming uniques
- **Expériences RP surMesure** — Solutions complètes pour communautés gaming
Site vitrine officiel d'OmégaKube — plateforme de création d'expériences Role Play immersives : bots Discord personnalisés, maps Fortnite UEFN, graphisme et consulting.
---
## 🏗️ Architecture du projet
## 🏗️ Architecture
```
website/
├── index.html # Page d'accueil principale
├── assets/ # Ressources statiques
├── index.html # Page d'accueil
├── .htaccess # Config Apache (404, redirections)
├── pages/
│ ├── services.html # Nos services
│ ├── realisations.html # Portfolio complet
│ ├── faq.html # Questions fréquentes
│ ├── mentions-legales.html
│ ├── cgv.html
│ └── 404.php # Page 404 (CSS/JS inline, fonctionne depuis n'importe quel chemin)
├── components/
│ ├── header.html # Navbar (data-include)
│ ├── footer.html # Footer + réseaux sociaux (data-include)
│ ├── background.html # Fond géométrique animé
│ ├── contact.html # Formulaire de contact réutilisable
│ └── favicons.html
├── assets/
│ ├── css/
│ │ └── main.css # Feuille de styles globale (thème, animations, layout)
│ │ ├── main.css # Styles globaux (thème dark/light, composants)
│ │ ├── 404.css # Styles page 404
│ │ └── legal.css # Styles pages légales
│ ├── js/
│ │ ├── main.js # Script principal (chargement, thème, composants)
│ │ ├── home.js # Logique spécifique à la page d'accueil
│ │ └── pages/ # Scripts pour pages spécifiques
│ ├── images/ # Images et assets visuels
│ ├── fonts/ # Polices personnalisées (OmegaLarge, OmegaSmall)
│ ├── data/ # Données JSON (services, projets, etc.)
│ └── php/ # Scripts PHP backend (formulaires, envois email)
├── components/ # Composants réutilisables (système d'include)
│ ├── header.html # Navigation + logo
│ ├── footer.html # Pied de page
│ ├── background.html # Animation de fond géométrique
│ ├── favicons.html # Favicon et meta tags
│ ├── index/ # Composants spécifiques à l'accueil
│ │ └── ... (hero, about, services, contact)
│ └── services/ # Composants de la page services
├── pages/ # Pages du site
│ ├── services.html # Détail des services
│ ├── realisations.html # Portfolio/réalisations
│ ├── cgv.html # Conditions Générales de Vente
│ ├── mentions-legales.html # Mentions légales
│ ├── 404.php # Page erreur 404
│ └── test*.html # Pages de test
├── .htaccess # Configuration serveur Apache
├── .git/ # Versionning Git
└── README.md # Ce fichier
│ │ ├── main.js # Script partagé : thème, includes, social links, feather
│ │ ├── home.js # Accueil : portfolio, team carousel, partners, easter egg
│ │ └── pages/
│ │ ├── realisations.js # Portfolio + modal projet + galerie
│ │ ├── services.js # Onglets services
│ │ ├── services-process.js # Animation timeline processus
│ │ ├── legal.js # Pages légales
│ │ └── 404.js # Easter egg page 404 (Game of Life)
│ ├── data/
│ │ ├── projects.json # ⭐ Projets (images, stats, tech, social)
│ │ ├── team.json # ⭐ Membres de l'équipe
│ │ ├── partners.json # ⭐ Partenaires
│ │ ├── services.json # Détail des services
│ │ └── faq.json # Questions / réponses FAQ
│ ├── images/
│ │ ├── projects/ # Images des projets
│ │ │ ├── kuby/ # imgk-1.png, ...
│ │ │ ├── victorias-vice-1/ # imgvv1-1.jpg, ...
│ │ │ ├── vicy/
│ │ │ ├── site-omega/
│ │ │ └── branding-omega-kube/
│ │ ├── team/ # Photos membres : {id_discord}.png
│ │ ├── partners/ # Logos partenaires : {name}.png
│ │ └── favicon/
│ ├── fonts/ # OmegaLarge, OmegaSmall, Montserrat
│ └── php/
│ └── send_mail.php # Formulaire de contact (JSON POST)
```
### Système de composants
Le projet utilise un **système de composants custom** basé sur `data-include`:
```html
<!-- Incluye un composant HTML -->
<div data-include="components/header.html"></div>
<!-- Se charge dynamiquement via JavaScript -->
```
**Avantages:**
- Pas de dépendance Build (HTML pur)
- Réutilisabilité des composants
- Maintenabilité accrue
---
## 🎨 Technologie & Stack
## ⚙️ Configuration rapide
### 1. Réseaux sociaux — un seul endroit
Dans `assets/js/main.js`, modifier l'objet `window.OK_SOCIAL` :
```js
window.OK_SOCIAL = {
discord: 'https://discord.gg/FwvNkvu5dy',
fortnite: 'https://www.fortnite.com/@omega_kube_team'
twitter: 'https://x.com/Omegakube',
instagram: 'https://www.instagram.com/omegakube/',
youtube: 'https://www.youtube.com/@OmegaKube',
tiktok: 'https://www.tiktok.com/@omegakube',
facebook: '',
bluesky: '',
email: 'contact@omegakube.fr'
};
```
Tous les liens du site (footer, contact, modals) se mettent à jour automatiquement.
---
### 2. Ajouter / modifier un projet
Éditer `assets/data/projects.json` :
```json
{
"id": 6,
"category": "bot", // "bot" | "map" | "graphisme"
"title": "Nom du projet",
"desc": "Description complète...",
"gradient": "linear-gradient(135deg, #5E2040, #8b3a62)",
"year": "2025",
"duration": "2 mois",
"images": "assets/images/projects/nom-du-projet/",
"files": [
"trailer.mp4", // vidéo locale
"https://youtube.com/watch?v=VIDEO_ID", // URL YouTube complète
"yt:VIDEO_ID", // shortcode YouTube
"img-1.png",
"img-2.jpg"
],
"features": ["Feature 1", "Feature 2"],
"tech": [
{ "name": "Python", "icon": "code" }
],
"stats": [
{ "icon": "users", "value": "500+", "label": "Utilisateurs" }
],
"social": {
"discord": "https://discord.gg/...",
"twitter": "",
"instagram": ""
}
}
```
Déposer les images dans `assets/images/projects/nom-du-projet/`.
---
### 3. Ajouter un membre de l'équipe
Éditer `assets/data/team.json`. Photo optionnelle : `assets/images/team/{id_discord}.png`.
```json
{
"id": "123456789", // ID Discord = nom du fichier photo
"name": "Pseudo",
"role": "Rôle",
"avatar": "P", // Lettre fallback si pas de photo
"bio": "...",
"stats": [...],
"skills": ["Skill 1"],
"social": {
"discord": "",
"twitter": "",
"github": ""
}
}
```
---
### 4. Ajouter un partenaire
Éditer `assets/data/partners.json`. Logo optionnel : `assets/images/partners/{name}.png`.
```json
{
"name": "nom-slug", // = nom du fichier logo
"displayName": "Nom Affiché",
"description": "Courte description",
"longDesc": "Description complète pour la modal",
"gradient": "linear-gradient(...)",
"since": "2025",
"services": ["Service rendu"],
"stats": [
{ "icon": "users", "value": "100+", "label": "Membres" }
],
"social": {
"discord": "",
"website": "",
"twitter": ""
}
}
```
---
### 5. FAQ
Éditer `assets/data/faq.json` — ajouter des catégories ou questions :
```json
{
"id": "ma-categorie",
"icon": "help-circle", // icône Feather Icons
"title": "Ma catégorie",
"questions": [
{ "q": "La question ?", "a": "La réponse." }
]
}
```
---
## 🌐 Déploiement
- **Serveur** : PHP requis uniquement pour `404.php` et `send_mail.php`
- **Contact** : configurer l'adresse email dans `assets/php/send_mail.php` (`$to`)
- **Apache** : le `.htaccess` gère la 404 et désactive le listing de dossiers
- **Thème** : dark par défaut, toggle persisté en `localStorage`
---
## 🥚 Easter Eggs
- **PC** : taper le Konami Code `↑ ↑ ↓ ↓ ← → ← → B A`
- **Mobile** : 10 taps rapides sur le logo navbar ou le logo footer
- **Page 404** : 10 taps sur le "404" → Game of Life de Conway
---
## 🛠️ Stack technique
| Technologie | Usage |
|---|---|
| **HTML5** | Structure sémantique |
| **CSS3** | Styling, animations, variables CSS |
| **JavaScript (Vanilla)** | Interactivité, système de composants |
| **Feather Icons** | Bibliothèque d'icônes légères |
| **PHP** | Backend pour formulaires/emails |
| **Apache HTTP Server** | Serveur web |
| **Git** | Contrôle de version |
### Système de thème
Design **dark-first** avec toggle light/dark:
- CSS Custom Properties pour les couleurs
- Thème stoké en `data-theme` sur `<html>`
- Variables principales:
- `--bg-deep`, `--bg-secondary`, `--bg-tertiary`
- `--accent-primary`, `--accent-secondary`
- Colors du thème changent automatiquement
### Polices personnalisées
- **OmegaLarge** — Titres et éléments grands
- **OmegaSmall** — Textes courants
- **Montserrat-Bold et Montserrat-BoldItalic/** — Polices par defaut pour le moment
- Localisées dans `assets/fonts/`
---
## 🚀 Setup & Installation
### Prérequis
- **Serveur web** (Apache, Nginx, PHP 7.4+. Ou tout simplement Wamp pour Windows ou Lamp pour Linux)
- **Git** pour le versioning
- **Navigateur moderne** (Chrome, Firefox, Safari, Edge)
- Aucune dépendance externe à installer
### Installation locale
#### 1. **Cloner le projet**
```bash
cd e:\votre _repertoire_de_dev
git clone https://gitlab.com/Omega_Kube/website.git
cd website
```
#### 2. **Configurer le serveur local**
**Option A - Avec PHP intégré (développement rapide):**
```bash
php -S localhost:8000
# Accédez à http://localhost:8000
```
**Option B - Avec Apache:**
1. Placer le dossier dans votre `htdocs` ou `www`
2. Configurer le VirtualHost si nécessaire
3. S'assurer que le fichier `.htaccess` est présent et actif
**Option C - Avec un serveur Live Server (VS Code):**
- Installer l'extension "Live Server"
- Clic-droit → "Open with Live Server"
**Option D - Démarrer le serveur Wamp ou Lamp:**
- Placer le dossier dans votre `www`
- Crée un alias vers votre dossier de programation ou le déplacer dans votre `www`
#### 3. **Tester le projet**
- Ouvrir http://localhost:8000 (ou votre serveur)
- Vérifier que tous les composants se chargent
- Tester le toggle dark/light
- Vérifier les liens de navigation
---
## 📝 Guide de développement
### Structure des fichiers CSS
**`assets/css/main.css`** — Point d'entrée unique:
```css
/* Variables & Thèmes */
:root { --bg-deep: #1a0f1f; ... }
/* Layout & Composants */
.hero { }
.about { }
.btn { }
/* ... */
/* Animations & Responsive */
@media (max-width: 768px) { }
```
### Ajouter une nouvelle page
1. **Créer le fichier HTML** dans `pages/`:
```bash
pages/ma-page.html
```
2. **Inclure les composants** (header, footer):
```html
<div data-include="components/header.html"></div>
<main>
<!-- Contenu -->
</main>
<div data-include="components/footer.html"></div>
```
3. **Ajouter le lien** dans le header:
```html
<!-- components/header.html -->
<li><a href="pages/ma-page.html">Ma Page</a></li>
```
4. **Si JS nécessaire**, créer `assets/js/pages/ma-page.js`
### Ajouter un composant
1. **Créer le fichier** dans `components/`:
```bash
components/mon-composant.html
```
2. **L'inclure** n'importe où:
```html
<div data-include="components/mon-composant.html"></div>
```
### Modifier les styles globaux
- **Couleurs** → Modifier les variables CSS dans `assets/css/main.css` (section `:root`)
- **Polices** → Uploader dans `assets/fonts/`, puis ajouter `@font-face`
- **Animations** → Ajouter dans `assets/css/main.css` (respect du système de thème)
### Intégrer le backend PHP
**Exemple: Formulaire de contact**
1. **Créer le formulaire** dans un composant:
```html
<!-- components/contact-form.html -->
<form method="POST" action="assets/php/send-message.php">
<input type="email" name="email" required>
<textarea name="message"></textarea>
<button type="submit">Envoyer</button>
</form>
```
2. **Créer le script PHP** (`assets/php/send-message.php`):
```php
<?php
$to = "info@omegakube.com";
$subject = $_POST['subject'] ?? "Message OmégaKube";
$message = $_POST['message'];
$headers = "From: " . $_POST['email'];
if (mail($to, $subject, $message, $headers)) {
echo "Message envoyé !";
} else {
echo "Erreur lors de l'envoi";
}
?>
```
---
## 🔧 Maintenance & Dépannage
### Le site ne charge pas les composants
**Vérifications:**
- [ ] Le fichier `assets/js/main.js` contient le code d'inclusion
- [ ] Les chemins dans `data-include` sont corrects (relatifs à la racine)
- [ ] Les fichiers HTML existent et n'ont pas d'erreur de syntaxe
### Les styles ne s'appliquent pas
- [ ] Vérifier que `assets/css/main.css` est lié dans le `<head>`
- [ ] Rafraîchir le cache du navigateur (Ctrl+Shift+R ou Ctrl+F5)
- [ ] Vérifier les media-queries pour le responsive
### Les icônes Feather ne s'affichent pas
- [ ] S'assurer que la CDN Feather Icons est incluse
- [ ] Vérifier les noms d'icônes sur [feathericons.com](https://feathericons.com)
### Le toggle dark/light ne fonctionne pas
- [ ] Vérifier que `home.js` est chargé
- [ ] S'assurer que les variables CSS sont correctement définies
- [ ] Vérifier la balise `<html data-theme="dark">` ou `<html data-theme="light">`
---
## 📋 Checklist pour nouveaux développeurs
Avant de commencer:
- [ ] Cloner le projet en local
- [ ] Lancer un serveur local (PHP , Live Server ou Wamp/Lamp)
- [ ] Vérifier que le site s'affiche correctement
- [ ] Consulter la structure des fichiers
- [ ] Comprendre le système de composants `data-include`
- [ ] Tester le dark/light mode
- [ ] Vérifier les liens de navigation
---
## 🔗 Ressources utiles
- **Feather Icons**: https://feathericons.com
- **CSS Variables**: https://developer.mozilla.org/en-US/docs/Web/CSS/--*
- **Responsive Design**: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design
- **Git Guide**: https://git-scm.com/doc
| HTML5 / CSS3 | Structure et styles, thème dark/light |
| JavaScript ES6+ | Logique, composants, données JSON |
| PHP | Formulaire de contact, page 404 |
| Feather Icons | Icônes (CDN) |
| Montserrat | Police secondaire |
| OmegaLarge / OmegaSmall | Polices custom OmégaKube |
---
@ -305,6 +236,6 @@ Pour contribuer au projet:
Consulter [pages/mentions-legales.html](pages/mentions-legales.html) et [pages/cgv.html](pages/cgv.html).
---
**Version du site web**: 1.5.1.4
**Dernière mise à jour**: Mars 2026
**Version du site web**: 1.5.2.16
**Dernière mise à jour**: Avril 2026
**Mainteneur**: KIRITO_KMA / L'équipe OmégaKube

View file

@ -1130,7 +1130,7 @@ section {
position: absolute;
inset: 0;
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
opacity: 0.4;
/* opacity retiré — géré par portfolio-content gradient */
}
.portfolio-content {
@ -1140,7 +1140,9 @@ section {
flex-direction: column;
justify-content: flex-end;
padding: 2rem;
background: linear-gradient(180deg, transparent 0%, var(--bg-deep) 100%);
/* Gradient sombre uniquement en bas pour lisibilité du texte sans masquer l'image */
background: linear-gradient(180deg, transparent 55%, rgba(10,10,15,0.92) 100%);
z-index: 2;
}
.portfolio-tag {
@ -1169,9 +1171,7 @@ section {
}
.portfolio-item p {
color: var(--text-muted);
font-size: 0.9rem;
margin-top: 0.5rem;
display: none; /* Description masquée dans les cartes — visible uniquement dans la modal */
}
/* Portfolio Detail */
@ -2118,13 +2118,12 @@ footer {
overflow: hidden;
}
/* Pas d'overlay sur le bento — juste gradient bas pour lisibilité texte */
.bento-project .bento-image::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(139, 58, 98, 0.3), rgba(45, 27, 51, 0.5));
display: none;
}
/* Gradient sombre en bas pour le texte */
.bento-project .bento-image::after {
content: '';
position: absolute;
@ -2132,7 +2131,9 @@ footer {
left: 0;
right: 0;
height: 50%;
background: linear-gradient(to top, var(--bg-deep), transparent);
background: linear-gradient(to top, rgba(10,10,15,0.9) 0%, transparent 100%);
z-index: 2;
pointer-events: none;
}
.bento-image-content {
@ -2140,7 +2141,7 @@ footer {
bottom: 2rem;
left: 2rem;
right: 2rem;
z-index: 1;
z-index: 3;
}
.bento-image-content .portfolio-tag {
@ -2462,19 +2463,43 @@ footer {
}
.project-gallery-slides {
display: flex;
position: relative;
width: 100%;
height: 100%;
transition: transform 0.5s ease;
}
.project-gallery-slide {
min-width: 100%;
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
opacity: 0;
pointer-events: none;
transition: opacity 0.35s ease, transform 0.35s ease;
transform: translateX(100%);
will-change: transform, opacity;
}
/* Slide à gauche de la courante */
.project-gallery-slide.slide-left {
transform: translateX(-100%);
opacity: 0;
}
/* Slide à droite de la courante */
.project-gallery-slide.slide-right {
transform: translateX(100%);
opacity: 0;
}
/* Slide active — visible */
.project-gallery-slide.active {
opacity: 1;
transform: translateX(0) !important;
pointer-events: auto;
}
.project-gallery-slide::before {
@ -2700,12 +2725,12 @@ footer {
position: absolute;
inset: 0;
background: transparent;
z-index: 1;
z-index: 3;
transition: background 0.3s ease;
}
.portfolio-item:hover::before {
background: rgba(139, 58, 98, 0.1);
background: rgba(139, 58, 98, 0.08);
}
.portfolio-item:active {
@ -4171,13 +4196,85 @@ footer {
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1;
z-index: 1; /* au-dessus du gradient .portfolio-bg */
display: block;
}
/* .portfolio-bg conserve son gradient en fallback derrière l'image */
/* .portfolio-bg — override pour les cartes avec image */
.portfolio-bg {
position: absolute;
inset: 0;
overflow: hidden;
/* gradient retiré ici — défini dans la règle principale */
background: none;
}
/* ═══════════════════════════════════════════════════════════════════════════
BENTO + PORTFOLIO — image de couverture réelle
═══════════════════════════════════════════════════════════════════════════ */
.bento-cover-img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1; /* au-dessus du gradient de fond */
display: block;
}
/* bento-image-content — z-index géré dans la règle principale ligne ~2141 */
/* ═══════════════════════════════════════════════════════════════════════════
GALLERY — iframe YouTube
═══════════════════════════════════════════════════════════════════════════ */
.yt-wrapper {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.gallery-slide-yt-iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* ═══════════════════════════════════════════════════════════════════════════
GALLERY — bouton mute vidéo
═══════════════════════════════════════════════════════════════════════════ */
.gallery-mute-btn {
position: absolute;
bottom: 3rem; /* au-dessus des dots */
right: 1rem;
z-index: 10;
width: 38px;
height: 38px;
background: rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: white;
transition: background 0.2s ease;
backdrop-filter: blur(4px);
}
.gallery-mute-btn:hover {
background: rgba(139, 58, 98, 0.8);
border-color: var(--accent-primary);
}
/* Icône son coupé = visible par défaut (muted=true au départ) */
.gallery-mute-btn .icon-muted { display: block; }
.gallery-mute-btn .icon-unmuted { display: none; }
/* Quand le son est activé */
.gallery-mute-btn.unmuted .icon-muted { display: none; }
.gallery-mute-btn.unmuted .icon-unmuted { display: block; }

View file

@ -1,50 +1,113 @@
[
{
"name": "victorias-vice",
"displayName": "Victoria's Vice",
"displayName": "Victoria's Vice 2",
"description": "Serveur RP whitelist Fortnite sur thème italien. L'une de nos collaborations les plus abouties.",
"longDesc": "Victoria's Vice est un serveur RP whitelist Fortnite immersif basé sur une esthétique italienne unique. OmégaKube a développé l'intégralité de la map UEFN, le bot RP Vicy et l'identité visuelle du projet.",
"gradient": "linear-gradient(135deg, #5E2040, #8b3a62)",
"url": "#",
"discord": "#",
"since": "2025",
"services": ["Map UEFN", "Bot Discord", "Branding"],
"services": [
"Map UEFN",
"Bot Discord",
"Branding"
],
"stats": [
{ "icon": "users", "value": "70", "label": "Joueurs max" },
{ "icon": "home", "value": "30+", "label": "Bâtiments" },
{ "icon": "play", "value": "30+", "label": "Sessions" }
]
{
"icon": "users",
"value": "70",
"label": "Joueurs max"
},
{
"icon": "home",
"value": "30+",
"label": "Bâtiments"
},
{
"icon": "play",
"value": "30+",
"label": "Sessions"
}
],
"social": {
"discord": "https://discord.gg/8D564jm2SD",
"twitter": "",
"instagram": "",
"youtube": "https://www.youtube.com/@VictoriasVice",
"tiktok": "",
"website": "#"
}
},
{
"name": "partenaire-2",
"displayName": "Partenaire 2",
"description": "Description courte du partenaire.",
"longDesc": "Description longue du partenaire à renseigner.",
"name": "red-dust-redemption",
"displayName": "Red Dust Redemption",
"description": "Serveur RP whitelist sur le thème far-west.",
"longDesc": "Serveur RP whitelist sur le thème du far-west.",
"gradient": "linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))",
"url": "#",
"discord": "#",
"since": "2025",
"services": ["Service 1", "Service 2"],
"services": [
"Sessions RP Red Dust Redemption"
],
"stats": [
{ "icon": "star", "value": "?", "label": "Stat 1" },
{ "icon": "zap", "value": "?", "label": "Stat 2" },
{ "icon": "heart", "value": "?", "label": "Stat 3" }
]
{
"icon": "star",
"value": "?",
"label": "Stat 1"
},
{
"icon": "zap",
"value": "?",
"label": "Stat 2"
},
{
"icon": "heart",
"value": "?",
"label": "Stat 3"
}
],
"social": {
"discord": "#",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"website": "#"
}
},
{
"name": "partenaire-3",
"displayName": "Partenaire 3",
"description": "Description courte du partenaire.",
"name": "rickroll",
"displayName": "Rick Astley",
"description": "Official Video 4K Remaster",
"longDesc": "Description longue du partenaire à renseigner.",
"gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)",
"url": "#",
"discord": "#",
"since": "2025",
"services": ["Service 1", "Service 2"],
"services": [
"Service 1",
"Service 2"
],
"stats": [
{ "icon": "star", "value": "?", "label": "Stat 1" },
{ "icon": "zap", "value": "?", "label": "Stat 2" },
{ "icon": "heart", "value": "?", "label": "Stat 3" }
]
{
"icon": "star",
"value": "?",
"label": "Stat 1"
},
{
"icon": "zap",
"value": "?",
"label": "Stat 2"
},
{
"icon": "heart",
"value": "?",
"label": "Stat 3"
}
],
"social": {
"discord": "#",
"twitter": "",
"instagram": "",
"youtube": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"tiktok": "",
"website": "#"
}
}
]
]

View file

@ -55,7 +55,18 @@
"label": "Semaines de dev"
}
],
"images": "assets/images/projects/kuby/"
"images": "assets/images/projects/kuby/",
"files": [
"imgk-1.png",
"https://www.youtube.com/watch?v=dQw4w9WgXcQ"
],
"social": {
"discord": "",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 2,
@ -113,7 +124,26 @@
"label": "Mois de dev"
}
],
"images": "assets/images/projects/victorias-vice/"
"images": "assets/images/projects/victorias-vice-1/",
"files": [
"imgvv1-1.jpg",
"imgvv1-2.jpg",
"imgvv1-3.jpg",
"imgvv1-4.jpg",
"imgvv1-5.jpg",
"imgvv1-6.jpg",
"imgvv1-7.jpg",
"imgvv1-8.jpg",
"TrailerOfficielVV1.mp4",
"yt:xiCdHA-Z7uM"
],
"social": {
"discord": "https://discord.gg/8D564jm2SD",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 3,
@ -171,7 +201,18 @@
"label": "Uptime"
}
],
"images": "assets/images/projects/vicy/"
"images": "assets/images/projects/vicy/",
"files": [
"imgvicy-1.png",
"yt:dQw4w9WgXcQ"
],
"social": {
"discord": "https://discord.gg/8D564jm2SD",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 4,
@ -222,7 +263,15 @@
"label": "Illustrator"
}
],
"images": "assets/images/projects/branding-omega-kube/"
"images": "assets/images/projects/branding-omega-kube/",
"files": ["logo.png"],
"social": {
"discord": "https://discord.gg/FwvNkvu5dy",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 5,
@ -270,6 +319,164 @@
"label": "Systèmes"
}
],
"images": "assets/images/projects/site-omega/"
}
"images": "assets/images/projects/site-omega/",
"files": [
"imgso-1.png",
"imgso-2.png",
"imgso-3.png",
"imgso-4.png",
"imgso-5.png",
"imgso-6.png",
"imgso-7.png",
"imgso-8.png",
"imgso-9.jpg"
],
"social": {
"discord": "",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 6,
"category": "map",
"title":"Victoria's Vice 2",
"desc": "Suite de Victoria's Vice, avec une map plus grande, plus de bâtiments, plus de systèmes et une immersion encore plus poussée dans l'univers italien.",
"gradient": "linear-gradient(135deg, #5E2040, #8b3a62)",
"year": "2026",
"duration": "en cour...",
"features": [
"économie réaliste",
"50+ bâtiments",
"Système jour/nuit",
"Serveur Whitelist",
"Métiers légaux variés",
"Bot RP Vicy exclusif"
],
"tech": [
{
"name": "Fortnite Creative",
"icon": "map"
},
{
"name": "UEFN",
"icon": "layers"
},
{
"name": "Verse",
"icon": "cpu"
},
{
"name": "Texturing",
"icon": "image"
}
],
"stats": [
{
"icon": "play",
"value": "en cour...",
"label": "Sessions"
},
{
"icon": "home",
"value": "en cour...",
"label": "Bâtiments"
},
{
"icon": "users",
"value": "en cour...",
"label": "Joueurs max"
},
{
"icon": "calendar",
"value": "en cour...",
"label": "Mois de dev"
}
],
"images": "assets/images/projects/victorias-vice-2/",
"files": [
"imgvv2-1.jpg",
"imgvv2-2.jpg",
"imgvv2-3.jpg",
"https://www.youtube.com/watch?v=Lx-1FWOJN9E"
],
"social": {
"discord": "https://discord.gg/AM3e58cZvX",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
},
{
"id": 7,
"category": "bot",
"title": "Beta",
"desc": "En cour de développement chez Omega Kube.",
"gradient": "linear-gradient(135deg, #000000, #434343)",
"year": "2026",
"duration": "en cour...",
"features": [
"en cour..."
],
"tech": [
{
"name": "Python",
"icon": "code"
},
{
"name": "Discord.py",
"icon": "message-circle"
},
{
"name": "MySQL",
"icon": "database"
},
{
"name": "Pillow",
"icon": "image"
},
{
"name": "Autre tech",
"icon": "tool"
}
],
"stats": [
{
"icon": "users",
"value": "en cour...",
"label": "Utilisateurs"
},
{
"icon": "terminal",
"value": "en cour...",
"label": "Commandes"
},
{
"icon": "credit-card",
"value": "en cour...",
"label": "RP menés"
},
{
"icon": "activity",
"value": "en cour...",
"label": "Uptime"
}
],
"images": "assets/images/projects/beta/",
"files": [
"imgb-1.png",
"imgb-2.png",
"yt:dQw4w9WgXcQ"
],
"social": {
"discord": "https://discord.gg/FwvNkvu5dy",
"instagram": "",
"tiktok": "",
"youtube": "",
"twitter": ""
}
}
]

View file

@ -6,11 +6,37 @@
"avatar": "N",
"bio": "Nixfix donne vie aux identités visuelles des projets RP. Passionné de design graphique, il excelle aussi bien dans la création de logos que dans la gestion de projets. Son style unique allie une esthétique gaming affirmée à un véritable professionnalisme, afin de proposer des visuels percutants et cohérents.",
"stats": [
{ "value": "6", "label": "Ans d'exp." },
{ "value": "15+", "label": "Projets" },
{ "value": "★★★", "label": "Expert" }
{
"value": "6",
"label": "Ans d'exp."
},
{
"value": "15+",
"label": "Projets"
},
{
"value": "★★★",
"label": "Expert"
}
],
"skills": ["Polyvalent", "Gestion de projets", "Illustrator", "After Effects", "Figma", "UEFN", "Photoshop"]
"skills": [
"Polyvalent",
"Gestion de projets",
"Illustrator",
"After Effects",
"Figma",
"UEFN",
"Photoshop",
"Verse"
],
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
},
{
"id": "353942721997832195",
@ -19,76 +45,146 @@
"avatar": "M",
"bio": "Je suis un développeur UEFN et Builder Minecraft polyvalent avec une solide expérience dans la création de systèmes pour divers Serveurs RP. J'ai travaillé sur des projets de plus ou moins grande envergure sur diverses plateformes de jeux. J'ai aussi une certaine expérience en gestion de projet et je me montre polyvalent pour beaucoup de tâches",
"stats": [
{ "value": "6", "label": "Ans d'exp." },
{ "value": "25+", "label": "Systèmes" },
{ "value": "∞", "label": "Fan de Louis de Funès" }
{
"value": "6",
"label": "Ans d'exp."
},
{
"value": "25+",
"label": "Systèmes"
},
{
"value": "∞",
"label": "Fan de Louis de Funès"
}
],
"skills": ["Polyvalent", "Gestion de projets", "UEFN", "Builder Minecraft"]
"skills": [
"Polyvalent",
"Gestion de projets",
"UEFN",
"Builder Minecraft"
],
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
},
{
"id": "971446412690722826",
"name": "Gameur",
"role": "Développeur Principal",
"role": "Agent - Développeur Principal",
"avatar": "G",
"bio": "Avec plus de 3 ans d'expérience dans le self-hosting, j'ai créé des bots pour des communautés de toutes tailles, allant de petits serveurs à des projets d'envergure. Mon expertise couvre la création de commandes personnalisées, l'intégration d'API tierces et la gestion de bases de données pour offrir des fonctionnalités avancées et une expérience utilisateur fluide.",
"stats": [
{ "value": "3", "label": "Ans d'exp." },
{ "value": "50+", "label": "Projets" },
{ "value": "★★★★", "label": "Intermédiaire - Avançé" }
{
"value": "3",
"label": "Ans d'exp."
},
{
"value": "50+",
"label": "Projets"
},
{
"value": "★★★★",
"label": "Intermédiaire - Avançé"
}
],
"skills": ["Python", "Discord.py", "API REST", "Docker", "self-hosting", "Linux", "Gestion de projets"]
},
{
"id": "1324439906046574693",
"name": "Lowei",
"role": "Développeur Backend",
"avatar": "L",
"bio": "Salut ! Je suis un développeur backend passionné par la création de systèmes robustes, performants et dans l'automatisation de tâches. N'hésite pas à checker mes réseaux ci-dessous !",
"stats": [
{ "value": "5", "label": "Ans d'exp." },
{ "value": "20+", "label": "Systèmes" },
{ "value": "∞", "label": "Disponibilité" }
"skills": [
"Python",
"Discord.py",
"API REST",
"Docker",
"self-hosting",
"Linux",
"Gestion de projets"
],
"skills": ["Fortnite Creative", "UEFN", "Level Design", "3D", "Lighting", "Scripting"]
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
},
{
"id": "561485865562406914",
"name": "KIRITO_KMA",
"role": "All Master & Développeur",
"role": "Agent - All Master & Développeur",
"avatar": "K",
"bio": "Au-delà des horizons et par-delà les temps",
"stats": [
{ "value": "4", "label": "Ans d'exp." },
{ "value": "50+", "label": "Projets menés" },
{ "value": "1000%", "label": "Maîtrise du shifoumi" }
{
"value": "4",
"label": "Ans d'exp."
},
{
"value": "50+",
"label": "Projets menés"
},
{
"value": "1000%",
"label": "Maîtrise du shifoumi"
}
],
"skills": ["Dev Full-Stack", "SEO", "Map-Maker", "Bot Discord", "Scénariste", "Shifoumi", "RedStone Minecraft"]
"skills": [
"Dev Full-Stack",
"SEO",
"Map-Maker",
"Bot Discord",
"Scénariste",
"Shifoumi",
"RedStone Minecraft"
],
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
},
{
"id": "983747660265693258",
"name": "Mr Reiko",
"role": "Développeur UEFN",
"avatar": "R",
"bio": "Lucas est l'architecte invisible derrière les systèmes les plus robustes d'OmégaKube. Spécialisé dans les bases de données et les API, il conçoit des systèmes d'économie capables de gérer des millions de transactions sans ralentissement.",
"id": "1324439906046574693",
"name": "Lowei",
"role": "Agent - Développeur Backend",
"avatar": "L",
"bio": "Salut ! Je suis un développeur backend passionné par la création de systèmes robustes, performants et dans l'automatisation de tâches. N'hésite pas à checker mes réseaux ci-dessous !",
"stats": [
{ "value": "8", "label": "Ans d'exp." },
{ "value": "30+", "label": "Systèmes" },
{ "value": "99.9%", "label": "Uptime" }
{
"value": "5",
"label": "Ans d'exp."
},
{
"value": "20+",
"label": "Systèmes"
},
{
"value": "∞",
"label": "Disponibilité"
}
],
"skills": ["Javascript", "HTML", "C++", "Verse", "Python", "UEFN", "Blender"]
},
{
"id": "1013376581458210986",
"name": "Kayrix",
"role": "Développer UEFN",
"avatar": "K",
"bio": "Je suis polyvalent et curieux, je cherche à toucher à tout sur l'UEFN. En parallèle, je suis en études de cinéma d'animation 3D et jeux vidéo, avec des compétences en modélisation 3D, animation et environnements sur Unreal Engine. Je développe aussi mes propres projets RP depuis un moment, ce qui m'a permis d'acquérir une bonne expérience, tout en restant dans une démarche d'apprentissage et d'amélioration continue.",
"stats": [
{ "value": "5", "label": "Ans d'exp." },
{ "value": "40+", "label": "Scénarios" },
{ "value": "∞", "label": "Créativité" }
"skills": [
"Python",
"HTML",
"CSS",
"C, C++",
"Java",
"Blender"
],
"skills": ["UEFN", "Maya", "Polyvalent", "Gestion de projets"]
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
},
{
"id": "1151495567256584253",
@ -97,10 +193,34 @@
"avatar": "K",
"bio": "Kuby est le gardien de la qualité du moindre octets au quetta-octets chez OmégaKube. Aucune commande ne lui échappe. Il execute méticuleusement chaque instructions, chaque commande, chaque interaction pour garantir une expérience sans accroc aux utilisateurs finaux.",
"stats": [
{ "value": "2", "label": "Yotta d'exp sur Discord" },
{ "value": "500+", "label": "Commande réalisées" },
{ "value": "0", "label": "Vulnerabilités" }
{
"value": "2",
"label": "Yotta d'exp sur Discord"
},
{
"value": "500+",
"label": "Commande réalisées"
},
{
"value": "0",
"label": "Vulnerabilités"
}
],
"skills": ["Moderation", "Events", "Documentation", "Serveur Discord", "Administration","Bot"]
"skills": [
"Moderation",
"Events",
"Documentation",
"Serveur Discord",
"Administration",
"Bot"
],
"social": {
"discord": "",
"twitter": "",
"instagram": "",
"youtube": "",
"tiktok": "",
"github": ""
}
}
]
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -12,16 +12,16 @@ document.addEventListener('DOMContentLoaded', () => {
const categoryLabels = { bot: 'Bot Discord', map: 'Fortnite Map', graphisme: 'Graphisme' };
homePortfolioGrid.innerHTML = first3.map((p, i) => {
const imgSrc = (p.images || '') + '1.png';
const firstFile = Array.isArray(p.files) && p.files.length > 0 ? p.files[0] : null;
const imgSrc = firstFile ? (p.images || '') + firstFile : null;
return `
<div class="portfolio-item reveal${delayClasses[i]}" data-project-id="${p.id}" role="button" tabindex="0" aria-label="Voir le projet ${p.title}">
<div class="portfolio-bg" style="background: ${p.gradient};">
<img src="${imgSrc}" alt="${p.title}" class="portfolio-cover-img" onerror="this.style.display='none'">
${imgSrc ? `<img src="${imgSrc}" alt="${p.title}" class="portfolio-cover-img" onerror="this.style.display='none'">` : ''}
</div>
<div class="portfolio-content">
<span class="portfolio-tag">${categoryLabels[p.category] || p.category}</span>
<h4>${p.title}</h4>
<p>${p.desc.substring(0, 80)}${p.desc.length > 80 ? '…' : ''}</p>
</div>
</div>
`;
@ -32,13 +32,24 @@ document.addEventListener('DOMContentLoaded', () => {
if (window.feather && feather.replace) try { feather.replace(); } catch(e) {}
}, 80);
// Délégation : attend que openProjectModal soit défini par realisations.js
// Délégation globale sur document — fonctionne même si realisations.js
// se charge après home.js (ordre des scripts defer non garanti)
homePortfolioGrid.addEventListener('click', e => {
const item = e.target.closest('.portfolio-item[data-project-id]');
if (!item) return;
const id = item.getAttribute('data-project-id');
if (typeof window.openProjectModal === 'function') {
window.openProjectModal(id);
} else {
// Retry : realisations.js pas encore initialisé, on attend
const wait = setInterval(() => {
if (typeof window.openProjectModal === 'function') {
clearInterval(wait);
window.openProjectModal(id);
}
}, 50);
// Abandon après 3 secondes
setTimeout(() => clearInterval(wait), 3000);
}
});
})
@ -163,16 +174,10 @@ document.addEventListener('DOMContentLoaded', () => {
</div>
`).join('');
const links = document.getElementById('partner-modal-links');
links.innerHTML = '';
if (p.url && p.url !== '#') {
links.innerHTML += `<a href="${p.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary"><i data-feather="external-link"></i> Site web</a>`;
}
if (p.discord && p.discord !== '#') {
links.innerHTML += `<a href="${p.discord}" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/></svg>
Discord
</a>`;
}
// Réseaux sociaux depuis le champ social du partenaire
links.innerHTML = typeof window.renderSocialLinks === 'function'
? window.renderSocialLinks(p.social)
: '';
if (window.feather) feather.replace();
partnerModalOverlay.classList.add('active');
document.body.style.overflow = 'hidden';
@ -302,13 +307,13 @@ document.addEventListener('DOMContentLoaded', () => {
// Modal
const modalOverlay = document.getElementById('team-modal-overlay');
const modalClose = document.getElementById('team-modal-close');
const modalAvatar = document.getElementById('modal-avatar');
const modalName = document.getElementById('modal-name');
const modalRole = document.getElementById('modal-role');
const modalBio = document.getElementById('modal-bio');
const modalStats = document.getElementById('modal-stats');
const modalSkills = document.getElementById('modal-skills');
const modalSocial = document.getElementById('modal-social');
const modalAvatar = document.getElementById('team-modal-avatar');
const modalName = document.getElementById('team-modal-name');
const modalRole = document.getElementById('team-modal-role');
const modalBio = document.getElementById('team-modal-bio');
const modalStats = document.getElementById('team-modal-stats');
const modalSkills = document.getElementById('team-modal-skills');
const modalSocial = document.getElementById('team-modal-social');
function openMemberModal(index) {
const m = members[index];
@ -330,14 +335,11 @@ document.addEventListener('DOMContentLoaded', () => {
</div>
`).join('');
modalSkills.innerHTML = (m.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
modalSocial.innerHTML = `
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" aria-label="Discord">
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
</a>
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="X (Twitter)">
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.259 5.631 5.905-5.631zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
</a>
`;
// Réseaux sociaux depuis le JSON du membre (+ fallback discord global)
const socialHtml = typeof window.renderSocialLinks === 'function'
? window.renderSocialLinks(m.social, (window.OK_SOCIAL || {}).discord)
: '';
modalSocial.innerHTML = socialHtml;
modalOverlay.classList.add('active');
document.body.style.overflow = 'hidden';
}

View file

@ -1,5 +1,58 @@
// OmégaKube - shared scripts
// ═══════════════════════════════════════════════════════════════════════════
// CONFIGURATION GLOBALE — liens réseaux sociaux
// Modifier ici pour mettre à jour partout sur le site
// ═══════════════════════════════════════════════════════════════════════════
window.OK_SOCIAL = {
discord: 'https://discord.gg/FwvNkvu5dy',
fortnite: 'https://www.fortnite.com/@omega_kube_team',
twitter: 'https://x.com/Omegakube',
instagram: 'https://www.instagram.com/omegakube/',
youtube: 'https://www.youtube.com/@OmegaKube',
tiktok: 'https://www.tiktok.com/@omegakube',
facebook: '#',
bluesky: '#',
email: 'contact@omegakube.fr'
};
// SVGs des réseaux — utilisé partout dans les modals
const SVG_ICONS = {
discord: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>',
fortnite: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M6 3h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm0 2v8h12V5H6zm2 2h2v1h1V7h2v4h-2V9h-1v2H8V7zm7 1a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm2 2a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-4 4l-2 4h2l1-2 1 2h2l-2-4h-2z"/></svg>',
twitter: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.259 5.631 5.905-5.631zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>',
instagram: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>',
youtube: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>',
tiktok: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-2.88 2.5 2.89 2.89 0 0 1-2.89-2.89 2.89 2.89 0 0 1 2.89-2.89c.28 0 .54.04.79.1V9.01a6.32 6.32 0 0 0-.79-.05 6.34 6.34 0 0 0-6.34 6.34 6.34 6.34 0 0 0 6.34 6.34 6.34 6.34 0 0 0 6.33-6.34V8.69a8.17 8.17 0 0 0 4.77 1.52V6.75a4.85 4.85 0 0 1-1-.06z"/></svg>',
github: '<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>',
website: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>',
};
/**
* Génère le HTML des liens sociaux pour les modals
* @param {Object} social - objet {discord, twitter, instagram, ...}
* @param {string} [discordFallback] - lien discord global si pas de lien perso
* @returns {string} HTML des icônes sociales
*/
window.renderSocialLinks = function(social, discordFallback) {
const S = social || {};
const G = window.OK_SOCIAL || {};
const entries = [
{ key: 'discord', url: S.discord || discordFallback || G.discord },
{ key: 'fortnite', url: S.fortnite || G.fortnite },
{ key: 'twitter', url: S.twitter },
{ key: 'instagram', url: S.instagram },
{ key: 'youtube', url: S.youtube },
{ key: 'tiktok', url: S.tiktok },
{ key: 'github', url: S.github },
{ key: 'website', url: S.website },
];
return entries
.filter(e => e.url && e.url !== '#' && e.url !== '')
.map(e => `<a href="${e.url}" target="_blank" rel="noopener noreferrer" aria-label="${e.key}">${SVG_ICONS[e.key] || ''}</a>`)
.join('');
};
// Feather icons loader (centralized)
// loads https://unpkg.com/feather-icons when needed and exposes window.ensureFeather()
const FEATHER_URL = 'https://unpkg.com/feather-icons';
@ -86,6 +139,27 @@ document.addEventListener('DOMContentLoaded', async () => {
// wait for includes to be loaded so subsequent initialization can bind to elements inside them
await loadIncludes();
// Hydrate les liens sociaux (data-social) avec les URLs de OK_SOCIAL
(function hydrateSocialLinks() {
const S = window.OK_SOCIAL || {};
const map = {
'discord': S.discord,
'twitter': S.twitter,
'instagram': S.instagram,
'youtube': S.youtube,
'tiktok': S.tiktok,
'facebook': S.facebook,
'bluesky': S.bluesky,
'email-link': S.email ? 'mailto:' + S.email : '#',
};
document.querySelectorAll('[data-social]').forEach(el => {
const key = el.getAttribute('data-social');
if (map[key] && map[key] !== '#') {
el.setAttribute('href', map[key]);
}
});
})();
// Déclenche les reveals maintenant que tous les includes sont dans le DOM
setTimeout(() => {
document.querySelectorAll('.reveal:not(.active)').forEach(el => {

View file

@ -57,19 +57,24 @@ document.addEventListener('DOMContentLoaded', async () => {
}
function renderProjects() {
if (!portfolioGrid) return; // pas sur la page réalisations
const filtered = getFilteredProjects();
const projects = getProjectsForPage(filtered, currentPage);
portfolioGrid.innerHTML = projects.map(project => `
<div class="portfolio-item reveal" data-project-id="${project.id}">
<div class="portfolio-bg" style="background: ${project.gradient};"></div>
<div class="portfolio-content">
<span class="portfolio-tag">${getCategoryLabel(project.category)}</span>
<h4>${project.title}</h4>
<p>${project.desc.substring(0, 80)}...</p>
portfolioGrid.innerHTML = projects.map(project => {
const prefix = isRoot ? '' : '../';
const firstFile = Array.isArray(project.files) && project.files.length > 0 ? project.files[0] : null;
const coverImg = firstFile ? `<img src="${prefix}${project.images}${firstFile}" alt="${project.title}" class="portfolio-cover-img" onerror="this.style.display='none'">` : '';
return `
<div class="portfolio-item reveal" data-project-id="${project.id}">
<div class="portfolio-bg" style="background: ${project.gradient};">${coverImg}</div>
<div class="portfolio-content">
<span class="portfolio-tag">${getCategoryLabel(project.category)}</span>
<h4>${project.title}</h4>
</div>
</div>
</div>
`).join('');
`;
}).join('');
// activate reveals & icons
setTimeout(() => {
@ -83,6 +88,7 @@ document.addEventListener('DOMContentLoaded', async () => {
}
function renderPagination() {
if (!paginationContainer) return; // pas sur la page réalisations
const filtered = getFilteredProjects();
const totalPages = getTotalPages(filtered);
@ -183,35 +189,75 @@ document.addEventListener('DOMContentLoaded', async () => {
</div>
`).join('');
// Gallery : charger les images du dossier via convention de nommage
// images/projects/{slug}/1.png, 2.png... avec fallback gradient
const imgPath = project.images || '';
const imgExtensions = ['png', 'jpg', 'jpeg', 'webp'];
// On tente de charger jusqu'à 6 images numérotées depuis le dossier
const maxImages = 6;
let slidesHtml = '';
// Toujours créer au moins 1 slide gradient (fallback)
// Les images réelles se chargent avec onerror pour masquer si absentes
for (let i = 1; i <= maxImages; i++) {
slidesHtml += `
<div class="project-gallery-slide" data-img-idx="${i}" style="background: ${project.gradient};">
<img src="${imgPath}${i}.png" alt="${project.title} ${i}"
class="gallery-slide-img"
onerror="this.parentElement.classList.add('no-image')"
onload="this.parentElement.classList.add('has-image')">
</div>`;
}
gallerySlides.innerHTML = slidesHtml;
// Gallery : utilise project.files (noms réels) avec fallback gradient si vide
const imgPath = (isRoot ? '' : '../') + (project.images || '');
const files = Array.isArray(project.files) ? project.files : [];
// reset gallery state
if (files.length > 0) {
// Images réelles nommées explicitement dans le JSON
// Supporte : fichier local, mp4 local, URL YouTube, shortcode yt:VIDEO_ID
gallerySlides.innerHTML = files.map((filename, i) => {
const isLocalVideo = /\.(mp4|webm|ogg)$/i.test(filename);
// Détecte YouTube : URL complète ou shortcode yt:ID
const ytMatch = filename.match(/(?:yt:|youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
if (ytMatch) {
const videoId = ytMatch[1];
return `
<div class="project-gallery-slide slide-right gallery-slide-youtube" data-img-idx="${i}" style="background:${project.gradient};">
<div class="yt-wrapper">
<iframe
src="https://www.youtube.com/embed/${videoId}?rel=0&modestbranding=1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
class="gallery-slide-yt-iframe"
title="Vidéo YouTube ${project.title}"
loading="lazy">
</iframe>
</div>
</div>`;
}
if (isLocalVideo) {
return `
<div class="project-gallery-slide slide-right" data-img-idx="${i}" style="background:${project.gradient};">
<video class="gallery-slide-img gallery-video" src="${imgPath}${filename}" muted loop autoplay playsinline></video>
<button class="gallery-mute-btn" aria-label="Activer le son" title="Activer le son" onclick="(function(btn){var v=btn.closest('.project-gallery-slide').querySelector('video');v.muted=!v.muted;btn.classList.toggle('unmuted',!v.muted);btn.setAttribute('aria-label',v.muted?'Activer le son':'Couper le son');})(this)">
<svg class="icon-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
<svg class="icon-unmuted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
</button>
</div>`;
}
return `
<div class="project-gallery-slide slide-right" data-img-idx="${i}" style="background:${project.gradient};">
<img src="${imgPath}${filename}" alt="${project.title} ${i + 1}"
class="gallery-slide-img"
onerror="this.parentElement.classList.add('no-image');this.style.display='none'">
</div>`;
}).join('');
} else {
// Fallback : une slide gradient unique
gallerySlides.innerHTML = `
<div class="project-gallery-slide has-image" style="background:${project.gradient};"></div>`;
}
// reset gallery + affichage immédiat
galleryIndex = 0;
updateGallery();
updateGalleryDots();
if (modalOverlay) modalOverlay.classList.add('active');
// Réseaux sociaux du projet
const projectSocialEl = document.getElementById('modal-project-social');
if (projectSocialEl) {
projectSocialEl.innerHTML = typeof window.renderSocialLinks === 'function'
? window.renderSocialLinks(project.social)
: '';
}
if (window.feather && feather.replace) {
try { feather.replace(); } catch (e) {}
}
@ -223,88 +269,100 @@ document.addEventListener('DOMContentLoaded', async () => {
// Gallery helpers
let galleryIndex = 0;
function getVisibleSlides() {
function getAllSlides() {
if (!gallerySlides) return [];
// Retourne uniquement les slides qui ont une image chargée OU qui sont la première (fallback gradient)
const all = Array.from(gallerySlides.children);
const withImg = all.filter(s => s.classList.contains('has-image'));
return withImg.length > 0 ? withImg : [all[0]].filter(Boolean);
return Array.from(gallerySlides.children).filter(s => !s.classList.contains('no-image'));
}
function updateGallery() {
const visible = getVisibleSlides();
const all = gallerySlides ? Array.from(gallerySlides.children) : [];
// Cache toutes les slides non visibles
all.forEach(s => {
if (!visible.includes(s)) { s.style.display = 'none'; }
else { s.style.display = ''; }
});
// Positionne les slides visibles
visible.forEach((s, i) => {
s.style.transform = `translateX(${(i - galleryIndex) * 100}%)`;
const slides = getAllSlides();
if (!slides.length) return;
if (galleryIndex >= slides.length) galleryIndex = 0;
if (galleryIndex < 0) galleryIndex = slides.length - 1;
slides.forEach((s, i) => {
// Le CSS gère tout via .active — on ne touche JAMAIS à style.transform
s.classList.toggle('active', i === galleryIndex);
// Juste la direction (gauche/droite) via une classe data
if (i < galleryIndex) {
s.classList.add('slide-left');
s.classList.remove('slide-right');
} else if (i > galleryIndex) {
s.classList.add('slide-right');
s.classList.remove('slide-left');
} else {
s.classList.remove('slide-left', 'slide-right');
}
});
// Clamp index
if (galleryIndex >= visible.length) galleryIndex = Math.max(0, visible.length - 1);
}
function updateGalleryDots() {
const galleryDots = document.getElementById('gallery-dots');
if (!galleryDots) return;
// Attend que les images se chargent avant de créer les dots
setTimeout(() => {
const visible = getVisibleSlides();
galleryDots.innerHTML = visible.map((_, i) =>
`<div class="gallery-dot${i === 0 ? ' active' : ''}" data-idx="${i}"></div>`
).join('');
galleryDots.querySelectorAll('.gallery-dot').forEach(dot => {
dot.addEventListener('click', () => {
galleryIndex = parseInt(dot.dataset.idx);
updateGallery();
galleryDots.querySelectorAll('.gallery-dot').forEach((d, i) => d.classList.toggle('active', i === galleryIndex));
});
const slides = getAllSlides();
galleryDots.innerHTML = slides.map((_, i) =>
`<div class="gallery-dot${i === 0 ? ' active' : ''}" data-idx="${i}"></div>`
).join('');
galleryDots.querySelectorAll('.gallery-dot').forEach(dot => {
dot.addEventListener('click', () => {
galleryIndex = parseInt(dot.dataset.idx);
updateGallery();
galleryDots.querySelectorAll('.gallery-dot').forEach((d, i) =>
d.classList.toggle('active', i === galleryIndex));
});
}, 300);
});
}
function syncDots() {
const galleryDots = document.getElementById('gallery-dots');
if (!galleryDots) return;
galleryDots.querySelectorAll('.gallery-dot').forEach((d, i) =>
d.classList.toggle('active', i === galleryIndex));
}
function prevSlide() {
const visible = getVisibleSlides();
galleryIndex = galleryIndex <= 0 ? visible.length - 1 : galleryIndex - 1;
galleryIndex = galleryIndex <= 0 ? getAllSlides().length - 1 : galleryIndex - 1;
updateGallery();
syncDots();
}
function nextSlide() {
const visible = getVisibleSlides();
galleryIndex = galleryIndex >= visible.length - 1 ? 0 : galleryIndex + 1;
galleryIndex = galleryIndex >= getAllSlides().length - 1 ? 0 : galleryIndex + 1;
updateGallery();
syncDots();
}
// Event bindings
filterTabs.forEach(tab => tab.addEventListener('click', (e) => {
filterTabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentFilter = tab.dataset.filter || 'all';
currentPage = 1;
renderProjects();
renderPagination();
}));
// Event bindings — uniquement sur la page réalisations
if (filterTabs.length) {
filterTabs.forEach(tab => tab.addEventListener('click', (e) => {
filterTabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentFilter = tab.dataset.filter || 'all';
currentPage = 1;
renderProjects();
renderPagination();
}));
}
paginationContainer.addEventListener('click', (e) => {
const btn = e.target.closest('button[data-page]');
if (!btn) return;
const page = Number(btn.getAttribute('data-page'));
goToPage(page);
});
if (paginationContainer) {
paginationContainer.addEventListener('click', (e) => {
const btn = e.target.closest('button[data-page]');
if (!btn) return;
const page = Number(btn.getAttribute('data-page'));
goToPage(page);
});
}
portfolioGrid.addEventListener('click', (e) => {
const item = e.target.closest('.portfolio-item');
if (!item) return;
const id = item.getAttribute('data-project-id');
openProjectModal(id);
});
if (portfolioGrid) {
portfolioGrid.addEventListener('click', (e) => {
const item = e.target.closest('.portfolio-item');
if (!item) return;
const id = item.getAttribute('data-project-id');
openProjectModal(id);
});
}
if (modalClose) modalClose.addEventListener('click', closeProjectModal);
if (modalOverlay) modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeProjectModal(); });
window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeProjectModal(); });
window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay && modalOverlay.classList.contains('active')) closeProjectModal(); });
if (galleryPrev) galleryPrev.addEventListener('click', prevSlide);
if (galleryNext) galleryNext.addEventListener('click', nextSlide);
@ -331,9 +389,16 @@ document.addEventListener('DOMContentLoaded', async () => {
const featuresHtml = (p.features || []).slice(0, 6).map(f => `<span class="bento-feature">${f}</span>`).join('');
const divider = i < featured.length - 1 ? '<div class="bento-divider"></div>' : '';
const prefix = isRoot ? '' : '../';
const firstFile = Array.isArray(p.files) && p.files.length > 0 ? p.files[0] : null;
const bentoCover = firstFile
? `<img src="${prefix}${p.images}${firstFile}" alt="${p.title}" class="bento-cover-img" onerror="this.style.display='none'">`
: '';
return `
<div class="bento-project${isReverse} reveal" data-project-id="${p.id}" style="cursor:pointer;" role="button" tabindex="0" aria-label="Voir le projet ${p.title}">
<div class="bento-image" style="background: ${p.gradient};">
${bentoCover}
<div class="bento-image-content">
<span class="portfolio-tag">${categoryLabels[p.category] || p.category}</span>
<h3>${p.title}</h3>

View file

@ -10,13 +10,13 @@
<h3>Discutons ensemble</h3>
<p>Vous avez un serveur à développer, une map à créer ou besoin d'un bot personnalisé ? Notre équipe est prête à donner vie à vos idées.</p>
<div class="contact-links">
<a href="mailto:contact@omegakube.fr" class="contact-link">
<a href="#" data-social="email-link" class="contact-link">
<div class="contact-link-icon">
<i data-feather="mail"></i>
</div>
<span>contact@omegakube.fr</span>
</a>
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" class="contact-link">
<a href="#" data-social="discord" target="_blank" rel="noopener noreferrer" class="contact-link">
<div class="contact-link-icon">
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/></svg>
</div>

View file

@ -30,31 +30,35 @@
</div>
<div class="footer-social">
<!-- Discord -->
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" aria-label="Discord">
<a href="#" data-social="discord" target="_blank" rel="noopener noreferrer" aria-label="Discord">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
</a>
<!-- Fortnite -->
<a href="https://www.fortnite.com/@omega_kube_team" target="_blank" rel="noopener noreferrer" aria-label="Fortnite">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M6 3h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm0 2v8h12V5H6zm2 2h2v1h1V7h2v4h-2V9h-1v2H8V7zm7 1a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm2 2a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-4 4l-2 4h2l1-2 1 2h2l-2-4h-2z"/></svg>
</a>
<!-- X / Twitter -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="X (Twitter)">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="X (Twitter)" data-social="twitter">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.259 5.631 5.905-5.631zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
</a>
<!-- Instagram -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Instagram">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Instagram" data-social="instagram">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
</a>
<!-- YouTube -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="YouTube">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="YouTube" data-social="youtube">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
</a>
<!-- TikTok -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="TikTok">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="TikTok" data-social="tiktok">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M19.59 6.69a4.83 4.83 0 0 1-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 0 1-2.88 2.5 2.89 2.89 0 0 1-2.89-2.89 2.89 2.89 0 0 1 2.89-2.89c.28 0 .54.04.79.1V9.01a6.32 6.32 0 0 0-.79-.05 6.34 6.34 0 0 0-6.34 6.34 6.34 6.34 0 0 0 6.34 6.34 6.34 6.34 0 0 0 6.33-6.34V8.69a8.17 8.17 0 0 0 4.77 1.52V6.75a4.85 4.85 0 0 1-1-.06z"/></svg>
</a>
<!-- Facebook -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Facebook">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Facebook" data-social="facebook">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
</a>
<!-- Bluesky -->
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Bluesky">
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="Bluesky" data-social="bluesky">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8z"/></svg>
</a>
</div>

View file

@ -170,16 +170,16 @@
<i data-feather="x"></i>
</button>
<div class="team-modal-avatar">
<div class="team-modal-avatar-inner" id="modal-avatar">A</div>
<div class="team-modal-avatar-inner" id="team-modal-avatar">A</div>
</div>
<h3 class="team-modal-name" id="modal-name">Alexandre</h3>
<p class="team-modal-role" id="modal-role">Développeur Principal</p>
<h3 class="team-modal-name" id="team-modal-name">Alexandre</h3>
<p class="team-modal-role" id="team-modal-role">Développeur Principal</p>
</div>
<div class="team-modal-content">
<p class="team-modal-bio" id="modal-bio">Bio du membre...</p>
<div class="team-modal-stats" id="modal-stats"></div>
<div class="team-modal-skills" id="modal-skills"></div>
<div class="team-modal-social" id="modal-social"></div>
<p class="team-modal-bio" id="team-modal-bio">Bio du membre...</p>
<div class="team-modal-stats" id="team-modal-stats"></div>
<div class="team-modal-skills" id="team-modal-skills"></div>
<div class="team-modal-social" id="team-modal-social"></div>
</div>
</div>
</div>
@ -272,18 +272,18 @@
<h3>Discutons ensemble</h3>
<p>Vous avez un serveur à développer, une map à créer ou besoin d'un bot personnalisé ? Notre équipe est prête à donner vie à vos idées.</p>
<div class="contact-links">
<a href="mailto:contact@omegakube.fr" class="contact-link">
<a href="#" data-social="email-link" class="contact-link">
<div class="contact-link-icon">
<i data-feather="mail"></i>
</div>
<span>contact@omegakube.fr</span>
</a>
<a href="https://discord.gg/FwvNkvu5dy" class="contact-link">
<a href="#" data-social="discord" class="contact-link">
<div class="contact-link-icon">
<!-- <i data-feather="message-circle"></i> -->
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
</div>
<span>Discord : OmégaKube</span>
<span>Discord : OmegaKube</span>
</a>
</div>
</div>
@ -448,6 +448,7 @@
<div class="project-modal-tab-content active" id="tab-features"><div class="project-features-grid" id="modal-features"></div></div>
<div class="project-modal-tab-content" id="tab-tech"><div class="project-tech-grid" id="modal-tech"></div></div>
<div class="project-modal-tab-content" id="tab-stats"><div class="project-stats-grid" id="modal-stats"></div></div>
<div class="team-modal-social" id="modal-project-social"></div>
</div>
</div>
</div>

View file

@ -32,7 +32,7 @@
<i data-feather="message-circle"></i>
Nous contacter
</a>
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" class="btn btn-secondary">
<a href="#" data-social="discord" target="_blank" rel="noopener noreferrer" class="btn btn-secondary">
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
Rejoindre notre Discord
</a>

View file

@ -146,6 +146,7 @@
<div class="project-modal-tab-content active" id="tab-features"><div class="project-features-grid" id="modal-features"></div></div>
<div class="project-modal-tab-content" id="tab-tech"><div class="project-tech-grid" id="modal-tech"></div></div>
<div class="project-modal-tab-content" id="tab-stats"><div class="project-stats-grid" id="modal-stats"></div></div>
<div class="team-modal-social" id="modal-project-social"></div>
</div>
</div>
</div>
@ -158,7 +159,7 @@
<p class="section-desc">Votre projet pourrait être le prochain dans notre portfolio</p>
</div>
<div style="text-align: center;" class="reveal">
<a href="index.html#contact" class="btn btn-primary">
<a href="../index.html" class="btn btn-primary">
<i data-feather="message-circle"></i>
Démarrer un projet
</a>