v1.5.2
This commit is contained in:
parent
7d26ffcee4
commit
25ea6cf6f6
19 changed files with 2270 additions and 467 deletions
12
.htaccess
12
.htaccess
|
|
@ -4,14 +4,22 @@ Options -Indexes
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
|
|
||||||
# Si fichier/dossier n'existe pas → afficher 404.php
|
# Force HTTPS (décommenter en production)
|
||||||
|
# RewriteCond %{HTTPS} off
|
||||||
|
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||||
|
|
||||||
|
# Si le fichier ou dossier demandé n'existe pas → 404
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
RewriteRule ^ pages/404.php [L]
|
RewriteRule ^ pages/404.php [L]
|
||||||
|
|
||||||
# Si c'est un dossier sans index → afficher 404.php
|
# Dossier sans index → 404
|
||||||
RewriteCond %{REQUEST_FILENAME} -d
|
RewriteCond %{REQUEST_FILENAME} -d
|
||||||
RewriteCond %{REQUEST_FILENAME}/index.html !-f
|
RewriteCond %{REQUEST_FILENAME}/index.html !-f
|
||||||
RewriteCond %{REQUEST_FILENAME}/index.php !-f
|
RewriteCond %{REQUEST_FILENAME}/index.php !-f
|
||||||
RewriteRule ^ pages/404.php [L]
|
RewriteRule ^ pages/404.php [L]
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
|
# Page d'erreur Apache native (fallback si mod_rewrite absent)
|
||||||
|
ErrorDocument 404 /pages/404.php
|
||||||
|
ErrorDocument 403 /pages/404.php
|
||||||
|
|
|
||||||
|
|
@ -441,6 +441,19 @@ nav.scrolled {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Le contenu du page-hero est toujours visible dès le chargement */
|
||||||
|
.page-hero .reveal,
|
||||||
|
.page-hero-content {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
transition: none;
|
||||||
|
animation: fadeInUp 0.7s ease forwards;
|
||||||
|
}
|
||||||
|
.page-hero .reveal.active {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-content {
|
.hero-content {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
|
|
@ -989,14 +1002,14 @@ section {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: -4px;
|
inset: -4px;
|
||||||
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||||
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
clip-path: polygon(29.3% 0%, 70.7% 0%, 100% 29.3%, 100% 70.7%, 70.7% 100%, 29.3% 100%, 0% 70.7%, 0% 29.3%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-avatar-inner {
|
.team-avatar-inner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
clip-path: polygon(29.3% 0%, 70.7% 0%, 100% 29.3%, 100% 70.7%, 70.7% 100%, 29.3% 100%, 0% 70.7%, 0% 29.3%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -1415,6 +1428,14 @@ footer {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-social a svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: currentColor;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-social a:hover {
|
.footer-social a:hover {
|
||||||
|
|
@ -1423,6 +1444,110 @@ footer {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Partners Section ──────────────────────────────────────────────────────── */
|
||||||
|
.partners {
|
||||||
|
padding: 6rem 2rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partners-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.25rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-card:hover {
|
||||||
|
border-color: var(--accent-primary);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 8px 30px rgba(var(--accent-primary-rgb, 142, 36, 170), 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-avatar {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: white;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-info h4 {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-info p {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Easter Egg ────────────────────────────────────────────────────────────── */
|
||||||
|
#easter-egg-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.85);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easter-egg-box {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 2px solid var(--accent-primary);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3rem 2.5rem;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 480px;
|
||||||
|
width: 90%;
|
||||||
|
animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.easter-egg-icon {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easter-egg-box h2 {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--accent-light);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.easter-egg-box p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleIn {
|
||||||
|
from { transform: scale(0.7); opacity: 0; }
|
||||||
|
to { transform: scale(1); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
.footer-bottom {
|
.footer-bottom {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 2rem auto 0;
|
margin: 2rem auto 0;
|
||||||
|
|
@ -1697,14 +1822,14 @@ footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: -4px;
|
inset: -4px;
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.3);
|
||||||
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
clip-path: polygon(29.3% 0%, 70.7% 0%, 100% 29.3%, 100% 70.7%, 70.7% 100%, 29.3% 100%, 0% 70.7%, 0% 29.3%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-modal-avatar-inner {
|
.team-modal-avatar-inner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
clip-path: polygon(29.3% 0%, 70.7% 0%, 100% 29.3%, 100% 70.7%, 70.7% 100%, 29.3% 100%, 0% 70.7%, 0% 29.3%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -1815,6 +1940,8 @@ footer {
|
||||||
.team-modal-social a svg {
|
.team-modal-social a svg {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
fill: currentColor;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
|
@ -2535,50 +2662,9 @@ footer {
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-tech-item {
|
/* project-tech-item and project-stats-grid → see end of file */
|
||||||
padding: 0.75rem 1.25rem;
|
|
||||||
background: var(--glass-bg);
|
|
||||||
border: 1px solid var(--glass-border);
|
|
||||||
border-radius: 4px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-tech-item:hover {
|
/* project-stat-card-value / label → see end of file */
|
||||||
border-color: var(--accent-primary);
|
|
||||||
color: var(--accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stats Grid in Modal */
|
|
||||||
.project-stats-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-stat-card {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1.5rem;
|
|
||||||
background: var(--glass-bg);
|
|
||||||
border: 1px solid var(--glass-border);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-stat-card-value {
|
|
||||||
font-family: 'OmegaLarge', sans-serif;
|
|
||||||
font-size: 2rem;
|
|
||||||
color: var(--accent-light);
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-stat-card-label {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.project-modal {
|
.project-modal {
|
||||||
|
|
@ -2728,7 +2814,11 @@ footer {
|
||||||
}
|
}
|
||||||
|
|
||||||
.process-step.active .process-step-icon {
|
.process-step.active .process-step-icon {
|
||||||
color: white;
|
color: var(--accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .process-step.active .process-step-icon {
|
||||||
|
color: var(--accent-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.process-step-content {
|
.process-step-content {
|
||||||
|
|
@ -3585,3 +3675,497 @@ footer {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
HOME — Partners carousel
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.partners-carousel-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2rem 0;
|
||||||
|
position: relative;
|
||||||
|
mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
|
||||||
|
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partners-carousel-track {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
width: max-content;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-slide {
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-slide-inner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.25rem 1.75rem;
|
||||||
|
width: 160px;
|
||||||
|
transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-slide:hover .partner-slide-inner {
|
||||||
|
border-color: var(--accent-primary);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(142,36,170,0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-logo-img {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-logo-fallback {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: white;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-slide-name {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Partner modal — réutilise team-modal, surcharge avatar */
|
||||||
|
.partner-modal-avatar {
|
||||||
|
background: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-modal-avatar-photo {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 10px;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-modal-avatar-fallback {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
border-radius: 10px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: white;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
TEAM — Avatar photo
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.team-avatar-photo {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-avatar-fallback {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-avatar-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal avatar photo */
|
||||||
|
.team-modal-avatar-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
SERVICES — Note
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.service-note {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-note-mark {
|
||||||
|
color: var(--accent-light);
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
FAQ — Page
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.faq-teaser {
|
||||||
|
padding: 0 2rem 2rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-teaser-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 2rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-left: 4px solid var(--accent-primary);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-teaser-text p.section-tag {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-teaser-text h2 {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-teaser-text p:last-child {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-teaser-btn {
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-page-section {
|
||||||
|
padding: 2rem 2rem 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-page-container {
|
||||||
|
max-width: 860px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-category-title {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--accent-light);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
border-bottom: 1px solid var(--glass-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-category-title svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-item {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-item.open {
|
||||||
|
border-color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-question {
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 1.1rem 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-align: left;
|
||||||
|
transition: color 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-question:hover {
|
||||||
|
color: var(--accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-chevron {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--text-muted);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-item.open .faq-chevron {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
color: var(--accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-answer {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.35s cubic-bezier(0.4,0,0.2,1), padding 0.3s ease;
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-item.open .faq-answer {
|
||||||
|
max-height: 300px;
|
||||||
|
padding: 0 1.5rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-answer p {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-answer strong {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-cta-section {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-top: 1px solid var(--glass-border);
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-cta-inner {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-cta-inner h2 {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-cta-inner p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-cta-btns {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.faq-teaser-inner {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.faq-teaser-btn {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
GALLERY — image réelle dans les slides
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* Slide sans image chargée → masqué */
|
||||||
|
.project-gallery-slide.no-image {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slide avec image → l'image remplace le gradient */
|
||||||
|
.project-gallery-slide.has-image {
|
||||||
|
background: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-slide-img {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enlève l'overlay gradient quand une vraie image est présente */
|
||||||
|
.project-gallery-slide.has-image::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
MODAL TECH — tech items avec icônes
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.project-tech-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tech-item {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--glass-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tech-item svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tech-item:hover {
|
||||||
|
border-color: var(--accent-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: rgba(142, 36, 170, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
MODAL STATS — grille 2×2 valorisée
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.project-stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem 1rem;
|
||||||
|
background: var(--glass-bg);
|
||||||
|
border: 1px solid var(--glass-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: border-color 0.25s ease, transform 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card:hover {
|
||||||
|
border-color: var(--accent-primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card-icon svg {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card-value {
|
||||||
|
font-family: 'OmegaLarge', sans-serif;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
color: var(--accent-light);
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-stat-card-label {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
PORTFOLIO — image de couverture dans les cartes
|
||||||
|
═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
.portfolio-cover-img {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .portfolio-bg conserve son gradient en fallback derrière l'image */
|
||||||
|
.portfolio-bg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
|
||||||
78
assets/data/faq.json
Normal file
78
assets/data/faq.json
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "delais",
|
||||||
|
"icon": "clock",
|
||||||
|
"title": "Délais & Process",
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"q": "Quels sont les délais habituels pour un bot Discord ?",
|
||||||
|
"a": "Un bot Discord RP complet demande généralement entre 2 et 4 semaines selon la complexité des systèmes (économie, combat, inventaire…). Nous établissons un planning détaillé dès la validation du devis."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Et pour une map Fortnite ?",
|
||||||
|
"a": "La création d'une map UEFN prend entre 3 et 6 semaines. Ce délai inclut le design, la construction, les scripts Verse et les tests de performance. Des points réguliers sont organisés pour valider chaque étape."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Comment se déroule le processus du début à la fin ?",
|
||||||
|
"a": "Notre processus se déroule en 4 étapes : <strong>Contact</strong> (échange sur vos besoins), <strong>Devis</strong> (proposition détaillée et validation), <strong>Création</strong> (développement avec suivi régulier) et <strong>Livraison</strong> (tests, ajustements et remise des fichiers). Vous êtes impliqué à chaque étape."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tarifs",
|
||||||
|
"icon": "dollar-sign",
|
||||||
|
"title": "Tarifs & Devis",
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"q": "Comment sont calculés vos tarifs ?",
|
||||||
|
"a": "Chaque projet est unique, nos tarifs sont donc établis sur devis selon la complexité, le nombre de fonctionnalités et les délais souhaités. Le devis est gratuit, détaillé et sans engagement."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Le consulting est-il vraiment gratuit ?",
|
||||||
|
"a": "Oui, la première consultation est entièrement gratuite et sans engagement. Elle permet de cerner vos besoins, évaluer votre projet et vous orienter vers la solution la plus adaptée, qu'elle soit payante ou non."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Y a-t-il des frais cachés ?",
|
||||||
|
"a": "Non. Le devis que nous soumettons est exhaustif. Les éventuels ajouts de fonctionnalités en cours de projet font l'objet d'un avenant écrit et validé par les deux parties avant toute réalisation."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "revisions",
|
||||||
|
"icon": "refresh-cw",
|
||||||
|
"title": "Révisions & Support",
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"q": "Que signifient les « révisions illimitées* » en graphisme ?",
|
||||||
|
"a": "Les révisions illimitées s'appliquent aux ajustements dans le cadre du brief initial : couleurs, proportions, typo, espacement. Elles ne couvrent pas les changements de direction créative complète (nouveau concept, refonte totale), qui sont facturés séparément."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Que se passe-t-il si mon bot plante après livraison ?",
|
||||||
|
"a": "Un mois de support est inclus après chaque livraison. Durant cette période, tout bug lié à notre code est corrigé gratuitement et en priorité. Passé ce délai, un contrat de maintenance peut être négocié."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Puis-je modifier moi-même le projet après livraison ?",
|
||||||
|
"a": "Oui, vous recevez les fichiers sources complets. Nous livrons également une documentation pour vous permettre d'effectuer des modifications de base. Pour des évolutions plus complexes, notre équipe reste disponible."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "technique",
|
||||||
|
"icon": "code",
|
||||||
|
"title": "Technique",
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"q": "Quelles technologies utilisez-vous pour les bots Discord ?",
|
||||||
|
"a": "Nos bots sont développés principalement en Python avec discord.py, couplés à une base de données MySQL. Pour l'hébergement, nous pouvons vous guider vers des solutions adaptées (VPS, serveur dédié) ou gérer l'hébergement pour vous."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Les maps Fortnite sont-elles compatibles avec les dernières mises à jour du jeu ?",
|
||||||
|
"a": "Oui. Nous développons exclusivement via UEFN (Unreal Editor for Fortnite), l'outil officiel d'Epic Games, ce qui garantit une compatibilité optimale et l'accès aux dernières fonctionnalités disponibles."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"q": "Pouvez-vous reprendre un projet existant ?",
|
||||||
|
"a": "Oui, nous réalisons des audits de projets existants. Après analyse du code ou de la map, nous vous soumettons un rapport et un plan d'action. C'est d'ailleurs inclus dans notre offre de consulting gratuit."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
50
assets/data/partners.json
Normal file
50
assets/data/partners.json
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "victorias-vice",
|
||||||
|
"displayName": "Victoria's Vice",
|
||||||
|
"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"],
|
||||||
|
"stats": [
|
||||||
|
{ "icon": "users", "value": "70", "label": "Joueurs max" },
|
||||||
|
{ "icon": "home", "value": "30+", "label": "Bâtiments" },
|
||||||
|
{ "icon": "play", "value": "30+", "label": "Sessions" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "partenaire-2",
|
||||||
|
"displayName": "Partenaire 2",
|
||||||
|
"description": "Description courte du partenaire.",
|
||||||
|
"longDesc": "Description longue du partenaire à renseigner.",
|
||||||
|
"gradient": "linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))",
|
||||||
|
"url": "#",
|
||||||
|
"discord": "#",
|
||||||
|
"since": "2025",
|
||||||
|
"services": ["Service 1", "Service 2"],
|
||||||
|
"stats": [
|
||||||
|
{ "icon": "star", "value": "?", "label": "Stat 1" },
|
||||||
|
{ "icon": "zap", "value": "?", "label": "Stat 2" },
|
||||||
|
{ "icon": "heart", "value": "?", "label": "Stat 3" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "partenaire-3",
|
||||||
|
"displayName": "Partenaire 3",
|
||||||
|
"description": "Description courte du partenaire.",
|
||||||
|
"longDesc": "Description longue du partenaire à renseigner.",
|
||||||
|
"gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)",
|
||||||
|
"url": "#",
|
||||||
|
"discord": "#",
|
||||||
|
"since": "2025",
|
||||||
|
"services": ["Service 1", "Service 2"],
|
||||||
|
"stats": [
|
||||||
|
{ "icon": "star", "value": "?", "label": "Stat 1" },
|
||||||
|
{ "icon": "zap", "value": "?", "label": "Stat 2" },
|
||||||
|
{ "icon": "heart", "value": "?", "label": "Stat 3" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -7,21 +7,113 @@
|
||||||
"gradient": "linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))",
|
"gradient": "linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))",
|
||||||
"year": "2025",
|
"year": "2025",
|
||||||
"duration": "3 semaines",
|
"duration": "3 semaines",
|
||||||
"features": ["Système d'absences staff", "Systèmes de sécurité et logs", "Gestion de sanctions", "Système de convocations", "Tickets avec stats", "Et bien plus à venir"],
|
"features": [
|
||||||
"tech": ["Python", "Discord.py", "MySQL", "Bach"],
|
"Système d'absences staff",
|
||||||
"stats": [{ "value": "1075+", "label": "Utilisateurs" }, { "value": "150+", "label": "Commandes" }, { "value": "97%", "label": "Uptime" }]
|
"Systèmes de sécurité et logs",
|
||||||
|
"Gestion de sanctions",
|
||||||
|
"Système de convocations",
|
||||||
|
"Tickets avec stats",
|
||||||
|
"Et bien plus à venir"
|
||||||
|
],
|
||||||
|
"tech": [
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"icon": "code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Discord.py",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MySQL",
|
||||||
|
"icon": "database"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bash",
|
||||||
|
"icon": "terminal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"icon": "users",
|
||||||
|
"value": "1075+",
|
||||||
|
"label": "Utilisateurs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "terminal",
|
||||||
|
"value": "150+",
|
||||||
|
"label": "Commandes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "activity",
|
||||||
|
"value": "97%",
|
||||||
|
"label": "Uptime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "clock",
|
||||||
|
"value": "3",
|
||||||
|
"label": "Semaines de dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": "assets/images/projects/kuby/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"category": "map",
|
"category": "map",
|
||||||
"title": "Victoria's Vice",
|
"title": "Victoria's Vice",
|
||||||
"desc": "Victoria's Vice est serveur RP whitelist Fortnite, Victoria’s Vice, qui est basé sur un thème italien ! Plongez-vous dans un univers authentique où chaque partie est une immersion dans les rues, la culture, et l’ambiance unique italienne.",
|
"desc": "Victoria's Vice est serveur RP whitelist Fortnite, Victoria's Vice, qui est basé sur un thème italien ! Plongez-vous dans un univers authentique où chaque partie est une immersion dans les rues, la culture, et l'ambiance unique italienne.",
|
||||||
"gradient": "linear-gradient(135deg, #5E2040, #8b3a62)",
|
"gradient": "linear-gradient(135deg, #5E2040, #8b3a62)",
|
||||||
"year": "2025",
|
"year": "2025",
|
||||||
"duration": "4 mois",
|
"duration": "4 mois",
|
||||||
"features": ["économie réaliste", "30+ bâtiments", "Système jour/nuit", "Serveur Whitelist", "Métiers légaux variés", "Bot RP Vicy exclusif"],
|
"features": [
|
||||||
"tech": ["Fortnite Creative", "UEFN", "Verse", "Texturing"],
|
"économie réaliste",
|
||||||
"stats": [{ "value": "30+", "label": "Sessions" }, { "value": "30+", "label": "Bâtiments" }, { "value": "70", "label": "Joueurs max" }]
|
"30+ 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": "30+",
|
||||||
|
"label": "Sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "home",
|
||||||
|
"value": "30+",
|
||||||
|
"label": "Bâtiments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "users",
|
||||||
|
"value": "70",
|
||||||
|
"label": "Joueurs max"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "calendar",
|
||||||
|
"value": "4",
|
||||||
|
"label": "Mois de dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": "assets/images/projects/victorias-vice/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
|
|
@ -31,9 +123,55 @@
|
||||||
"gradient": "linear-gradient(135deg, #3a1f5e, #8b3a62)",
|
"gradient": "linear-gradient(135deg, #3a1f5e, #8b3a62)",
|
||||||
"year": "2024",
|
"year": "2024",
|
||||||
"duration": "",
|
"duration": "",
|
||||||
"features": ["Économie réaliste", "Gestion ticket Whitelist", "Carte d'identités", "Prise de RDV", "système de comptes avec RIB", "Fonctionnalités bonus"],
|
"features": [
|
||||||
"tech": ["Python", "Discord.py", "MySQL", "Pillow"],
|
"Économie réaliste",
|
||||||
"stats": [{ "value": "300+", "label": "Utilisateurs" }, { "value": "100+", "label": "Commandes" }, { "value": "60+", "label": "RIB générés" }]
|
"Gestion ticket Whitelist",
|
||||||
|
"Carte d'identités",
|
||||||
|
"Prise de RDV",
|
||||||
|
"système de comptes avec RIB",
|
||||||
|
"Fonctionnalités bonus"
|
||||||
|
],
|
||||||
|
"tech": [
|
||||||
|
{
|
||||||
|
"name": "Python",
|
||||||
|
"icon": "code"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Discord.py",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MySQL",
|
||||||
|
"icon": "database"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pillow",
|
||||||
|
"icon": "image"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"icon": "users",
|
||||||
|
"value": "300+",
|
||||||
|
"label": "Utilisateurs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "terminal",
|
||||||
|
"value": "100+",
|
||||||
|
"label": "Commandes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "credit-card",
|
||||||
|
"value": "60+",
|
||||||
|
"label": "RIB générés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "activity",
|
||||||
|
"value": "99%",
|
||||||
|
"label": "Uptime"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": "assets/images/projects/vicy/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
|
|
@ -42,10 +180,49 @@
|
||||||
"desc": "Identité visuelle complète incluant logo, bannières, emojis personnalisés et templates pour Omega Kube.",
|
"desc": "Identité visuelle complète incluant logo, bannières, emojis personnalisés et templates pour Omega Kube.",
|
||||||
"gradient": "linear-gradient(135deg, #2d1b5e, #5E2040)",
|
"gradient": "linear-gradient(135deg, #2d1b5e, #5E2040)",
|
||||||
"year": "2024",
|
"year": "2024",
|
||||||
"duration": "1 semaines",
|
"duration": "1 semaine",
|
||||||
"features": ["Logo principal", "charte graphique", "Bannières", "Animations After effect"],
|
"features": [
|
||||||
"tech": ["Photoshop", "Illustrator", "After Effects", "Figma"],
|
"Logo principal",
|
||||||
"stats": [{ "value": "5+", "label": "Éléments" }, { "value": "2h", "label": "After Effects" }, { "value": "3h", "label": "Illustrator" }]
|
"Charte graphique",
|
||||||
|
"Bannières",
|
||||||
|
"Animations After Effects"
|
||||||
|
],
|
||||||
|
"tech": [
|
||||||
|
{
|
||||||
|
"name": "Photoshop",
|
||||||
|
"icon": "edit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Illustrator",
|
||||||
|
"icon": "pen-tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After Effects",
|
||||||
|
"icon": "film"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Figma",
|
||||||
|
"icon": "pen-tool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"icon": "layers",
|
||||||
|
"value": "5+",
|
||||||
|
"label": "Éléments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "clock",
|
||||||
|
"value": "2h",
|
||||||
|
"label": "After Effects"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "pen-tool",
|
||||||
|
"value": "3h",
|
||||||
|
"label": "Illustrator"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": "assets/images/projects/branding-omega-kube/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
|
|
@ -55,8 +232,44 @@
|
||||||
"gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)",
|
"gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)",
|
||||||
"year": "2026",
|
"year": "2026",
|
||||||
"duration": "4 mois",
|
"duration": "4 mois",
|
||||||
"features": ["Intégration de SCP", "Cinématiques", "Modeles 3D", "Site Souterrain", "Terraforming exterieur"],
|
"features": [
|
||||||
"tech": ["Fortnite Creative", "UEFN", "Custom Props"],
|
"Intégration de SCP",
|
||||||
"stats": [{ "value": "6+", "label": "Secteurs" }, { "value": "15+", "label": "SCP" }, { "value": "30+", "label": "Systèmes" }]
|
"Cinématiques",
|
||||||
|
"Modèles 3D",
|
||||||
|
"Site Souterrain",
|
||||||
|
"Terraforming extérieur"
|
||||||
|
],
|
||||||
|
"tech": [
|
||||||
|
{
|
||||||
|
"name": "Fortnite Creative",
|
||||||
|
"icon": "map"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "UEFN",
|
||||||
|
"icon": "layers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Custom Props",
|
||||||
|
"icon": "box"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"icon": "grid",
|
||||||
|
"value": "6+",
|
||||||
|
"label": "Secteurs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "shield",
|
||||||
|
"value": "15+",
|
||||||
|
"label": "SCP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "cpu",
|
||||||
|
"value": "30+",
|
||||||
|
"label": "Systèmes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": "assets/images/projects/site-omega/"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -14,21 +14,68 @@
|
||||||
"title": "Bots Discord RP",
|
"title": "Bots Discord RP",
|
||||||
"description": "Des bots entièrement personnalisés pour animer et gérer vos serveurs Role Play. Nous développons des systèmes complets adaptés à votre univers et à vos mécaniques de jeu.",
|
"description": "Des bots entièrement personnalisés pour animer et gérer vos serveurs Role Play. Nous développons des systèmes complets adaptés à votre univers et à vos mécaniques de jeu.",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
{ "value": "2-4", "label": "Semaines de dev" },
|
{
|
||||||
{ "value": "1 mois","label": "Support inclus" },
|
"value": "2-4",
|
||||||
{ "value": "100%", "label": "Personnalisable" }
|
"label": "Semaines de dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "1 mois",
|
||||||
|
"label": "Support inclus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "100%",
|
||||||
|
"label": "Personnalisable"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"features": [
|
"features": [
|
||||||
{ "icon": "dollar-sign", "title": "Économie virtuelle", "desc": "Monnaie, banque, transactions" },
|
{
|
||||||
{ "icon": "briefcase", "title": "Système de métiers", "desc": "Jobs, skills, progression" },
|
"icon": "dollar-sign",
|
||||||
{ "icon": "shield", "title": "Combat & Stats", "desc": "PvP, PvE, attributs" },
|
"title": "Économie virtuelle",
|
||||||
{ "icon": "box", "title": "Inventaire avancé", "desc": "Items, crafting, échanges" },
|
"desc": "Monnaie, banque, transactions"
|
||||||
{ "icon": "flag", "title": "Factions & Guildes", "desc": "Clans, territoires, guerres" },
|
},
|
||||||
{ "icon": "settings", "title": "Panel Admin", "desc": "Interface web de gestion" }
|
{
|
||||||
|
"icon": "briefcase",
|
||||||
|
"title": "Système de métiers",
|
||||||
|
"desc": "Jobs, skills, progression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "shield",
|
||||||
|
"title": "Combat & Stats",
|
||||||
|
"desc": "PvP, PvE, attributs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "box",
|
||||||
|
"title": "Inventaire avancé",
|
||||||
|
"desc": "Items, crafting, échanges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "flag",
|
||||||
|
"title": "Factions & Guildes",
|
||||||
|
"desc": "Clans, territoires, guerres"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "settings",
|
||||||
|
"title": "Panel Admin",
|
||||||
|
"desc": "Interface web de gestion"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"cta": [
|
"cta": [
|
||||||
{ "label": "Demander un devis", "href": "index.html#contact", "style": "primary", "icon": "message-circle" },
|
{
|
||||||
{ "label": "Voir nos réalisations","href": "realisations.html", "style": "secondary","icon": "eye" }
|
"label": "Demander un devis",
|
||||||
|
"href": "index.html#contact",
|
||||||
|
"style": "primary",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Voir nos réalisations",
|
||||||
|
"href": "realisations.html",
|
||||||
|
"style": "secondary",
|
||||||
|
"icon": "eye"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
{"value": "*","label": "Support inclus 1 mois après livraison, prolongeable sur devis"},
|
||||||
|
{"value": "✓","label": "Développement 100% personnalisé selon vos besoins"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -36,31 +83,86 @@
|
||||||
"nav": {
|
"nav": {
|
||||||
"label": "Maps Fortnite",
|
"label": "Maps Fortnite",
|
||||||
"icon": "map",
|
"icon": "map",
|
||||||
"stamp": { "type": "new", "text": "New" }
|
"stamp": {
|
||||||
|
"type": "new",
|
||||||
|
"text": "New"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"visual": {
|
"visual": {
|
||||||
"accentFrom": "#1a5e8b",
|
"accentFrom": "#1a5e8b",
|
||||||
"accentTo": "#3a8bb5",
|
"accentTo": "#3a8bb5",
|
||||||
"stamp": { "type": "new", "text": "✦ New" }
|
"stamp": {
|
||||||
|
"type": "new",
|
||||||
|
"text": "✦ New"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"title": "Maps Fortnite RP",
|
"title": "Maps Fortnite RP",
|
||||||
"description": "Des environnements immersifs créés sur mesure pour vos sessions Role Play. De la ville moderne au village médiéval, nous donnons vie à tous les univers.",
|
"description": "Des environnements immersifs créés sur mesure pour vos sessions Role Play. De la ville moderne au village médiéval, nous donnons vie à tous les univers.",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
{ "value": "3-6", "label": "Semaines de création" },
|
{
|
||||||
{ "value": "50+", "label": "Joueurs supportés" },
|
"value": "3-6",
|
||||||
{ "value": "UEFN", "label": "Compatible" }
|
"label": "Semaines de création"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "50+",
|
||||||
|
"label": "Joueurs supportés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "UEFN",
|
||||||
|
"label": "Compatible"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"features": [
|
"features": [
|
||||||
{ "icon": "home", "title": "Bâtiments détaillés", "desc": "Intérieurs accessibles et déco" },
|
{
|
||||||
{ "icon": "sun", "title": "Cycle jour/nuit", "desc": "Ambiance dynamique" },
|
"icon": "home",
|
||||||
{ "icon": "zap", "title": "Zones interactives", "desc": "Scripts et triggers" },
|
"title": "Bâtiments détaillés",
|
||||||
{ "icon": "cpu", "title": "Optimisation", "desc": "Performance garantie" },
|
"desc": "Intérieurs accessibles et déco"
|
||||||
{ "icon": "map-pin", "title": "Points d'intérêt", "desc": "Lieux uniques et mémorables" },
|
},
|
||||||
{ "icon": "edit-3", "title": "Modifications", "desc": "Ajustements post-livraison" }
|
{
|
||||||
|
"icon": "sun",
|
||||||
|
"title": "Cycle jour/nuit",
|
||||||
|
"desc": "Ambiance dynamique"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "zap",
|
||||||
|
"title": "Zones interactives",
|
||||||
|
"desc": "Scripts et triggers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "cpu",
|
||||||
|
"title": "Optimisation",
|
||||||
|
"desc": "Performance garantie"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "map-pin",
|
||||||
|
"title": "Points d'intérêt",
|
||||||
|
"desc": "Lieux uniques et mémorables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "edit-3",
|
||||||
|
"title": "Modifications",
|
||||||
|
"desc": "Ajustements post-livraison"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"cta": [
|
"cta": [
|
||||||
{ "label": "Demander un devis", "href": "index.html#contact", "style": "primary", "icon": "message-circle" },
|
{
|
||||||
{ "label": "Voir nos maps", "href": "realisations.html", "style": "secondary", "icon": "eye" }
|
"label": "Demander un devis",
|
||||||
|
"href": "index.html#contact",
|
||||||
|
"style": "primary",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Voir nos maps",
|
||||||
|
"href": "realisations.html",
|
||||||
|
"style": "secondary",
|
||||||
|
"icon": "eye"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
{
|
||||||
|
"value": "*",
|
||||||
|
"label": "Modifications post-livraison incluses pendant 2 semaines"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -78,21 +180,70 @@
|
||||||
"title": "Créations Graphiques",
|
"title": "Créations Graphiques",
|
||||||
"description": "Une identité visuelle forte et cohérente pour vos projets. Du logo aux bannières, nous créons des visuels qui marquent les esprits et attirent votre communauté.",
|
"description": "Une identité visuelle forte et cohérente pour vos projets. Du logo aux bannières, nous créons des visuels qui marquent les esprits et attirent votre communauté.",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
{ "value": "1-2", "label": "Semaines" },
|
{
|
||||||
{ "value": "∞", "label": "Révisions" },
|
"value": "1-2",
|
||||||
{ "value": "HD", "label": "Qualité" }
|
"label": "Semaines"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "∞",
|
||||||
|
"label": "Révisions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "HD",
|
||||||
|
"label": "Qualité"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"features": [
|
"features": [
|
||||||
{ "icon": "hexagon", "title": "Logo & Branding", "desc": "Identité visuelle complète" },
|
{
|
||||||
{ "icon": "layout", "title": "Bannières Discord", "desc": "Server banner, splash, icône" },
|
"icon": "hexagon",
|
||||||
{ "icon": "smile", "title": "Emojis custom", "desc": "Pack emojis personnalisés" },
|
"title": "Logo & Branding",
|
||||||
{ "icon": "grid", "title": "Icônes & Assets", "desc": "Items, skills, badges" },
|
"desc": "Identité visuelle complète"
|
||||||
{ "icon": "play-circle","title": "Animations", "desc": "GIFs et motion design" },
|
},
|
||||||
{ "icon": "book-open", "title": "Guide de style", "desc": "Charte graphique fournie" }
|
{
|
||||||
|
"icon": "layout",
|
||||||
|
"title": "Bannières Discord",
|
||||||
|
"desc": "Server banner, splash, icône"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "smile",
|
||||||
|
"title": "Emojis custom",
|
||||||
|
"desc": "Pack emojis personnalisés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "grid",
|
||||||
|
"title": "Icônes & Assets",
|
||||||
|
"desc": "Items, skills, badges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "play-circle",
|
||||||
|
"title": "Animations",
|
||||||
|
"desc": "GIFs et motion design"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "book-open",
|
||||||
|
"title": "Guide de style",
|
||||||
|
"desc": "Charte graphique fournie"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"cta": [
|
"cta": [
|
||||||
{ "label": "Demander un devis", "href": "index.html#contact", "style": "primary", "icon": "message-circle" },
|
{
|
||||||
{ "label": "Voir nos créations", "href": "realisations.html", "style": "secondary", "icon": "eye" }
|
"label": "Demander un devis",
|
||||||
|
"href": "index.html#contact",
|
||||||
|
"style": "primary",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Voir nos créations",
|
||||||
|
"href": "realisations.html",
|
||||||
|
"style": "secondary",
|
||||||
|
"icon": "eye"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
{
|
||||||
|
"value": "*",
|
||||||
|
"label": "Révisions illimitées sous conditions, voir FAQ"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -100,31 +251,85 @@
|
||||||
"nav": {
|
"nav": {
|
||||||
"label": "Consulting",
|
"label": "Consulting",
|
||||||
"icon": "users",
|
"icon": "users",
|
||||||
"stamp": { "type": "free", "text": "Gratuit" }
|
"stamp": {
|
||||||
|
"type": "free",
|
||||||
|
"text": "Gratuit"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"visual": {
|
"visual": {
|
||||||
"accentFrom": "#3a8b5e",
|
"accentFrom": "#3a8b5e",
|
||||||
"accentTo": "#5ab57a",
|
"accentTo": "#5ab57a",
|
||||||
"stamp": { "type": "free", "text": "✓ Gratuit", "animated": true }
|
"stamp": {
|
||||||
|
"type": "free",
|
||||||
|
"text": "✓ Gratuit",
|
||||||
|
"animated": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"title": "Consulting & Suivie",
|
"title": "Consulting & Suivie",
|
||||||
"titleStamp": { "type": "free", "text": "Gratuit" },
|
"titleStamp": {
|
||||||
|
"type": "free",
|
||||||
|
"text": "Gratuit"
|
||||||
|
},
|
||||||
"description": "Bénéficiez de notre expérience pour structurer, lancer et développer votre projet RP. Conseils stratégiques et accompagnement personnalisé.",
|
"description": "Bénéficiez de notre expérience pour structurer, lancer et développer votre projet RP. Conseils stratégiques et accompagnement personnalisé.",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
{ "value": "50+", "label": "Projets accompagnés" },
|
{
|
||||||
{ "value": "3 ans", "label": "D'expérience" },
|
"value": "50+",
|
||||||
{ "value": "24/7", "label": "Support Discord" }
|
"label": "Projets accompagnés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "3 ans",
|
||||||
|
"label": "D'expérience"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "24/7",
|
||||||
|
"label": "Support Discord"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"features": [
|
"features": [
|
||||||
{ "icon": "search", "title": "Audit serveur", "desc": "Analyse de l'existant" },
|
{
|
||||||
{ "icon": "trending-up", "title": "Stratégie croissance","desc": "Plan de développement" },
|
"icon": "search",
|
||||||
{ "icon": "user-check", "title": "Formation staff", "desc": "Équipe modération" },
|
"title": "Audit serveur",
|
||||||
{ "icon": "heart", "title": "Expérience joueur", "desc": "Optimisation UX" },
|
"desc": "Analyse de l'existant"
|
||||||
{ "icon": "calendar", "title": "Planning events", "desc": "Calendrier d'animations" },
|
},
|
||||||
{ "icon": "dollar-sign", "title": "Monétisation", "desc": "Conseils revenus" }
|
{
|
||||||
|
"icon": "trending-up",
|
||||||
|
"title": "Stratégie croissance",
|
||||||
|
"desc": "Plan de développement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "user-check",
|
||||||
|
"title": "Formation staff",
|
||||||
|
"desc": "Équipe modération"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "heart",
|
||||||
|
"title": "Expérience joueur",
|
||||||
|
"desc": "Optimisation UX"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "calendar",
|
||||||
|
"title": "Planning events",
|
||||||
|
"desc": "Calendrier d'animations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "dollar-sign",
|
||||||
|
"title": "Monétisation",
|
||||||
|
"desc": "Conseils revenus"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"cta": [
|
"cta": [
|
||||||
{ "label": "Prendre rendez-vous", "href": "index.html#contact", "style": "primary", "icon": "message-circle" }
|
{
|
||||||
|
"label": "Prendre rendez-vous",
|
||||||
|
"href": "index.html#contact",
|
||||||
|
"style": "primary",
|
||||||
|
"icon": "message-circle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
{
|
||||||
|
"value": "✓",
|
||||||
|
"label": "Première consultation gratuite et sans engagement"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -132,32 +337,91 @@
|
||||||
"nav": {
|
"nav": {
|
||||||
"label": "Pack Complet",
|
"label": "Pack Complet",
|
||||||
"icon": "package",
|
"icon": "package",
|
||||||
"stamp": { "type": "popular", "text": "Populaire" }
|
"stamp": {
|
||||||
|
"type": "popular",
|
||||||
|
"text": "Populaire"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"visual": {
|
"visual": {
|
||||||
"accentFrom": "#8b3a8b",
|
"accentFrom": "#8b3a8b",
|
||||||
"accentTo": "#b55ab5",
|
"accentTo": "#b55ab5",
|
||||||
"stamp": { "type": "popular", "text": "★ Populaire", "animated": true }
|
"stamp": {
|
||||||
|
"type": "popular",
|
||||||
|
"text": "★ Populaire",
|
||||||
|
"animated": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"title": "Pack Serveur Complet",
|
"title": "Pack Serveur Complet",
|
||||||
"titleStamp": { "type": "promo", "text": "-20%" },
|
"titleStamp": {
|
||||||
|
"type": "promo",
|
||||||
|
"text": "-20%"
|
||||||
|
},
|
||||||
"description": "La solution tout-en-un pour lancer votre projet RP. Bot personnalisé, map Fortnite, identité visuelle et accompagnement : tout est inclus pour un lancement réussi.",
|
"description": "La solution tout-en-un pour lancer votre projet RP. Bot personnalisé, map Fortnite, identité visuelle et accompagnement : tout est inclus pour un lancement réussi.",
|
||||||
"highlights": [
|
"highlights": [
|
||||||
{ "value": "-20%", "label": "VS services séparés" },
|
{
|
||||||
{ "value": "3 mois", "label": "Support inclus" },
|
"value": "-20%",
|
||||||
{ "value": "Clé en main","label": "Prêt à lancer" }
|
"label": "VS services séparés"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "3 mois",
|
||||||
|
"label": "Support inclus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Clé en main",
|
||||||
|
"label": "Prêt à lancer"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"features": [
|
"features": [
|
||||||
{ "icon": "code", "title": "Bot Discord RP", "desc": "Système complet personnalisé" },
|
{
|
||||||
{ "icon": "map", "title": "Map Fortnite", "desc": "Environnement sur mesure" },
|
"icon": "code",
|
||||||
{ "icon": "image", "title": "Pack graphique", "desc": "Logo, bannières, emojis" },
|
"title": "Bot Discord RP",
|
||||||
{ "icon": "server", "title": "Config Discord", "desc": "Serveur prêt à l'emploi" },
|
"desc": "Système complet personnalisé"
|
||||||
{ "icon": "book", "title": "Documentation", "desc": "Guides et tutoriels" },
|
},
|
||||||
{ "icon": "headphones", "title": "Support 3 mois", "desc": "Accompagnement lancement" }
|
{
|
||||||
|
"icon": "map",
|
||||||
|
"title": "Map Fortnite",
|
||||||
|
"desc": "Environnement sur mesure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "image",
|
||||||
|
"title": "Pack graphique",
|
||||||
|
"desc": "Logo, bannières, emojis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "server",
|
||||||
|
"title": "Config Discord",
|
||||||
|
"desc": "Serveur prêt à l'emploi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "book",
|
||||||
|
"title": "Documentation",
|
||||||
|
"desc": "Guides et tutoriels"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "headphones",
|
||||||
|
"title": "Support 3 mois",
|
||||||
|
"desc": "Accompagnement lancement"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"cta": [
|
"cta": [
|
||||||
{ "label": "Demander un devis pack", "href": "index.html#contact", "style": "primary", "icon": "message-circle" },
|
{
|
||||||
{ "label": "Voir nos packs livrés", "href": "realisations.html", "style": "secondary", "icon": "eye" }
|
"label": "Demander un devis pack",
|
||||||
|
"href": "index.html#contact",
|
||||||
|
"style": "primary",
|
||||||
|
"icon": "message-circle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Voir nos packs livrés",
|
||||||
|
"href": "realisations.html",
|
||||||
|
"style": "secondary",
|
||||||
|
"icon": "eye"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
{
|
||||||
|
"value": "*",
|
||||||
|
"label": "Réduction de 20% appliquée automatiquement vs services séparés"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -1,36 +1,238 @@
|
||||||
// Home page specific JS: team carousel + modal
|
// Home page specific JS: team carousel + modal + portfolio preview + partners + konami
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Fetch team data and render
|
|
||||||
fetch('assets/data/team.json')
|
// ── Portfolio preview — même cartes que la page réalisations ──────────────────
|
||||||
|
const homePortfolioGrid = document.getElementById('home-portfolio-grid');
|
||||||
|
if (homePortfolioGrid) {
|
||||||
|
fetch('assets/data/projects.json')
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(members => {
|
.then(projects => {
|
||||||
initTeamSection(members);
|
const first3 = projects.slice(0, 3);
|
||||||
|
const delayClasses = ['', ' reveal-delay-1', ' reveal-delay-2'];
|
||||||
|
const categoryLabels = { bot: 'Bot Discord', map: 'Fortnite Map', graphisme: 'Graphisme' };
|
||||||
|
|
||||||
|
homePortfolioGrid.innerHTML = first3.map((p, i) => {
|
||||||
|
const imgSrc = (p.images || '') + '1.png';
|
||||||
|
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'">
|
||||||
|
</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>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
homePortfolioGrid.querySelectorAll('.reveal').forEach(el => el.classList.add('active'));
|
||||||
|
if (window.feather && feather.replace) try { feather.replace(); } catch(e) {}
|
||||||
|
}, 80);
|
||||||
|
|
||||||
|
// Délégation : attend que openProjectModal soit défini par realisations.js
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => console.error('Failed to load projects for home preview', err));
|
||||||
console.error('Failed to load team data', err);
|
}
|
||||||
|
|
||||||
|
// ── Partners — carousel + modal ─────────────────────────────────────────────
|
||||||
|
const partnersTrack = document.getElementById('partners-track');
|
||||||
|
const partnerModalOverlay = document.getElementById('partner-modal-overlay');
|
||||||
|
|
||||||
|
if (partnersTrack) {
|
||||||
|
fetch('assets/data/partners.json')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(partners => {
|
||||||
|
if (!partners.length) {
|
||||||
|
const section = document.getElementById('partenaires');
|
||||||
|
if (section) section.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Générer les slides (tripler pour garantir boucle sans trou)
|
||||||
|
const copies = [...partners, ...partners, ...partners];
|
||||||
|
partnersTrack.innerHTML = copies.map((p, i) => {
|
||||||
|
const imgSrc = `assets/images/partners/${p.name}.png`;
|
||||||
|
return `
|
||||||
|
<div class="partner-slide" data-partner-idx="${i % partners.length}" role="button" tabindex="0" aria-label="Voir ${p.displayName}">
|
||||||
|
<div class="partner-slide-inner">
|
||||||
|
<img src="${imgSrc}" alt="${p.displayName}" class="partner-logo-img"
|
||||||
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex';">
|
||||||
|
<div class="partner-logo-fallback" style="background:${p.gradient};display:none;">
|
||||||
|
${p.displayName.substring(0,2).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<span class="partner-slide-name">${p.displayName}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// Défilement infini — loopWidth mesuré une seule fois après rendu complet
|
||||||
|
const track = partnersTrack;
|
||||||
|
let isPaused = false;
|
||||||
|
let scrollPos = 0;
|
||||||
|
let loopWidth = 0;
|
||||||
|
let animFrame;
|
||||||
|
const SPEED = 0.6;
|
||||||
|
|
||||||
|
function measureLoop() {
|
||||||
|
// Mesure la largeur exacte d'un bloc (1/3 des slides)
|
||||||
|
// On utilise offsetWidth de chaque slide réel + gap pour éviter les arrondis de scrollWidth
|
||||||
|
const slides = Array.from(track.querySelectorAll('.partner-slide'));
|
||||||
|
const perGroup = partners.length;
|
||||||
|
if (slides.length < perGroup) return 0;
|
||||||
|
// Largeur d'un groupe = somme des (offsetWidth + gap) des premiers slides
|
||||||
|
const style = window.getComputedStyle(track);
|
||||||
|
const gap = parseFloat(style.gap) || 24;
|
||||||
|
let w = 0;
|
||||||
|
for (let i = 0; i < perGroup; i++) {
|
||||||
|
w += slides[i].offsetWidth + gap;
|
||||||
|
}
|
||||||
|
return Math.round(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoScroll() {
|
||||||
|
if (!isPaused) {
|
||||||
|
scrollPos += SPEED;
|
||||||
|
if (loopWidth > 0 && scrollPos >= loopWidth) {
|
||||||
|
scrollPos -= loopWidth;
|
||||||
|
}
|
||||||
|
track.style.transform = `translateX(-${scrollPos}px)`;
|
||||||
|
}
|
||||||
|
animFrame = requestAnimationFrame(autoScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attendre que les slides soient rendues et mesurées
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
loopWidth = measureLoop();
|
||||||
|
animFrame = requestAnimationFrame(autoScroll);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Re-mesurer au resize pour rester précis
|
||||||
|
let resizeTO;
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
clearTimeout(resizeTO);
|
||||||
|
resizeTO = setTimeout(() => { loopWidth = measureLoop(); }, 150);
|
||||||
|
});
|
||||||
|
|
||||||
|
track.parentElement.addEventListener('mouseenter', () => { isPaused = true; });
|
||||||
|
track.parentElement.addEventListener('mouseleave', () => { isPaused = false; });
|
||||||
|
|
||||||
|
// Click → modal
|
||||||
|
track.querySelectorAll('.partner-slide').forEach(slide => {
|
||||||
|
const open = () => {
|
||||||
|
const idx = parseInt(slide.dataset.partnerIdx, 10);
|
||||||
|
openPartnerModal(partners[idx]);
|
||||||
|
};
|
||||||
|
slide.addEventListener('click', open);
|
||||||
|
slide.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); } });
|
||||||
|
});
|
||||||
|
|
||||||
|
function openPartnerModal(p) {
|
||||||
|
isPaused = true;
|
||||||
|
const imgSrc = `assets/images/partners/${p.name}.png`;
|
||||||
|
document.getElementById('partner-modal-logo-img').src = imgSrc;
|
||||||
|
document.getElementById('partner-modal-logo-img').alt = p.displayName;
|
||||||
|
document.getElementById('partner-modal-logo-img').onerror = function() {
|
||||||
|
this.style.display = 'none';
|
||||||
|
document.getElementById('partner-modal-logo-fallback').style.display = 'flex';
|
||||||
|
document.getElementById('partner-modal-logo-fallback').style.background = p.gradient;
|
||||||
|
document.getElementById('partner-modal-logo-fallback').textContent = p.displayName.substring(0,2).toUpperCase();
|
||||||
|
};
|
||||||
|
document.getElementById('partner-modal-logo-fallback').style.display = 'none';
|
||||||
|
document.getElementById('partner-modal-name').textContent = p.displayName;
|
||||||
|
document.getElementById('partner-modal-desc').textContent = p.longDesc;
|
||||||
|
document.getElementById('partner-modal-since').textContent = p.since;
|
||||||
|
document.getElementById('partner-modal-services').innerHTML = (p.services || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
|
||||||
|
document.getElementById('partner-modal-stats').innerHTML = (p.stats || []).map(s => `
|
||||||
|
<div class="team-modal-stat">
|
||||||
|
<div class="team-modal-stat-value">${s.value}</div>
|
||||||
|
<div class="team-modal-stat-label">${s.label}</div>
|
||||||
|
</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>`;
|
||||||
|
}
|
||||||
|
if (window.feather) feather.replace();
|
||||||
|
partnerModalOverlay.classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partnerModalOverlay) {
|
||||||
|
const closeBtn = document.getElementById('partner-modal-close');
|
||||||
|
closeBtn && closeBtn.addEventListener('click', closePartnerModal);
|
||||||
|
partnerModalOverlay.addEventListener('click', e => { if (e.target === partnerModalOverlay) closePartnerModal(); });
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
if (e.key === 'Escape' && partnerModalOverlay.classList.contains('active')) closePartnerModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePartnerModal() {
|
||||||
|
partnerModalOverlay.classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
isPaused = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
const section = document.getElementById('partenaires');
|
||||||
|
if (section) section.style.display = 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Team section ─────────────────────────────────────────────────────────────
|
||||||
|
fetch('assets/data/team.json')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(members => { initTeamSection(members); })
|
||||||
|
.catch(err => { console.error('Failed to load team data', err); });
|
||||||
|
|
||||||
|
// Konami + easter egg → définis au niveau top-level du script (voir bas de fichier)
|
||||||
|
|
||||||
|
// ── initTeamSection ──────────────────────────────────────────────────────────
|
||||||
function initTeamSection(members) {
|
function initTeamSection(members) {
|
||||||
const carousel = document.getElementById('team-carousel');
|
const carousel = document.getElementById('team-carousel');
|
||||||
const dotsContainer = document.getElementById('carousel-dots');
|
const dotsContainer = document.getElementById('carousel-dots');
|
||||||
const prevBtn = document.getElementById('carousel-prev');
|
const prevBtn = document.getElementById('carousel-prev');
|
||||||
const nextBtn = document.getElementById('carousel-next');
|
const nextBtn = document.getElementById('carousel-next');
|
||||||
|
|
||||||
// Render cards
|
carousel.innerHTML = members.map((m, i) => {
|
||||||
carousel.innerHTML = members.map((m, i) => `
|
const imgSrc = `assets/images/team/${m.id}.png`;
|
||||||
<div class="team-card" data-member="${i}">
|
return `
|
||||||
|
<div class="team-card" data-member="${i}" role="button" tabindex="0" aria-label="Voir le profil de ${m.name}">
|
||||||
<div class="team-avatar">
|
<div class="team-avatar">
|
||||||
<div class="team-avatar-inner">${m.avatar}</div>
|
<div class="team-avatar-inner" id="team-avatar-inner-${i}">
|
||||||
|
<img src="${imgSrc}" alt="${m.name}" class="team-avatar-photo"
|
||||||
|
onerror="this.style.display='none';document.getElementById('team-avatar-fallback-${i}').style.display='flex';">
|
||||||
|
<span class="team-avatar-fallback" id="team-avatar-fallback-${i}" style="display:none;">${m.avatar}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h4>${m.name}</h4>
|
<h4>${m.name}</h4>
|
||||||
<p class="team-role">${m.role}</p>
|
<p class="team-role">${m.role}</p>
|
||||||
<p class="team-desc">${(m.bio || '').split('.').shift()}.</p>
|
<p class="team-desc">${(m.bio || '').split('.').shift()}.</p>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
// Carousel logic
|
let cards = Array.from(carousel.querySelectorAll('.team-card'));
|
||||||
const cardsContainer = carousel; // the track
|
|
||||||
let cards = carousel.querySelectorAll('.team-card');
|
|
||||||
let currentIndex = 0;
|
let currentIndex = 0;
|
||||||
let cardsPerView = 3;
|
let cardsPerView = 3;
|
||||||
let autoPlayInterval;
|
let autoPlayInterval;
|
||||||
|
|
@ -41,10 +243,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
else cardsPerView = 3;
|
else cardsPerView = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTotalDots() { return Math.max(1, cards.length - cardsPerView + 1); }
|
||||||
|
|
||||||
function createDots() {
|
function createDots() {
|
||||||
dotsContainer.innerHTML = '';
|
dotsContainer.innerHTML = '';
|
||||||
const totalDots = Math.max(1, Math.ceil(cards.length / cardsPerView));
|
for (let i = 0; i < getTotalDots(); i++) {
|
||||||
for (let i = 0; i < totalDots; i++) {
|
|
||||||
const dot = document.createElement('div');
|
const dot = document.createElement('div');
|
||||||
dot.classList.add('carousel-dot');
|
dot.classList.add('carousel-dot');
|
||||||
if (i === 0) dot.classList.add('active');
|
if (i === 0) dot.classList.add('active');
|
||||||
|
|
@ -54,65 +257,49 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCarousel() {
|
function updateCarousel() {
|
||||||
cards = carousel.querySelectorAll('.team-card');
|
cards = Array.from(carousel.querySelectorAll('.team-card'));
|
||||||
if (cards.length === 0) return;
|
if (!cards.length) return;
|
||||||
const cardStyle = window.getComputedStyle(cards[0]);
|
const gap = 32;
|
||||||
const gap = 32; // matches previous implementation
|
|
||||||
const cardWidth = cards[0].offsetWidth + gap;
|
const cardWidth = cards[0].offsetWidth + gap;
|
||||||
carousel.style.transform = `translateX(-${currentIndex * cardWidth}px)`;
|
carousel.style.transform = `translateX(-${currentIndex * cardWidth}px)`;
|
||||||
|
Array.from(dotsContainer.querySelectorAll('.carousel-dot')).forEach((d, i) => d.classList.toggle('active', i === currentIndex));
|
||||||
const dots = dotsContainer.querySelectorAll('.carousel-dot');
|
|
||||||
const activeDotIndex = Math.floor(currentIndex / cardsPerView);
|
|
||||||
dots.forEach((dot, i) => dot.classList.toggle('active', i === activeDotIndex));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToSlide(dotIndex) {
|
function goToSlide(index) {
|
||||||
currentIndex = dotIndex * cardsPerView;
|
currentIndex = Math.min(index, Math.max(0, cards.length - cardsPerView));
|
||||||
if (currentIndex > cards.length - cardsPerView) currentIndex = Math.max(0, cards.length - cardsPerView);
|
updateCarousel(); resetAutoPlay();
|
||||||
updateCarousel();
|
|
||||||
resetAutoPlay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextSlide() {
|
function nextSlide() {
|
||||||
currentIndex++;
|
const max = Math.max(0, cards.length - cardsPerView);
|
||||||
if (currentIndex > cards.length - cardsPerView) currentIndex = 0;
|
currentIndex = currentIndex >= max ? 0 : currentIndex + 1;
|
||||||
updateCarousel();
|
updateCarousel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevSlide() {
|
function prevSlide() {
|
||||||
currentIndex--;
|
const max = Math.max(0, cards.length - cardsPerView);
|
||||||
if (currentIndex < 0) currentIndex = Math.max(0, cards.length - cardsPerView);
|
currentIndex = currentIndex <= 0 ? max : currentIndex - 1;
|
||||||
updateCarousel();
|
updateCarousel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function startAutoPlay() {
|
function startAutoPlay() { clearInterval(autoPlayInterval); autoPlayInterval = setInterval(nextSlide, 4000); }
|
||||||
clearInterval(autoPlayInterval);
|
function resetAutoPlay() { clearInterval(autoPlayInterval); startAutoPlay(); }
|
||||||
autoPlayInterval = setInterval(nextSlide, 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetAutoPlay() {
|
|
||||||
clearInterval(autoPlayInterval);
|
|
||||||
startAutoPlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevBtn && prevBtn.addEventListener('click', () => { prevSlide(); resetAutoPlay(); });
|
prevBtn && prevBtn.addEventListener('click', () => { prevSlide(); resetAutoPlay(); });
|
||||||
nextBtn && nextBtn.addEventListener('click', () => { nextSlide(); resetAutoPlay(); });
|
nextBtn && nextBtn.addEventListener('click', () => { nextSlide(); resetAutoPlay(); });
|
||||||
|
|
||||||
carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval));
|
carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval));
|
||||||
carousel.addEventListener('mouseleave', startAutoPlay);
|
carousel.addEventListener('mouseleave', startAutoPlay);
|
||||||
|
|
||||||
updateCardsPerView();
|
let resizeTimer;
|
||||||
createDots();
|
const ro = new ResizeObserver(() => {
|
||||||
startAutoPlay();
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(() => { updateCardsPerView(); currentIndex = 0; createDots(); updateCarousel(); }, 100);
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
updateCardsPerView();
|
|
||||||
createDots();
|
|
||||||
currentIndex = 0;
|
|
||||||
updateCarousel();
|
|
||||||
});
|
});
|
||||||
|
ro.observe(carousel.parentElement || document.body);
|
||||||
|
|
||||||
// Modal logic
|
updateCardsPerView(); createDots(); updateCarousel(); startAutoPlay();
|
||||||
|
|
||||||
|
// Modal
|
||||||
const modalOverlay = document.getElementById('team-modal-overlay');
|
const modalOverlay = document.getElementById('team-modal-overlay');
|
||||||
const modalClose = document.getElementById('team-modal-close');
|
const modalClose = document.getElementById('team-modal-close');
|
||||||
const modalAvatar = document.getElementById('modal-avatar');
|
const modalAvatar = document.getElementById('modal-avatar');
|
||||||
|
|
@ -124,29 +311,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
const modalSocial = document.getElementById('modal-social');
|
const modalSocial = document.getElementById('modal-social');
|
||||||
|
|
||||||
function openMemberModal(index) {
|
function openMemberModal(index) {
|
||||||
const member = members[index];
|
const m = members[index];
|
||||||
if (!member) return;
|
if (!m) return;
|
||||||
modalAvatar.textContent = member.avatar || member.name.charAt(0);
|
// Avatar avec image si dispo
|
||||||
modalName.textContent = member.name;
|
const imgSrc = `assets/images/team/${m.id}.png`;
|
||||||
modalRole.textContent = member.role;
|
modalAvatar.innerHTML = `
|
||||||
modalBio.textContent = member.bio;
|
<img src="${imgSrc}" alt="${m.name}" class="team-modal-avatar-photo"
|
||||||
|
onerror="this.style.display='none';this.nextElementSibling.style.display='flex';">
|
||||||
modalStats.innerHTML = (member.stats || []).map(stat => `
|
<span class="team-modal-avatar-fallback">${m.avatar || m.name.charAt(0)}</span>
|
||||||
|
`;
|
||||||
|
modalName.textContent = m.name;
|
||||||
|
modalRole.textContent = m.role;
|
||||||
|
modalBio.textContent = m.bio;
|
||||||
|
modalStats.innerHTML = (m.stats || []).map(s => `
|
||||||
<div class="team-modal-stat">
|
<div class="team-modal-stat">
|
||||||
<div class="team-modal-stat-value">${stat.value}</div>
|
<div class="team-modal-stat-value">${s.value}</div>
|
||||||
<div class="team-modal-stat-label">${stat.label}</div>
|
<div class="team-modal-stat-label">${s.label}</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
modalSkills.innerHTML = (m.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
|
||||||
modalSkills.innerHTML = (member.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
|
|
||||||
|
|
||||||
modalSocial.innerHTML = `
|
modalSocial.innerHTML = `
|
||||||
<a href="#" aria-label="Discord"><i data-feather="message-circle"></i></a>
|
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" aria-label="Discord">
|
||||||
<a href="#" aria-label="Twitter"><i data-feather="twitter"></i></a>
|
<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>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (window.feather && feather.replace) feather.replace();
|
|
||||||
|
|
||||||
modalOverlay.classList.add('active');
|
modalOverlay.classList.add('active');
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
}
|
}
|
||||||
|
|
@ -156,16 +347,68 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach click listeners to generated cards
|
|
||||||
carousel.querySelectorAll('.team-card').forEach(card => {
|
carousel.querySelectorAll('.team-card').forEach(card => {
|
||||||
card.addEventListener('click', () => {
|
const openCard = () => { const idx = parseInt(card.dataset.member, 10); openMemberModal(idx); };
|
||||||
const idx = parseInt(card.dataset.member, 10);
|
card.addEventListener('click', openCard);
|
||||||
openMemberModal(idx);
|
card.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openCard(); } });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modalClose && modalClose.addEventListener('click', closeMemberModal);
|
modalClose && modalClose.addEventListener('click', closeMemberModal);
|
||||||
modalOverlay && modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeMemberModal(); });
|
modalOverlay && modalOverlay.addEventListener('click', e => { if (e.target === modalOverlay) closeMemberModal(); });
|
||||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeMemberModal(); });
|
document.addEventListener('keydown', e => { if (e.key === 'Escape' && modalOverlay && modalOverlay.classList.contains('active')) closeMemberModal(); });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// EASTER EGG — top-level (hors DOMContentLoaded pour garantir l'attachement)
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var KONAMI = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
|
||||||
|
var progress = 0;
|
||||||
|
|
||||||
|
// PC : Konami Code — capture:true pour passer avant tout autre listener
|
||||||
|
window.addEventListener('keydown', function(e) {
|
||||||
|
var key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
|
||||||
|
if (key === KONAMI[progress]) {
|
||||||
|
progress++;
|
||||||
|
if (progress === KONAMI.length) {
|
||||||
|
progress = 0;
|
||||||
|
triggerEasterEgg();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
progress = (key === KONAMI[0]) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}, true); // capture phase — reçu avant tout stopPropagation
|
||||||
|
|
||||||
|
// Mobile : 10 taps sur le logo navbar OU logo footer
|
||||||
|
var tapCount = 0;
|
||||||
|
var tapTimer = null;
|
||||||
|
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (!e.target.closest('.logo') && !e.target.closest('.logo-mini') && !e.target.closest('.omega-symbol')) return;
|
||||||
|
if (!('ontouchstart' in window) && navigator.maxTouchPoints === 0) return;
|
||||||
|
tapCount++;
|
||||||
|
clearTimeout(tapTimer);
|
||||||
|
tapTimer = setTimeout(function() { tapCount = 0; }, 600);
|
||||||
|
if (tapCount >= 10) { tapCount = 0; triggerEasterEgg(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
function triggerEasterEgg() {
|
||||||
|
if (document.getElementById('easter-egg-overlay')) return;
|
||||||
|
var egg = document.createElement('div');
|
||||||
|
egg.id = 'easter-egg-overlay';
|
||||||
|
egg.setAttribute('role', 'dialog');
|
||||||
|
egg.setAttribute('aria-modal', 'true');
|
||||||
|
egg.innerHTML = '<div class="easter-egg-box"><div class="easter-egg-icon">🎮</div><h2>GG\u00a0! Tu as trouvé l\u2019easter egg\u00a0!</h2><p>La vraie question c\u2019est\u00a0: combien de temps tu as mis\u00a0?</p><button id="easter-egg-close" class="btn btn-primary">Fermer</button></div>';
|
||||||
|
document.body.appendChild(egg);
|
||||||
|
var closeBtn = document.getElementById('easter-egg-close');
|
||||||
|
closeBtn && closeBtn.focus();
|
||||||
|
var remove = function() { egg.remove(); };
|
||||||
|
closeBtn && closeBtn.addEventListener('click', remove);
|
||||||
|
egg.addEventListener('click', function(e) { if (e.target === egg) remove(); });
|
||||||
|
window.addEventListener('keydown', function esc(e) {
|
||||||
|
if (e.key === 'Escape') { remove(); window.removeEventListener('keydown', esc); }
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,15 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
// wait for includes to be loaded so subsequent initialization can bind to elements inside them
|
// wait for includes to be loaded so subsequent initialization can bind to elements inside them
|
||||||
await loadIncludes();
|
await loadIncludes();
|
||||||
|
|
||||||
|
// Déclenche les reveals maintenant que tous les includes sont dans le DOM
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelectorAll('.reveal:not(.active)').forEach(el => {
|
||||||
|
if (el.getBoundingClientRect().top < window.innerHeight - 50) {
|
||||||
|
el.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
|
|
||||||
function getPreferredTheme() {
|
function getPreferredTheme() {
|
||||||
|
|
@ -367,27 +376,27 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
window.addEventListener('hashchange', wrappedSetActive);
|
window.addEventListener('hashchange', wrappedSetActive);
|
||||||
window.addEventListener('popstate', wrappedSetActive);
|
window.addEventListener('popstate', wrappedSetActive);
|
||||||
|
|
||||||
// Reveal on scroll
|
// Reveal on scroll — re-query après chargement des includes
|
||||||
const revealElements = document.querySelectorAll('.reveal');
|
|
||||||
function revealOnScroll() {
|
function revealOnScroll() {
|
||||||
if (!revealElements.length) return;
|
const els = document.querySelectorAll('.reveal:not(.active)');
|
||||||
revealElements.forEach(el => {
|
els.forEach(el => {
|
||||||
const elementTop = el.getBoundingClientRect().top;
|
if (el.getBoundingClientRect().top < window.innerHeight - 50) {
|
||||||
if (elementTop < window.innerHeight - 100) {
|
|
||||||
el.classList.add('active');
|
el.classList.add('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
window.addEventListener('scroll', revealOnScroll);
|
// Déclenche immédiatement après includes + au scroll + au load
|
||||||
|
revealOnScroll();
|
||||||
|
window.addEventListener('scroll', revealOnScroll, { passive: true });
|
||||||
window.addEventListener('load', revealOnScroll);
|
window.addEventListener('load', revealOnScroll);
|
||||||
|
|
||||||
// Contact form (if exists)
|
// Contact form (if exists) c'est en double
|
||||||
const contactForm = document.getElementById('contact-form');
|
// const contactForm = document.getElementById('contact-form');
|
||||||
if (contactForm) {
|
// if (contactForm) {
|
||||||
contactForm.addEventListener('submit', function (e) {
|
// contactForm.addEventListener('submit', function (e) {
|
||||||
e.preventDefault();
|
// e.preventDefault();
|
||||||
alert('Merci pour votre message ! Nous vous répondrons très bientôt.');
|
// alert('Merci pour votre message ! Nous vous répondrons très bientôt.');
|
||||||
this.reset();
|
// this.reset();
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
// Page script for /pages/realisations.html
|
// Page script for /pages/realisations.html et index.html
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const dataUrl = '../assets/data/projects.json';
|
// Fonctionne depuis /pages/ et depuis / (index.html)
|
||||||
|
const isRoot = !window.location.pathname.includes('/pages/');
|
||||||
|
const dataUrl = isRoot ? 'assets/data/projects.json' : '../assets/data/projects.json';
|
||||||
let allProjects = [];
|
let allProjects = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -133,35 +135,80 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
portfolioGrid.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
portfolioGrid.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Modal tab switching ──────────────────────────────────────────────────────
|
||||||
|
const modalTabs = document.querySelectorAll('.project-modal-tab');
|
||||||
|
modalTabs.forEach(tab => {
|
||||||
|
tab.addEventListener('click', () => {
|
||||||
|
const tabId = tab.dataset.tab;
|
||||||
|
modalTabs.forEach(t => t.classList.remove('active'));
|
||||||
|
tab.classList.add('active');
|
||||||
|
document.querySelectorAll('.project-modal-tab-content').forEach(c => {
|
||||||
|
c.classList.toggle('active', c.id === 'tab-' + tabId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetModalTabs() {
|
||||||
|
modalTabs.forEach((t, i) => t.classList.toggle('active', i === 0));
|
||||||
|
document.querySelectorAll('.project-modal-tab-content').forEach((c, i) => c.classList.toggle('active', i === 0));
|
||||||
|
}
|
||||||
|
|
||||||
function openProjectModal(projectId) {
|
function openProjectModal(projectId) {
|
||||||
const project = allProjects.find(p => p.id === Number(projectId));
|
const project = allProjects.find(p => p.id === Number(projectId));
|
||||||
if (!project) return;
|
if (!project) return;
|
||||||
|
|
||||||
|
resetModalTabs();
|
||||||
|
|
||||||
modalCategory.textContent = getCategoryLabel(project.category);
|
modalCategory.textContent = getCategoryLabel(project.category);
|
||||||
modalTitle.textContent = project.title;
|
modalTitle.textContent = project.title;
|
||||||
modalDesc.textContent = project.desc;
|
modalDesc.textContent = project.desc;
|
||||||
|
|
||||||
modalMeta.innerHTML = `<span><i data-feather="calendar"></i> ${project.year}</span> <span><i data-feather="clock"></i> ${project.duration}</span>`;
|
modalMeta.innerHTML = `<span><i data-feather="calendar"></i> ${project.year}</span>${project.duration ? ` <span><i data-feather="clock"></i> ${project.duration}</span>` : ''}`;
|
||||||
|
|
||||||
modalFeatures.innerHTML = project.features.map(f => `<div class="project-feature-item">${f}</div>`).join('');
|
modalFeatures.innerHTML = project.features.map(f => `<div class="project-feature-item">${f}</div>`).join('');
|
||||||
modalTech.innerHTML = project.tech.map(t => `<div class="project-tech-item">${t}</div>`).join('');
|
|
||||||
|
// Tech avec icônes (support ancien format string et nouveau format {name, icon})
|
||||||
|
modalTech.innerHTML = project.tech.map(t => {
|
||||||
|
const name = typeof t === 'object' ? t.name : t;
|
||||||
|
const icon = typeof t === 'object' ? (t.icon || 'code') : 'code';
|
||||||
|
return `<div class="project-tech-item"><i data-feather="${icon}"></i>${name}</div>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// Stats en grille 2×2 avec icônes
|
||||||
modalStats.innerHTML = project.stats.map(s => `
|
modalStats.innerHTML = project.stats.map(s => `
|
||||||
<div class="project-stat-card">
|
<div class="project-stat-card">
|
||||||
|
<div class="project-stat-card-icon"><i data-feather="${s.icon || 'star'}"></i></div>
|
||||||
<div class="project-stat-card-value">${s.value}</div>
|
<div class="project-stat-card-value">${s.value}</div>
|
||||||
<div class="project-stat-card-label">${s.label}</div>
|
<div class="project-stat-card-label">${s.label}</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// gallery slides (use gradient as placeholder slides like original)
|
// Gallery : charger les images du dossier via convention de nommage
|
||||||
gallerySlides.innerHTML = `
|
// images/projects/{slug}/1.png, 2.png... avec fallback gradient
|
||||||
<div class="project-gallery-slide" style="background: ${project.gradient};"></div>
|
const imgPath = project.images || '';
|
||||||
<div class="project-gallery-slide" style="background: ${project.gradient}; filter: hue-rotate(30deg);"></div>
|
const imgExtensions = ['png', 'jpg', 'jpeg', 'webp'];
|
||||||
<div class="project-gallery-slide" style="background: ${project.gradient}; filter: hue-rotate(-30deg);"></div>
|
|
||||||
`;
|
// 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;
|
||||||
|
|
||||||
// reset gallery state
|
// reset gallery state
|
||||||
galleryIndex = 0;
|
galleryIndex = 0;
|
||||||
updateGallery();
|
updateGallery();
|
||||||
|
updateGalleryDots();
|
||||||
|
|
||||||
if (modalOverlay) modalOverlay.classList.add('active');
|
if (modalOverlay) modalOverlay.classList.add('active');
|
||||||
|
|
||||||
|
|
@ -176,16 +223,60 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
|
||||||
// Gallery helpers
|
// Gallery helpers
|
||||||
let galleryIndex = 0;
|
let galleryIndex = 0;
|
||||||
|
function getVisibleSlides() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
function updateGallery() {
|
function updateGallery() {
|
||||||
const slides = gallerySlides ? Array.from(gallerySlides.children) : [];
|
const visible = getVisibleSlides();
|
||||||
slides.forEach((s, i) => {
|
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}%)`;
|
s.style.transform = `translateX(${(i - galleryIndex) * 100}%)`;
|
||||||
s.classList.toggle('active', i === galleryIndex);
|
s.classList.toggle('active', i === galleryIndex);
|
||||||
});
|
});
|
||||||
|
// Clamp index
|
||||||
|
if (galleryIndex >= visible.length) galleryIndex = Math.max(0, visible.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function prevSlide() { galleryIndex = Math.max(0, galleryIndex - 1); updateGallery(); }
|
function updateGalleryDots() {
|
||||||
function nextSlide() { const slides = gallerySlides ? gallerySlides.children.length : 0; galleryIndex = Math.min(slides - 1, galleryIndex + 1); updateGallery(); }
|
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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prevSlide() {
|
||||||
|
const visible = getVisibleSlides();
|
||||||
|
galleryIndex = galleryIndex <= 0 ? visible.length - 1 : galleryIndex - 1;
|
||||||
|
updateGallery();
|
||||||
|
}
|
||||||
|
function nextSlide() {
|
||||||
|
const visible = getVisibleSlides();
|
||||||
|
galleryIndex = galleryIndex >= visible.length - 1 ? 0 : galleryIndex + 1;
|
||||||
|
updateGallery();
|
||||||
|
}
|
||||||
|
|
||||||
// Event bindings
|
// Event bindings
|
||||||
filterTabs.forEach(tab => tab.addEventListener('click', (e) => {
|
filterTabs.forEach(tab => tab.addEventListener('click', (e) => {
|
||||||
|
|
@ -222,6 +313,60 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
window.openProjectModal = openProjectModal;
|
window.openProjectModal = openProjectModal;
|
||||||
window.goToPage = goToPage;
|
window.goToPage = goToPage;
|
||||||
|
|
||||||
|
// ── Projets phares (bento grid) — 3 premiers projets du JSON ───────────────
|
||||||
|
const featuredBento = document.getElementById('featured-bento-grid');
|
||||||
|
if (featuredBento) {
|
||||||
|
const featured = allProjects.slice(0, 3);
|
||||||
|
const categoryLabels = { bot: 'Bot Discord', map: 'Fortnite Map', graphisme: 'Graphisme' };
|
||||||
|
|
||||||
|
featuredBento.innerHTML = featured.map((p, i) => {
|
||||||
|
const isReverse = i % 2 === 1 ? ' reverse' : '';
|
||||||
|
const statsHtml = (p.stats || []).slice(0, 4).map((s) => `
|
||||||
|
<div class="bento-stat">
|
||||||
|
<div class="bento-stat-icon"><i data-feather="${s.icon || 'star'}"></i></div>
|
||||||
|
<div class="bento-stat-value">${s.value}</div>
|
||||||
|
<div class="bento-stat-label">${s.label}</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
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>' : '';
|
||||||
|
|
||||||
|
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};">
|
||||||
|
<div class="bento-image-content">
|
||||||
|
<span class="portfolio-tag">${categoryLabels[p.category] || p.category}</span>
|
||||||
|
<h3>${p.title}</h3>
|
||||||
|
<p>${p.desc.substring(0, 100)}${p.desc.length > 100 ? '…' : ''}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bento-stats">${statsHtml}</div>
|
||||||
|
<div class="bento-info">
|
||||||
|
<h4>Description</h4>
|
||||||
|
<p>${p.desc}</p>
|
||||||
|
<div class="bento-features">${featuresHtml}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${divider}
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// Replace feather icons
|
||||||
|
if (window.feather && feather.replace) { try { feather.replace(); } catch(e) {} }
|
||||||
|
|
||||||
|
// Click to open modal
|
||||||
|
featuredBento.querySelectorAll('.bento-project').forEach(el => {
|
||||||
|
const open = () => openProjectModal(el.getAttribute('data-project-id'));
|
||||||
|
el.addEventListener('click', open);
|
||||||
|
el.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); } });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reveal animation
|
||||||
|
setTimeout(() => {
|
||||||
|
featuredBento.querySelectorAll('.reveal').forEach(el => el.classList.add('active'));
|
||||||
|
}, 80);
|
||||||
|
}
|
||||||
|
|
||||||
// Initial render
|
// Initial render
|
||||||
renderProjects();
|
renderProjects();
|
||||||
renderPagination();
|
renderPagination();
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,11 @@
|
||||||
</div>`).join('');
|
</div>`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderNote(note) {
|
||||||
|
if (!note || !note.length) return '';
|
||||||
|
return `<p class="service-note">${note.map(n => `<span class="service-note-mark">${n.value}</span> ${n.label}<br>`).join(' ')}</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderCtas(ctas) {
|
function renderCtas(ctas) {
|
||||||
return ctas.map(c => `
|
return ctas.map(c => `
|
||||||
<a href="${c.href}" class="btn btn-${c.style}">
|
<a href="${c.href}" class="btn btn-${c.style}">
|
||||||
|
|
@ -97,6 +102,9 @@
|
||||||
<div class="service-tab-features">
|
<div class="service-tab-features">
|
||||||
${renderFeatures(service.features, accentFrom, accentTo)}
|
${renderFeatures(service.features, accentFrom, accentTo)}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="service-tab-note">
|
||||||
|
${renderNote(service.note)}
|
||||||
|
</div>
|
||||||
<div class="service-tab-cta">
|
<div class="service-tab-cta">
|
||||||
${renderCtas(service.cta)}
|
${renderCtas(service.cta)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
133
components/contact.html
Normal file
133
components/contact.html
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
<!-- components/contact.html — formulaire de contact réutilisable -->
|
||||||
|
<section class="contact" id="contact">
|
||||||
|
<div class="section-header reveal">
|
||||||
|
<p class="section-tag">Contact</p>
|
||||||
|
<h2 class="section-title">Parlons de votre projet</h2>
|
||||||
|
<p class="section-desc">Un projet RP en tête ? Contactez-nous !</p>
|
||||||
|
</div>
|
||||||
|
<div class="contact-wrapper">
|
||||||
|
<div class="contact-info reveal">
|
||||||
|
<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">
|
||||||
|
<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">
|
||||||
|
<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>
|
||||||
|
<span>Discord : OmégaKube</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="contact-form reveal reveal-delay-1" id="contact-form" novalidate>
|
||||||
|
<!-- Honeypot anti-bot -->
|
||||||
|
<div style="display:none;" aria-hidden="true">
|
||||||
|
<input type="text" name="website" tabindex="-1" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Nom</label>
|
||||||
|
<input type="text" id="name" name="name" placeholder="Votre nom" required minlength="2" maxlength="100" autocomplete="name">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" id="email" name="email" placeholder="votre@email.com" required maxlength="254" autocomplete="email">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="subject">Objet</label>
|
||||||
|
<input type="text" id="subject" name="subject" placeholder="Ex : Création d'un bot RP" maxlength="150" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="message">Message</label>
|
||||||
|
<textarea id="message" name="message" placeholder="Décrivez votre projet..." required minlength="10" maxlength="5000"></textarea>
|
||||||
|
</div>
|
||||||
|
<div id="form-feedback" role="alert" aria-live="polite" style="display:none;"></div>
|
||||||
|
<button type="submit" class="btn btn-primary form-submit" id="form-submit-btn">
|
||||||
|
<i data-feather="send"></i>
|
||||||
|
<span>Envoyer le message</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
// Attendre que le composant soit injecté dans le DOM
|
||||||
|
function initForm() {
|
||||||
|
const form = document.getElementById('contact-form');
|
||||||
|
const feedback = document.getElementById('form-feedback');
|
||||||
|
const submitBtn = document.getElementById('form-submit-btn');
|
||||||
|
if (!form || form.dataset.initialized) return;
|
||||||
|
form.dataset.initialized = 'true';
|
||||||
|
|
||||||
|
function showFeedback(message, type) {
|
||||||
|
feedback.style.cssText = [
|
||||||
|
'display:block', 'padding:1rem 1.25rem', 'border-radius:4px',
|
||||||
|
'font-size:0.95rem', 'margin-bottom:1rem', 'border:1px solid',
|
||||||
|
type === 'success'
|
||||||
|
? 'background:rgba(46,204,113,0.12);border-color:rgba(46,204,113,0.4);color:#2ecc71;'
|
||||||
|
: 'background:rgba(231,76,60,0.12);border-color:rgba(231,76,60,0.4);color:#e74c3c;'
|
||||||
|
].join(';');
|
||||||
|
feedback.textContent = message;
|
||||||
|
feedback.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoading(loading) {
|
||||||
|
const span = submitBtn.querySelector('span');
|
||||||
|
submitBtn.disabled = loading;
|
||||||
|
submitBtn.style.opacity = loading ? '0.7' : '1';
|
||||||
|
submitBtn.style.cursor = loading ? 'not-allowed' : '';
|
||||||
|
if (span) span.textContent = loading ? 'Envoi en cours…' : 'Envoyer le message';
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', async function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
feedback.style.display = 'none';
|
||||||
|
|
||||||
|
const name = form.elements['name'].value.trim();
|
||||||
|
const email = form.elements['email'].value.trim();
|
||||||
|
const subject = form.elements['subject'].value.trim();
|
||||||
|
const message = form.elements['message'].value.trim();
|
||||||
|
|
||||||
|
if (name.length < 2) { showFeedback('Le nom doit contenir au moins 2 caractères.', 'error'); form.elements['name'].focus(); return; }
|
||||||
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { showFeedback('Veuillez entrer une adresse email valide.', 'error'); form.elements['email'].focus(); return; }
|
||||||
|
if (message.length < 10) { showFeedback('Le message doit contenir au moins 10 caractères.', 'error'); form.elements['message'].focus(); return; }
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// Chemin auto-détecté selon la profondeur de la page
|
||||||
|
const depth = window.location.pathname.split('/').filter(Boolean).length;
|
||||||
|
const prefix = depth > 1 ? '../'.repeat(depth - 1) : '';
|
||||||
|
const phpPath = prefix + 'assets/php/send_mail.php';
|
||||||
|
|
||||||
|
const res = await fetch(phpPath, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
name, email,
|
||||||
|
subject: subject || 'Contact depuis le site',
|
||||||
|
message,
|
||||||
|
website: form.elements['website'] ? form.elements['website'].value : ''
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.success) { showFeedback(data.message, 'success'); form.reset(); }
|
||||||
|
else { showFeedback(data.message || 'Une erreur est survenue.', 'error'); }
|
||||||
|
} catch (err) {
|
||||||
|
showFeedback('Impossible de contacter le serveur. Contactez-nous directement sur Discord.', 'error');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exécute immédiatement si DOM prêt, sinon à DOMContentLoaded
|
||||||
|
if (document.readyState !== 'loading') { initForm(); }
|
||||||
|
else { document.addEventListener('DOMContentLoaded', initForm); }
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<a href="#apropos">À propos</a>
|
<a href="#apropos">À propos</a>
|
||||||
<a href="pages/services.html">Services</a>
|
<a href="pages/services.html">Services</a>
|
||||||
<a href="pages/realisations.html">Réalisations</a>
|
<a href="pages/realisations.html">Réalisations</a>
|
||||||
|
<a href="pages/faq.html">FAQ</a>
|
||||||
<a href="#contact">Contact</a>
|
<a href="#contact">Contact</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-links">
|
<div class="footer-links">
|
||||||
|
|
@ -28,9 +29,34 @@
|
||||||
<a href="pages/cgv.html">CGV</a>
|
<a href="pages/cgv.html">CGV</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-social">
|
<div class="footer-social">
|
||||||
<a href="#" aria-label="Discord"><i data-feather="message-circle"></i></a>
|
<!-- Discord -->
|
||||||
<a href="#" aria-label="Twitter"><i data-feather="twitter"></i></a>
|
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" aria-label="Discord">
|
||||||
<a href="#" aria-label="Instagram"><i data-feather="instagram"></i></a>
|
<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>
|
||||||
|
<!-- X / Twitter -->
|
||||||
|
<a href="#" target="_blank" rel="noopener noreferrer" aria-label="X (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">
|
||||||
|
<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">
|
||||||
|
<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">
|
||||||
|
<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">
|
||||||
|
<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">
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@
|
||||||
<div class="stat-label">Joueurs actifs</div>
|
<div class="stat-label">Joueurs actifs</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-number">3</div>
|
<div class="stat-number" id="experienceYears"></div>
|
||||||
<div class="stat-label">Ans d'expérience</div>
|
<div class="stat-label">Ans d'exp.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -34,3 +34,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<!-- <script>
|
||||||
|
const startYear = 2022; // Année de début de l'experience
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const years = currentYear - startYear;
|
||||||
|
document.getElementById('experienceYears').textContent = years;
|
||||||
|
</script> -->
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
<div class="service-icon">
|
<div class="service-icon">
|
||||||
<i data-feather="code"></i>
|
<i data-feather="code"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3>Bots Discord RP</h3>
|
<h3>Bots Discord</h3>
|
||||||
<p>Des bots entièrement personnalisés pour animer et gérer vos serveurs RP.</p>
|
<p>Des bots entièrement personnalisés pour animer et gérer touts vos serveurs.</p>
|
||||||
<ul class="service-features">
|
<ul class="service-features">
|
||||||
<li>Systèmes d'économie avancés</li>
|
<li>Systèmes d'économie avancés</li>
|
||||||
<li>Gestion des stats et inventaires</li>
|
<li>Gestion des stats et inventaires</li>
|
||||||
|
|
|
||||||
139
index.html
139
index.html
|
|
@ -64,8 +64,8 @@
|
||||||
<div class="stat-label">Joueurs actifs</div>
|
<div class="stat-label">Joueurs actifs</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-number">3</div>
|
<div class="stat-number" id="experienceYears">3</div>
|
||||||
<div class="stat-label">Ans d'expérience</div>
|
<div class="stat-label">Ans d'exp.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,6 +78,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script>
|
||||||
|
const startYear = 2023; // Année de début de l'experience
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const years = currentYear - startYear;
|
||||||
|
document.getElementById('experienceYears').textContent = years;
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Services Preview Section -->
|
<!-- Services Preview Section -->
|
||||||
<!-- <div data-include="components/index/services.html"></div> -->
|
<!-- <div data-include="components/index/services.html"></div> -->
|
||||||
|
|
@ -92,8 +98,8 @@
|
||||||
<div class="service-icon">
|
<div class="service-icon">
|
||||||
<i data-feather="code"></i>
|
<i data-feather="code"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3>Bots Discord RP</h3>
|
<h3>Bots Discord</h3>
|
||||||
<p>Des bots entièrement personnalisés pour animer et gérer vos serveurs RP.</p>
|
<p>Des bots entièrement personnalisés pour animer et gérer touts vos serveurs.</p>
|
||||||
<ul class="service-features">
|
<ul class="service-features">
|
||||||
<li>Systèmes d'économie avancés</li>
|
<li>Systèmes d'économie avancés</li>
|
||||||
<li>Gestion des stats et inventaires</li>
|
<li>Gestion des stats et inventaires</li>
|
||||||
|
|
@ -185,31 +191,8 @@
|
||||||
<h2 class="section-title">Nos réalisations</h2>
|
<h2 class="section-title">Nos réalisations</h2>
|
||||||
<p class="section-desc">Découvrez quelques-uns de nos projets récents</p>
|
<p class="section-desc">Découvrez quelques-uns de nos projets récents</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="portfolio-grid">
|
<div class="portfolio-grid" id="home-portfolio-grid">
|
||||||
<div class="portfolio-item reveal">
|
<!-- Rendu dynamique depuis assets/data/projects.json par home.js -->
|
||||||
<div class="portfolio-bg"></div>
|
|
||||||
<div class="portfolio-content">
|
|
||||||
<span class="portfolio-tag">Bot Discord</span>
|
|
||||||
<h4>Medieval RP Bot</h4>
|
|
||||||
<p>Système complet pour serveur fantasy</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="portfolio-item reveal reveal-delay-1">
|
|
||||||
<div class="portfolio-bg" style="background: linear-gradient(135deg, #5E2040, #8b3a62);"></div>
|
|
||||||
<div class="portfolio-content">
|
|
||||||
<span class="portfolio-tag">Fortnite Map</span>
|
|
||||||
<h4>Cyberpunk City</h4>
|
|
||||||
<p>Environnement urbain futuriste</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="portfolio-item reveal reveal-delay-2">
|
|
||||||
<div class="portfolio-bg" style="background: linear-gradient(135deg, #3a1f5e, #8b3a62);"></div>
|
|
||||||
<div class="portfolio-content">
|
|
||||||
<span class="portfolio-tag">Bot Discord</span>
|
|
||||||
<h4>Space Station RP</h4>
|
|
||||||
<p>Bot Sci-Fi avec économie spatiale</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align: center; margin-top: 3rem;" class="reveal">
|
<div style="text-align: center; margin-top: 3rem;" class="reveal">
|
||||||
<a href="pages/realisations.html" class="btn btn-secondary">
|
<a href="pages/realisations.html" class="btn btn-secondary">
|
||||||
|
|
@ -219,6 +202,63 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Partners Section -->
|
||||||
|
<section class="partners" id="partenaires">
|
||||||
|
<div class="section-header reveal">
|
||||||
|
<p class="section-tag">Partenaires</p>
|
||||||
|
<h2 class="section-title">Ils nous font confiance</h2>
|
||||||
|
<p class="section-desc">Des projets construits ensemble</p>
|
||||||
|
</div>
|
||||||
|
<div class="partners-carousel-wrapper reveal">
|
||||||
|
<div class="partners-carousel-track" id="partners-track">
|
||||||
|
<!-- Rendu dynamique depuis assets/data/partners.json par home.js -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Partner Modal -->
|
||||||
|
<div class="team-modal-overlay" id="partner-modal-overlay">
|
||||||
|
<div class="team-modal">
|
||||||
|
<div class="team-modal-header">
|
||||||
|
<button class="team-modal-close" id="partner-modal-close">
|
||||||
|
<i data-feather="x"></i>
|
||||||
|
</button>
|
||||||
|
<div class="team-modal-avatar partner-modal-avatar">
|
||||||
|
<img src="" alt="" id="partner-modal-logo-img" class="team-modal-avatar-photo">
|
||||||
|
<div class="team-modal-avatar-inner team-modal-avatar-fallback" id="partner-modal-logo-fallback"></div>
|
||||||
|
</div>
|
||||||
|
<h3 class="team-modal-name" id="partner-modal-name"></h3>
|
||||||
|
<p class="team-modal-role">Partenaire depuis <span id="partner-modal-since"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="team-modal-content">
|
||||||
|
<p class="team-modal-bio" id="partner-modal-desc"></p>
|
||||||
|
<div class="team-modal-stats" id="partner-modal-stats"></div>
|
||||||
|
<div class="team-modal-skills" id="partner-modal-services"></div>
|
||||||
|
<div class="team-modal-social" id="partner-modal-links"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FAQ Teaser -->
|
||||||
|
<section class="faq-teaser" id="faq-teaser">
|
||||||
|
<div class="faq-teaser-inner reveal">
|
||||||
|
<div class="faq-teaser-text">
|
||||||
|
<p class="section-tag">FAQ</p>
|
||||||
|
<h2>Une question avant de nous contacter ?</h2>
|
||||||
|
<p>
|
||||||
|
<!-- Délais, tarifs, révisions, technologies…<br> -->
|
||||||
|
Nous avons rassemblé les réponses aux questions les plus fréquentes.
|
||||||
|
Consultez notre FAQ ! <br>
|
||||||
|
Vous y trouverez peut-être exactement ce qu'il vous faut.<br>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="pages/faq.html" class="btn btn-secondary faq-teaser-btn">
|
||||||
|
<i data-feather="help-circle"></i>
|
||||||
|
Consulter la FAQ
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Contact Section -->
|
<!-- Contact Section -->
|
||||||
<!-- <div data-include="components/index/contact.html"></div> -->
|
<!-- <div data-include="components/index/contact.html"></div> -->
|
||||||
<section class="contact" id="contact">
|
<section class="contact" id="contact">
|
||||||
|
|
@ -240,7 +280,8 @@
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.gg/FwvNkvu5dy" class="contact-link">
|
<a href="https://discord.gg/FwvNkvu5dy" class="contact-link">
|
||||||
<div class="contact-link-icon">
|
<div class="contact-link-icon">
|
||||||
<i data-feather="message-circle"></i>
|
<!-- <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>
|
</div>
|
||||||
<span>Discord : OmégaKube</span>
|
<span>Discord : OmégaKube</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -347,7 +388,9 @@
|
||||||
website: form.elements['website'] ? form.elements['website'].value : ''
|
website: form.elements['website'] ? form.elements['website'].value : ''
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch('assets/php/send_mail.php', {
|
// Chemin relatif au script — fonctionne depuis racine et /pages/
|
||||||
|
const phpBase = window.location.pathname.includes('/pages/') ? '../assets/php/' : 'assets/php/';
|
||||||
|
const res = await fetch(phpBase + 'send_mail.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
|
|
@ -376,7 +419,41 @@
|
||||||
|
|
||||||
<div data-include="components/footer.html"></div>
|
<div data-include="components/footer.html"></div>
|
||||||
|
|
||||||
|
<!-- Modal projet (même structure que realisations.html) -->
|
||||||
|
<div class="project-modal-overlay" id="project-modal-overlay">
|
||||||
|
<div class="project-modal">
|
||||||
|
<button class="project-modal-close" id="project-modal-close">
|
||||||
|
<i data-feather="x"></i>
|
||||||
|
</button>
|
||||||
|
<div class="project-gallery" id="project-gallery">
|
||||||
|
<div class="project-gallery-slides" id="gallery-slides"></div>
|
||||||
|
<button class="gallery-nav prev" id="gallery-prev"><i data-feather="chevron-left"></i></button>
|
||||||
|
<button class="gallery-nav next" id="gallery-next"><i data-feather="chevron-right"></i></button>
|
||||||
|
<div class="gallery-dots" id="gallery-dots"></div>
|
||||||
|
</div>
|
||||||
|
<div class="project-modal-content">
|
||||||
|
<div class="project-modal-header">
|
||||||
|
<div>
|
||||||
|
<span class="portfolio-tag" id="modal-category"></span>
|
||||||
|
<h2 id="modal-title"></h2>
|
||||||
|
</div>
|
||||||
|
<div class="project-modal-meta" id="modal-meta"></div>
|
||||||
|
</div>
|
||||||
|
<p class="project-modal-desc" id="modal-desc"></p>
|
||||||
|
<div class="project-modal-tabs">
|
||||||
|
<button class="project-modal-tab active" data-tab="features">Fonctionnalités</button>
|
||||||
|
<button class="project-modal-tab" data-tab="tech">Technologies</button>
|
||||||
|
<button class="project-modal-tab" data-tab="stats">Statistiques</button>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js"></script>
|
||||||
|
<script src="assets/js/pages/realisations.js" defer></script>
|
||||||
<script src="assets/js/home.js" defer></script>
|
<script src="assets/js/home.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
<?php http_response_code(404); ?>
|
<?php
|
||||||
|
http_response_code(404);
|
||||||
|
// Calcule la racine du site à partir du chemin physique de ce fichier
|
||||||
|
// Ex: /var/www/html/omegakube/pages/404.php → base = /omegakube
|
||||||
|
$script = $_SERVER['SCRIPT_NAME']; // ex: /omegakube/pages/404.php
|
||||||
|
$base = rtrim(dirname(dirname($script)), '/'); // remonte 2 niveaux → /omegakube
|
||||||
|
// Si on est à la racine, $base vaut "" — c'est correct
|
||||||
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr" data-theme="dark">
|
<html lang="fr" data-theme="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>404 — Page non trouvée | OmégaKube</title>
|
<title>404 — Page non trouvée | OmégaKube</title>
|
||||||
<template data-include="/testarea/omegakube/v1bis/components/favicons.html"></template>
|
<link rel="stylesheet" href="<?php echo $base; ?>/assets/css/404.css">
|
||||||
<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=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
||||||
<script src="https://unpkg.com/feather-icons"></script>
|
|
||||||
<link rel="stylesheet" href="/testarea/omegakube/v1bis/assets/css/404.css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="bg-grid"></div>
|
<div class="bg-grid"></div>
|
||||||
|
|
@ -32,21 +33,31 @@
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="error-icon">
|
<div class="error-icon">
|
||||||
<i data-feather="alert-triangle"></i>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="40" height="40">
|
||||||
|
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||||||
|
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||||
|
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="error-code">404</div>
|
<div class="error-code">404</div>
|
||||||
<h1 class="error-title">Dimension introuvable</h1>
|
<h1 class="error-title">Dimension introuvable</h1>
|
||||||
<p class="error-message">
|
<p class="error-message">
|
||||||
Oups ! Il semblerait que vous ayez traversé un <span>portail défectueux</span>.
|
Oups ! Il semblerait que vous ayez traversé un <span>portail défectueux</span>.
|
||||||
Cette page n'existe pas dans notre univers... ou peut-être a-t-elle été téléportée ailleurs ?
|
Cette page n'existe pas dans notre univers... ou peut-être a-t-elle été téléportée ailleurs ?
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<a href="/testarea/omegakube/v1bis/index.html" class="btn btn-primary">
|
<a href="<?php echo $base; ?>/index.html" class="btn btn-primary">
|
||||||
<i data-feather="home"></i>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
|
||||||
|
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||||
|
<polyline points="9 22 9 12 15 12 15 22"/>
|
||||||
|
</svg>
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript:history.back()" class="btn btn-secondary">
|
<a href="javascript:history.back()" class="btn btn-secondary">
|
||||||
<i data-feather="arrow-left"></i>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
|
||||||
|
<line x1="19" y1="12" x2="5" y2="12"/>
|
||||||
|
<polyline points="12 19 5 12 12 5"/>
|
||||||
|
</svg>
|
||||||
Page précédente
|
Page précédente
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -55,9 +66,9 @@
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<span class="logo-mini">ΩK</span>
|
<span class="logo-mini">ΩK</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<a href="/testarea/omegakube/v1bis/index.html">OmégaKube</a>
|
<a href="<?php echo $base; ?>/index.html">OmégaKube</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/testarea/omegakube/v1bis/assets/js/pages/404.js"></script>
|
<script src="<?php echo $base; ?>/assets/js/pages/404.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
109
pages/faq.html
Normal file
109
pages/faq.html
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr" data-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FAQ — OmégaKube</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div data-include="../components/background.html"></div>
|
||||||
|
<div data-include="../components/header.html"></div>
|
||||||
|
|
||||||
|
<section class="page-hero">
|
||||||
|
<div class="page-hero-content">
|
||||||
|
<p class="section-tag">FAQ</p>
|
||||||
|
<h1>Questions <span class="highlight">fréquentes</span></h1>
|
||||||
|
<p>Tout ce que vous devez savoir avant de nous contacter.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="faq-page-section">
|
||||||
|
<div class="faq-page-container" id="faq-container"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="faq-cta-section">
|
||||||
|
<div class="faq-cta-inner reveal">
|
||||||
|
<h2>Vous n'avez pas trouvé votre réponse ?</h2>
|
||||||
|
<p>Notre équipe est disponible pour répondre à toutes vos questions spécifiques. N'hésitez pas à nous contacter directement.</p>
|
||||||
|
<div class="faq-cta-btns">
|
||||||
|
<a href="../index.html#contact" class="btn btn-primary">
|
||||||
|
<i data-feather="message-circle"></i>
|
||||||
|
Nous contacter
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/FwvNkvu5dy" 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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div data-include="../components/footer.html"></div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const container = document.getElementById('faq-container');
|
||||||
|
|
||||||
|
let faqData;
|
||||||
|
try {
|
||||||
|
const res = await fetch('../assets/data/faq.json');
|
||||||
|
if (!res.ok) throw new Error(res.statusText);
|
||||||
|
faqData = await res.json();
|
||||||
|
} catch (err) {
|
||||||
|
container.innerHTML = '<p style="text-align:center;color:var(--text-muted);">Impossible de charger la FAQ.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = faqData.map((cat, ci) => `
|
||||||
|
<div class="faq-category reveal${ci > 0 ? ' reveal-delay-' + Math.min(ci, 3) : ''}">
|
||||||
|
<h3 class="faq-category-title">
|
||||||
|
<i data-feather="${cat.icon}"></i>
|
||||||
|
${cat.title}
|
||||||
|
</h3>
|
||||||
|
<div class="faq-list">
|
||||||
|
${cat.questions.map((item, qi) => `
|
||||||
|
<div class="faq-item" id="faq-${cat.id}-${qi}">
|
||||||
|
<button class="faq-question" aria-expanded="false">
|
||||||
|
${item.q}
|
||||||
|
<i data-feather="chevron-down" class="faq-chevron"></i>
|
||||||
|
</button>
|
||||||
|
<div class="faq-answer">
|
||||||
|
<p>${item.a}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
if (window.feather) feather.replace();
|
||||||
|
|
||||||
|
container.querySelectorAll('.faq-question').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
const item = btn.closest('.faq-item');
|
||||||
|
const isOpen = item.classList.contains('open');
|
||||||
|
container.querySelectorAll('.faq-item').forEach(i => {
|
||||||
|
i.classList.remove('open');
|
||||||
|
i.querySelector('.faq-question').setAttribute('aria-expanded', 'false');
|
||||||
|
});
|
||||||
|
if (!isOpen) {
|
||||||
|
item.classList.add('open');
|
||||||
|
btn.setAttribute('aria-expanded', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelectorAll('.reveal').forEach(el => {
|
||||||
|
if (el.getBoundingClientRect().top < window.innerHeight - 50) {
|
||||||
|
el.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Projets Phares - Bento Grid -->
|
<!-- Projets Phares - Bento Grid (dynamique depuis projects.json) -->
|
||||||
<section style="padding-top: 2rem;">
|
<section style="padding-top: 2rem;">
|
||||||
<div class="section-header reveal">
|
<div class="section-header reveal">
|
||||||
<p class="section-tag">En vedette</p>
|
<p class="section-tag">En vedette</p>
|
||||||
|
|
@ -78,169 +78,8 @@
|
||||||
<p class="section-desc">Nos réalisations les plus marquantes</p>
|
<p class="section-desc">Nos réalisations les plus marquantes</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bento-grid" style="padding: 0 2rem;">
|
<div class="bento-grid" style="padding: 0 2rem;" id="featured-bento-grid">
|
||||||
|
<!-- Rendu dynamique depuis ../assets/data/projects.json par realisations.js -->
|
||||||
<!-- Projet Phare 1 - Medieval RP Bot -->
|
|
||||||
<div class="bento-project reveal">
|
|
||||||
<div class="bento-image" style="background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));">
|
|
||||||
<div class="bento-image-content">
|
|
||||||
<span class="portfolio-tag">Bot Discord</span>
|
|
||||||
<h3>Medieval RP Bot</h3>
|
|
||||||
<p>Un système RP médiéval complet pour serveur fantasy</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stats">
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="users"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">5000+</div>
|
|
||||||
<div class="bento-stat-label">Joueurs actifs</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="clock"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">3 mois</div>
|
|
||||||
<div class="bento-stat-label">Développement</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="terminal"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">150+</div>
|
|
||||||
<div class="bento-stat-label">Commandes</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="database"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">99.9%</div>
|
|
||||||
<div class="bento-stat-label">Uptime</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-info">
|
|
||||||
<h4>Description</h4>
|
|
||||||
<p>Développé pour l'un des plus grands serveurs Discord francophones, ce bot gère l'intégralité de l'expérience RP : économie, métiers, combat tour par tour, gestion de royaumes et alliances, quêtes automatisées et bien plus encore.</p>
|
|
||||||
<div class="bento-features">
|
|
||||||
<span class="bento-feature">Économie</span>
|
|
||||||
<span class="bento-feature">Combat</span>
|
|
||||||
<span class="bento-feature">Métiers</span>
|
|
||||||
<span class="bento-feature">Royaumes</span>
|
|
||||||
<span class="bento-feature">Quêtes</span>
|
|
||||||
<span class="bento-feature">Events</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bento-divider"></div>
|
|
||||||
|
|
||||||
<!-- Projet Phare 2 - Cyberpunk City (Reverse) -->
|
|
||||||
<div class="bento-project reverse reveal">
|
|
||||||
<div class="bento-image" style="background: linear-gradient(135deg, #5E2040, #1a3a5e);">
|
|
||||||
<div class="bento-image-content">
|
|
||||||
<span class="portfolio-tag">Fortnite Map</span>
|
|
||||||
<h3>Cyberpunk City</h3>
|
|
||||||
<p>Une ville futuriste immersive pour le RP urbain</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stats">
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="play"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">50K+</div>
|
|
||||||
<div class="bento-stat-label">Parties jouées</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="home"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">100+</div>
|
|
||||||
<div class="bento-stat-label">Bâtiments</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="map-pin"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">5</div>
|
|
||||||
<div class="bento-stat-label">Quartiers</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="users"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">50</div>
|
|
||||||
<div class="bento-stat-label">Joueurs max</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-info">
|
|
||||||
<h4>Description</h4>
|
|
||||||
<p>Néons, gratte-ciels, quartiers distincts et ambiance blade runner. Cette map a été optimisée pour accueillir jusqu'à 50 joueurs simultanément avec un système jour/nuit dynamique et des zones interactives scriptées.</p>
|
|
||||||
<div class="bento-features">
|
|
||||||
<span class="bento-feature">Néons</span>
|
|
||||||
<span class="bento-feature">Jour/Nuit</span>
|
|
||||||
<span class="bento-feature">Scripts</span>
|
|
||||||
<span class="bento-feature">Optimisé</span>
|
|
||||||
<span class="bento-feature">Immersif</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bento-divider"></div>
|
|
||||||
|
|
||||||
<!-- Projet Phare 3 - Nova RP -->
|
|
||||||
<div class="bento-project reveal">
|
|
||||||
<div class="bento-image" style="background: linear-gradient(135deg, #2d1b5e, #5E2040);">
|
|
||||||
<div class="bento-image-content">
|
|
||||||
<span class="portfolio-tag">Pack Complet</span>
|
|
||||||
<h3>Nova RP - Lancement</h3>
|
|
||||||
<p>Accompagnement complet pour un lancement réussi</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stats">
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="users"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">8000+</div>
|
|
||||||
<div class="bento-stat-label">Membres</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="trending-up"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">0→8K</div>
|
|
||||||
<div class="bento-stat-label">En 3 mois</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="package"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">4</div>
|
|
||||||
<div class="bento-stat-label">Services inclus</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat">
|
|
||||||
<div class="bento-stat-icon">
|
|
||||||
<i data-feather="award"></i>
|
|
||||||
</div>
|
|
||||||
<div class="bento-stat-value">Top 10</div>
|
|
||||||
<div class="bento-stat-label">Serveur FR</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bento-info">
|
|
||||||
<h4>Description</h4>
|
|
||||||
<p>Un accompagnement clé en main : bot Discord personnalisé, identité visuelle complète, map Fortnite exclusive et stratégie de lancement. En 3 mois, le serveur est passé de 0 à 8000 membres actifs.</p>
|
|
||||||
<div class="bento-features">
|
|
||||||
<span class="bento-feature">Bot RP</span>
|
|
||||||
<span class="bento-feature">Map Fortnite</span>
|
|
||||||
<span class="bento-feature">Branding</span>
|
|
||||||
<span class="bento-feature">Stratégie</span>
|
|
||||||
<span class="bento-feature">Support</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Baground géométrique -->
|
<!-- Baground géométrique -->
|
||||||
<div data-include="../components\background.html"></div>
|
<div data-include="../components/background.html"></div>
|
||||||
|
|
||||||
<!-- Header / Menu -->
|
<!-- Header / Menu -->
|
||||||
<div data-include="../components/header.html"></div>
|
<div data-include="../components/header.html"></div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue