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>
|
||||
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} !-d
|
||||
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}/index.html !-f
|
||||
RewriteCond %{REQUEST_FILENAME}/index.php !-f
|
||||
RewriteRule ^ pages/404.php [L]
|
||||
</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;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
text-align: center;
|
||||
max-width: 900px;
|
||||
|
|
@ -989,14 +1002,14 @@ section {
|
|||
position: absolute;
|
||||
inset: -4px;
|
||||
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 {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -1415,6 +1428,14 @@ footer {
|
|||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.footer-social a svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: currentColor;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.footer-social a:hover {
|
||||
|
|
@ -1423,6 +1444,110 @@ footer {
|
|||
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 {
|
||||
max-width: 1200px;
|
||||
margin: 2rem auto 0;
|
||||
|
|
@ -1697,14 +1822,14 @@ footer {
|
|||
position: absolute;
|
||||
inset: -4px;
|
||||
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 {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -1815,6 +1940,8 @@ footer {
|
|||
.team-modal-social a svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: currentColor;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
|
@ -2535,50 +2662,9 @@ footer {
|
|||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.project-tech-item {
|
||||
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 and project-stats-grid → see end of file */
|
||||
|
||||
.project-tech-item:hover {
|
||||
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);
|
||||
}
|
||||
/* project-stat-card-value / label → see end of file */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.project-modal {
|
||||
|
|
@ -2728,7 +2814,11 @@ footer {
|
|||
}
|
||||
|
||||
.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 {
|
||||
|
|
@ -3585,3 +3675,497 @@ footer {
|
|||
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))",
|
||||
"year": "2025",
|
||||
"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"],
|
||||
"tech": ["Python", "Discord.py", "MySQL", "Bach"],
|
||||
"stats": [{ "value": "1075+", "label": "Utilisateurs" }, { "value": "150+", "label": "Commandes" }, { "value": "97%", "label": "Uptime" }]
|
||||
"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"
|
||||
],
|
||||
"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,
|
||||
"category": "map",
|
||||
"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)",
|
||||
"year": "2025",
|
||||
"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"],
|
||||
"tech": ["Fortnite Creative", "UEFN", "Verse", "Texturing"],
|
||||
"stats": [{ "value": "30+", "label": "Sessions" }, { "value": "30+", "label": "Bâtiments" }, { "value": "70", "label": "Joueurs max" }]
|
||||
"features": [
|
||||
"économie réaliste",
|
||||
"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,
|
||||
|
|
@ -31,9 +123,55 @@
|
|||
"gradient": "linear-gradient(135deg, #3a1f5e, #8b3a62)",
|
||||
"year": "2024",
|
||||
"duration": "",
|
||||
"features": ["Économie réaliste", "Gestion ticket Whitelist", "Carte d'identités", "Prise de RDV", "système de comptes avec RIB", "Fonctionnalités bonus"],
|
||||
"tech": ["Python", "Discord.py", "MySQL", "Pillow"],
|
||||
"stats": [{ "value": "300+", "label": "Utilisateurs" }, { "value": "100+", "label": "Commandes" }, { "value": "60+", "label": "RIB générés" }]
|
||||
"features": [
|
||||
"Économie réaliste",
|
||||
"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,
|
||||
|
|
@ -42,10 +180,49 @@
|
|||
"desc": "Identité visuelle complète incluant logo, bannières, emojis personnalisés et templates pour Omega Kube.",
|
||||
"gradient": "linear-gradient(135deg, #2d1b5e, #5E2040)",
|
||||
"year": "2024",
|
||||
"duration": "1 semaines",
|
||||
"features": ["Logo principal", "charte graphique", "Bannières", "Animations After effect"],
|
||||
"tech": ["Photoshop", "Illustrator", "After Effects", "Figma"],
|
||||
"stats": [{ "value": "5+", "label": "Éléments" }, { "value": "2h", "label": "After Effects" }, { "value": "3h", "label": "Illustrator" }]
|
||||
"duration": "1 semaine",
|
||||
"features": [
|
||||
"Logo principal",
|
||||
"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,
|
||||
|
|
@ -55,8 +232,44 @@
|
|||
"gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)",
|
||||
"year": "2026",
|
||||
"duration": "4 mois",
|
||||
"features": ["Intégration de SCP", "Cinématiques", "Modeles 3D", "Site Souterrain", "Terraforming exterieur"],
|
||||
"tech": ["Fortnite Creative", "UEFN", "Custom Props"],
|
||||
"stats": [{ "value": "6+", "label": "Secteurs" }, { "value": "15+", "label": "SCP" }, { "value": "30+", "label": "Systèmes" }]
|
||||
"features": [
|
||||
"Intégration de SCP",
|
||||
"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",
|
||||
"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": [
|
||||
{ "value": "2-4", "label": "Semaines de dev" },
|
||||
{ "value": "1 mois","label": "Support inclus" },
|
||||
{ "value": "100%", "label": "Personnalisable" }
|
||||
{
|
||||
"value": "2-4",
|
||||
"label": "Semaines de dev"
|
||||
},
|
||||
{
|
||||
"value": "1 mois",
|
||||
"label": "Support inclus"
|
||||
},
|
||||
{
|
||||
"value": "100%",
|
||||
"label": "Personnalisable"
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
{ "icon": "dollar-sign", "title": "Économie virtuelle", "desc": "Monnaie, banque, transactions" },
|
||||
{ "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" }
|
||||
{
|
||||
"icon": "dollar-sign",
|
||||
"title": "Économie virtuelle",
|
||||
"desc": "Monnaie, banque, transactions"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{ "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": {
|
||||
"label": "Maps Fortnite",
|
||||
"icon": "map",
|
||||
"stamp": { "type": "new", "text": "New" }
|
||||
"stamp": {
|
||||
"type": "new",
|
||||
"text": "New"
|
||||
}
|
||||
},
|
||||
"visual": {
|
||||
"accentFrom": "#1a5e8b",
|
||||
"accentTo": "#3a8bb5",
|
||||
"stamp": { "type": "new", "text": "✦ New" }
|
||||
"stamp": {
|
||||
"type": "new",
|
||||
"text": "✦ New"
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"highlights": [
|
||||
{ "value": "3-6", "label": "Semaines de création" },
|
||||
{ "value": "50+", "label": "Joueurs supportés" },
|
||||
{ "value": "UEFN", "label": "Compatible" }
|
||||
{
|
||||
"value": "3-6",
|
||||
"label": "Semaines de création"
|
||||
},
|
||||
{
|
||||
"value": "50+",
|
||||
"label": "Joueurs supportés"
|
||||
},
|
||||
{
|
||||
"value": "UEFN",
|
||||
"label": "Compatible"
|
||||
}
|
||||
],
|
||||
"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": "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" }
|
||||
{
|
||||
"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": "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": [
|
||||
{ "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",
|
||||
"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": [
|
||||
{ "value": "1-2", "label": "Semaines" },
|
||||
{ "value": "∞", "label": "Révisions" },
|
||||
{ "value": "HD", "label": "Qualité" }
|
||||
{
|
||||
"value": "1-2",
|
||||
"label": "Semaines"
|
||||
},
|
||||
{
|
||||
"value": "∞",
|
||||
"label": "Révisions"
|
||||
},
|
||||
{
|
||||
"value": "HD",
|
||||
"label": "Qualité"
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
{ "icon": "hexagon", "title": "Logo & Branding", "desc": "Identité visuelle complète" },
|
||||
{ "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" }
|
||||
{
|
||||
"icon": "hexagon",
|
||||
"title": "Logo & Branding",
|
||||
"desc": "Identité visuelle complète"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{ "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": {
|
||||
"label": "Consulting",
|
||||
"icon": "users",
|
||||
"stamp": { "type": "free", "text": "Gratuit" }
|
||||
"stamp": {
|
||||
"type": "free",
|
||||
"text": "Gratuit"
|
||||
}
|
||||
},
|
||||
"visual": {
|
||||
"accentFrom": "#3a8b5e",
|
||||
"accentTo": "#5ab57a",
|
||||
"stamp": { "type": "free", "text": "✓ Gratuit", "animated": true }
|
||||
"stamp": {
|
||||
"type": "free",
|
||||
"text": "✓ Gratuit",
|
||||
"animated": true
|
||||
}
|
||||
},
|
||||
"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é.",
|
||||
"highlights": [
|
||||
{ "value": "50+", "label": "Projets accompagnés" },
|
||||
{ "value": "3 ans", "label": "D'expérience" },
|
||||
{ "value": "24/7", "label": "Support Discord" }
|
||||
{
|
||||
"value": "50+",
|
||||
"label": "Projets accompagnés"
|
||||
},
|
||||
{
|
||||
"value": "3 ans",
|
||||
"label": "D'expérience"
|
||||
},
|
||||
{
|
||||
"value": "24/7",
|
||||
"label": "Support Discord"
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
{ "icon": "search", "title": "Audit serveur", "desc": "Analyse de l'existant" },
|
||||
{ "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" }
|
||||
{
|
||||
"icon": "search",
|
||||
"title": "Audit serveur",
|
||||
"desc": "Analyse de l'existant"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{ "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": {
|
||||
"label": "Pack Complet",
|
||||
"icon": "package",
|
||||
"stamp": { "type": "popular", "text": "Populaire" }
|
||||
"stamp": {
|
||||
"type": "popular",
|
||||
"text": "Populaire"
|
||||
}
|
||||
},
|
||||
"visual": {
|
||||
"accentFrom": "#8b3a8b",
|
||||
"accentTo": "#b55ab5",
|
||||
"stamp": { "type": "popular", "text": "★ Populaire", "animated": true }
|
||||
"stamp": {
|
||||
"type": "popular",
|
||||
"text": "★ Populaire",
|
||||
"animated": true
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"highlights": [
|
||||
{ "value": "-20%", "label": "VS services séparés" },
|
||||
{ "value": "3 mois", "label": "Support inclus" },
|
||||
{ "value": "Clé en main","label": "Prêt à lancer" }
|
||||
{
|
||||
"value": "-20%",
|
||||
"label": "VS services séparés"
|
||||
},
|
||||
{
|
||||
"value": "3 mois",
|
||||
"label": "Support inclus"
|
||||
},
|
||||
{
|
||||
"value": "Clé en main",
|
||||
"label": "Prêt à lancer"
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
{ "icon": "code", "title": "Bot Discord RP", "desc": "Système complet personnalisé" },
|
||||
{ "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" }
|
||||
{
|
||||
"icon": "code",
|
||||
"title": "Bot Discord RP",
|
||||
"desc": "Système complet personnalisé"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{ "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', () => {
|
||||
// Fetch team data and render
|
||||
|
||||
// ── 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(projects => {
|
||||
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 => console.error('Failed to load projects for home preview', 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);
|
||||
});
|
||||
.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) {
|
||||
const carousel = document.getElementById('team-carousel');
|
||||
const dotsContainer = document.getElementById('carousel-dots');
|
||||
const prevBtn = document.getElementById('carousel-prev');
|
||||
const nextBtn = document.getElementById('carousel-next');
|
||||
|
||||
// Render cards
|
||||
carousel.innerHTML = members.map((m, i) => `
|
||||
<div class="team-card" data-member="${i}">
|
||||
<div class="team-avatar">
|
||||
<div class="team-avatar-inner">${m.avatar}</div>
|
||||
carousel.innerHTML = members.map((m, i) => {
|
||||
const imgSrc = `assets/images/team/${m.id}.png`;
|
||||
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-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>
|
||||
<h4>${m.name}</h4>
|
||||
<p class="team-role">${m.role}</p>
|
||||
<p class="team-desc">${(m.bio || '').split('.').shift()}.</p>
|
||||
</div>
|
||||
<h4>${m.name}</h4>
|
||||
<p class="team-role">${m.role}</p>
|
||||
<p class="team-desc">${(m.bio || '').split('.').shift()}.</p>
|
||||
</div>
|
||||
`).join('');
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// Carousel logic
|
||||
const cardsContainer = carousel; // the track
|
||||
let cards = carousel.querySelectorAll('.team-card');
|
||||
let cards = Array.from(carousel.querySelectorAll('.team-card'));
|
||||
let currentIndex = 0;
|
||||
let cardsPerView = 3;
|
||||
let autoPlayInterval;
|
||||
|
|
@ -41,10 +243,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
else cardsPerView = 3;
|
||||
}
|
||||
|
||||
function getTotalDots() { return Math.max(1, cards.length - cardsPerView + 1); }
|
||||
|
||||
function createDots() {
|
||||
dotsContainer.innerHTML = '';
|
||||
const totalDots = Math.max(1, Math.ceil(cards.length / cardsPerView));
|
||||
for (let i = 0; i < totalDots; i++) {
|
||||
for (let i = 0; i < getTotalDots(); i++) {
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('carousel-dot');
|
||||
if (i === 0) dot.classList.add('active');
|
||||
|
|
@ -54,65 +257,49 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
}
|
||||
|
||||
function updateCarousel() {
|
||||
cards = carousel.querySelectorAll('.team-card');
|
||||
if (cards.length === 0) return;
|
||||
const cardStyle = window.getComputedStyle(cards[0]);
|
||||
const gap = 32; // matches previous implementation
|
||||
cards = Array.from(carousel.querySelectorAll('.team-card'));
|
||||
if (!cards.length) return;
|
||||
const gap = 32;
|
||||
const cardWidth = cards[0].offsetWidth + gap;
|
||||
carousel.style.transform = `translateX(-${currentIndex * cardWidth}px)`;
|
||||
|
||||
const dots = dotsContainer.querySelectorAll('.carousel-dot');
|
||||
const activeDotIndex = Math.floor(currentIndex / cardsPerView);
|
||||
dots.forEach((dot, i) => dot.classList.toggle('active', i === activeDotIndex));
|
||||
Array.from(dotsContainer.querySelectorAll('.carousel-dot')).forEach((d, i) => d.classList.toggle('active', i === currentIndex));
|
||||
}
|
||||
|
||||
function goToSlide(dotIndex) {
|
||||
currentIndex = dotIndex * cardsPerView;
|
||||
if (currentIndex > cards.length - cardsPerView) currentIndex = Math.max(0, cards.length - cardsPerView);
|
||||
updateCarousel();
|
||||
resetAutoPlay();
|
||||
function goToSlide(index) {
|
||||
currentIndex = Math.min(index, Math.max(0, cards.length - cardsPerView));
|
||||
updateCarousel(); resetAutoPlay();
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
currentIndex++;
|
||||
if (currentIndex > cards.length - cardsPerView) currentIndex = 0;
|
||||
const max = Math.max(0, cards.length - cardsPerView);
|
||||
currentIndex = currentIndex >= max ? 0 : currentIndex + 1;
|
||||
updateCarousel();
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
currentIndex--;
|
||||
if (currentIndex < 0) currentIndex = Math.max(0, cards.length - cardsPerView);
|
||||
const max = Math.max(0, cards.length - cardsPerView);
|
||||
currentIndex = currentIndex <= 0 ? max : currentIndex - 1;
|
||||
updateCarousel();
|
||||
}
|
||||
|
||||
function startAutoPlay() {
|
||||
clearInterval(autoPlayInterval);
|
||||
autoPlayInterval = setInterval(nextSlide, 4000);
|
||||
}
|
||||
|
||||
function resetAutoPlay() {
|
||||
clearInterval(autoPlayInterval);
|
||||
startAutoPlay();
|
||||
}
|
||||
function startAutoPlay() { clearInterval(autoPlayInterval); autoPlayInterval = setInterval(nextSlide, 4000); }
|
||||
function resetAutoPlay() { clearInterval(autoPlayInterval); startAutoPlay(); }
|
||||
|
||||
prevBtn && prevBtn.addEventListener('click', () => { prevSlide(); resetAutoPlay(); });
|
||||
nextBtn && nextBtn.addEventListener('click', () => { nextSlide(); resetAutoPlay(); });
|
||||
|
||||
carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval));
|
||||
carousel.addEventListener('mouseleave', startAutoPlay);
|
||||
|
||||
updateCardsPerView();
|
||||
createDots();
|
||||
startAutoPlay();
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateCardsPerView();
|
||||
createDots();
|
||||
currentIndex = 0;
|
||||
updateCarousel();
|
||||
let resizeTimer;
|
||||
const ro = new ResizeObserver(() => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(() => { updateCardsPerView(); currentIndex = 0; createDots(); updateCarousel(); }, 100);
|
||||
});
|
||||
ro.observe(carousel.parentElement || document.body);
|
||||
|
||||
// Modal logic
|
||||
updateCardsPerView(); createDots(); updateCarousel(); startAutoPlay();
|
||||
|
||||
// Modal
|
||||
const modalOverlay = document.getElementById('team-modal-overlay');
|
||||
const modalClose = document.getElementById('team-modal-close');
|
||||
const modalAvatar = document.getElementById('modal-avatar');
|
||||
|
|
@ -124,29 +311,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
const modalSocial = document.getElementById('modal-social');
|
||||
|
||||
function openMemberModal(index) {
|
||||
const member = members[index];
|
||||
if (!member) return;
|
||||
modalAvatar.textContent = member.avatar || member.name.charAt(0);
|
||||
modalName.textContent = member.name;
|
||||
modalRole.textContent = member.role;
|
||||
modalBio.textContent = member.bio;
|
||||
|
||||
modalStats.innerHTML = (member.stats || []).map(stat => `
|
||||
const m = members[index];
|
||||
if (!m) return;
|
||||
// Avatar avec image si dispo
|
||||
const imgSrc = `assets/images/team/${m.id}.png`;
|
||||
modalAvatar.innerHTML = `
|
||||
<img src="${imgSrc}" alt="${m.name}" class="team-modal-avatar-photo"
|
||||
onerror="this.style.display='none';this.nextElementSibling.style.display='flex';">
|
||||
<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-value">${stat.value}</div>
|
||||
<div class="team-modal-stat-label">${stat.label}</div>
|
||||
<div class="team-modal-stat-value">${s.value}</div>
|
||||
<div class="team-modal-stat-label">${s.label}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
modalSkills.innerHTML = (member.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
|
||||
|
||||
modalSkills.innerHTML = (m.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
|
||||
modalSocial.innerHTML = `
|
||||
<a href="#" aria-label="Discord"><i data-feather="message-circle"></i></a>
|
||||
<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">
|
||||
<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');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
|
@ -156,16 +347,68 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Attach click listeners to generated cards
|
||||
carousel.querySelectorAll('.team-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
const idx = parseInt(card.dataset.member, 10);
|
||||
openMemberModal(idx);
|
||||
});
|
||||
const openCard = () => { const idx = parseInt(card.dataset.member, 10); openMemberModal(idx); };
|
||||
card.addEventListener('click', openCard);
|
||||
card.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openCard(); } });
|
||||
});
|
||||
|
||||
modalClose && modalClose.addEventListener('click', closeMemberModal);
|
||||
modalOverlay && modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeMemberModal(); });
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeMemberModal(); });
|
||||
modalOverlay && modalOverlay.addEventListener('click', e => { if (e.target === modalOverlay) 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
|
||||
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;
|
||||
|
||||
function getPreferredTheme() {
|
||||
|
|
@ -367,27 +376,27 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
window.addEventListener('hashchange', wrappedSetActive);
|
||||
window.addEventListener('popstate', wrappedSetActive);
|
||||
|
||||
// Reveal on scroll
|
||||
const revealElements = document.querySelectorAll('.reveal');
|
||||
// Reveal on scroll — re-query après chargement des includes
|
||||
function revealOnScroll() {
|
||||
if (!revealElements.length) return;
|
||||
revealElements.forEach(el => {
|
||||
const elementTop = el.getBoundingClientRect().top;
|
||||
if (elementTop < window.innerHeight - 100) {
|
||||
const els = document.querySelectorAll('.reveal:not(.active)');
|
||||
els.forEach(el => {
|
||||
if (el.getBoundingClientRect().top < window.innerHeight - 50) {
|
||||
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);
|
||||
|
||||
// Contact form (if exists)
|
||||
const contactForm = document.getElementById('contact-form');
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
alert('Merci pour votre message ! Nous vous répondrons très bientôt.');
|
||||
this.reset();
|
||||
});
|
||||
}
|
||||
// Contact form (if exists) c'est en double
|
||||
// const contactForm = document.getElementById('contact-form');
|
||||
// if (contactForm) {
|
||||
// contactForm.addEventListener('submit', function (e) {
|
||||
// e.preventDefault();
|
||||
// alert('Merci pour votre message ! Nous vous répondrons très bientôt.');
|
||||
// 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 () => {
|
||||
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 = [];
|
||||
|
||||
try {
|
||||
|
|
@ -133,35 +135,80 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
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) {
|
||||
const project = allProjects.find(p => p.id === Number(projectId));
|
||||
if (!project) return;
|
||||
|
||||
resetModalTabs();
|
||||
|
||||
modalCategory.textContent = getCategoryLabel(project.category);
|
||||
modalTitle.textContent = project.title;
|
||||
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('');
|
||||
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 => `
|
||||
<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-label">${s.label}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// gallery slides (use gradient as placeholder slides like original)
|
||||
gallerySlides.innerHTML = `
|
||||
<div class="project-gallery-slide" style="background: ${project.gradient};"></div>
|
||||
<div class="project-gallery-slide" style="background: ${project.gradient}; filter: hue-rotate(30deg);"></div>
|
||||
<div class="project-gallery-slide" style="background: ${project.gradient}; filter: hue-rotate(-30deg);"></div>
|
||||
`;
|
||||
// Gallery : charger les images du dossier via convention de nommage
|
||||
// images/projects/{slug}/1.png, 2.png... avec fallback gradient
|
||||
const imgPath = project.images || '';
|
||||
const imgExtensions = ['png', 'jpg', 'jpeg', 'webp'];
|
||||
|
||||
// On tente de charger jusqu'à 6 images numérotées depuis le dossier
|
||||
const maxImages = 6;
|
||||
let slidesHtml = '';
|
||||
|
||||
// Toujours créer au moins 1 slide gradient (fallback)
|
||||
// Les images réelles se chargent avec onerror pour masquer si absentes
|
||||
for (let i = 1; i <= maxImages; i++) {
|
||||
slidesHtml += `
|
||||
<div class="project-gallery-slide" data-img-idx="${i}" style="background: ${project.gradient};">
|
||||
<img src="${imgPath}${i}.png" alt="${project.title} ${i}"
|
||||
class="gallery-slide-img"
|
||||
onerror="this.parentElement.classList.add('no-image')"
|
||||
onload="this.parentElement.classList.add('has-image')">
|
||||
</div>`;
|
||||
}
|
||||
gallerySlides.innerHTML = slidesHtml;
|
||||
|
||||
// reset gallery state
|
||||
galleryIndex = 0;
|
||||
updateGallery();
|
||||
updateGalleryDots();
|
||||
|
||||
if (modalOverlay) modalOverlay.classList.add('active');
|
||||
|
||||
|
|
@ -176,16 +223,60 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
|
||||
// Gallery helpers
|
||||
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() {
|
||||
const slides = gallerySlides ? Array.from(gallerySlides.children) : [];
|
||||
slides.forEach((s, i) => {
|
||||
const visible = getVisibleSlides();
|
||||
const all = gallerySlides ? Array.from(gallerySlides.children) : [];
|
||||
// Cache toutes les slides non visibles
|
||||
all.forEach(s => {
|
||||
if (!visible.includes(s)) { s.style.display = 'none'; }
|
||||
else { s.style.display = ''; }
|
||||
});
|
||||
// Positionne les slides visibles
|
||||
visible.forEach((s, i) => {
|
||||
s.style.transform = `translateX(${(i - galleryIndex) * 100}%)`;
|
||||
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 nextSlide() { const slides = gallerySlides ? gallerySlides.children.length : 0; galleryIndex = Math.min(slides - 1, galleryIndex + 1); updateGallery(); }
|
||||
function updateGalleryDots() {
|
||||
const galleryDots = document.getElementById('gallery-dots');
|
||||
if (!galleryDots) return;
|
||||
// Attend que les images se chargent avant de créer les dots
|
||||
setTimeout(() => {
|
||||
const visible = getVisibleSlides();
|
||||
galleryDots.innerHTML = visible.map((_, i) =>
|
||||
`<div class="gallery-dot${i === 0 ? ' active' : ''}" data-idx="${i}"></div>`
|
||||
).join('');
|
||||
galleryDots.querySelectorAll('.gallery-dot').forEach(dot => {
|
||||
dot.addEventListener('click', () => {
|
||||
galleryIndex = parseInt(dot.dataset.idx);
|
||||
updateGallery();
|
||||
galleryDots.querySelectorAll('.gallery-dot').forEach((d, i) => d.classList.toggle('active', i === galleryIndex));
|
||||
});
|
||||
});
|
||||
}, 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
|
||||
filterTabs.forEach(tab => tab.addEventListener('click', (e) => {
|
||||
|
|
@ -222,6 +313,60 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
window.openProjectModal = openProjectModal;
|
||||
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
|
||||
renderProjects();
|
||||
renderPagination();
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@
|
|||
</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) {
|
||||
return ctas.map(c => `
|
||||
<a href="${c.href}" class="btn btn-${c.style}">
|
||||
|
|
@ -97,6 +102,9 @@
|
|||
<div class="service-tab-features">
|
||||
${renderFeatures(service.features, accentFrom, accentTo)}
|
||||
</div>
|
||||
<div class="service-tab-note">
|
||||
${renderNote(service.note)}
|
||||
</div>
|
||||
<div class="service-tab-cta">
|
||||
${renderCtas(service.cta)}
|
||||
</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="pages/services.html">Services</a>
|
||||
<a href="pages/realisations.html">Réalisations</a>
|
||||
<a href="pages/faq.html">FAQ</a>
|
||||
<a href="#contact">Contact</a>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
|
|
@ -28,9 +29,34 @@
|
|||
<a href="pages/cgv.html">CGV</a>
|
||||
</div>
|
||||
<div class="footer-social">
|
||||
<a href="#" aria-label="Discord"><i data-feather="message-circle"></i></a>
|
||||
<a href="#" aria-label="Twitter"><i data-feather="twitter"></i></a>
|
||||
<a href="#" aria-label="Instagram"><i data-feather="instagram"></i></a>
|
||||
<!-- Discord -->
|
||||
<a href="https://discord.gg/FwvNkvu5dy" target="_blank" rel="noopener noreferrer" aria-label="Discord">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" width="20" height="20"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
|
||||
</a>
|
||||
<!-- 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 class="footer-bottom">
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
<div class="stat-label">Joueurs actifs</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">3</div>
|
||||
<div class="stat-label">Ans d'expérience</div>
|
||||
<div class="stat-number" id="experienceYears"></div>
|
||||
<div class="stat-label">Ans d'exp.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -33,4 +33,10 @@
|
|||
</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">
|
||||
<i data-feather="code"></i>
|
||||
</div>
|
||||
<h3>Bots Discord RP</h3>
|
||||
<p>Des bots entièrement personnalisés pour animer et gérer vos serveurs RP.</p>
|
||||
<h3>Bots Discord</h3>
|
||||
<p>Des bots entièrement personnalisés pour animer et gérer touts vos serveurs.</p>
|
||||
<ul class="service-features">
|
||||
<li>Systèmes d'économie avancés</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>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">3</div>
|
||||
<div class="stat-label">Ans d'expérience</div>
|
||||
<div class="stat-number" id="experienceYears">3</div>
|
||||
<div class="stat-label">Ans d'exp.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -78,6 +78,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</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 -->
|
||||
<!-- <div data-include="components/index/services.html"></div> -->
|
||||
|
|
@ -92,8 +98,8 @@
|
|||
<div class="service-icon">
|
||||
<i data-feather="code"></i>
|
||||
</div>
|
||||
<h3>Bots Discord RP</h3>
|
||||
<p>Des bots entièrement personnalisés pour animer et gérer vos serveurs RP.</p>
|
||||
<h3>Bots Discord</h3>
|
||||
<p>Des bots entièrement personnalisés pour animer et gérer touts vos serveurs.</p>
|
||||
<ul class="service-features">
|
||||
<li>Systèmes d'économie avancés</li>
|
||||
<li>Gestion des stats et inventaires</li>
|
||||
|
|
@ -185,31 +191,8 @@
|
|||
<h2 class="section-title">Nos réalisations</h2>
|
||||
<p class="section-desc">Découvrez quelques-uns de nos projets récents</p>
|
||||
</div>
|
||||
<div class="portfolio-grid">
|
||||
<div class="portfolio-item reveal">
|
||||
<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 class="portfolio-grid" id="home-portfolio-grid">
|
||||
<!-- Rendu dynamique depuis assets/data/projects.json par home.js -->
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 3rem;" class="reveal">
|
||||
<a href="pages/realisations.html" class="btn btn-secondary">
|
||||
|
|
@ -219,6 +202,63 @@
|
|||
</div>
|
||||
</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 -->
|
||||
<!-- <div data-include="components/index/contact.html"></div> -->
|
||||
<section class="contact" id="contact">
|
||||
|
|
@ -240,7 +280,8 @@
|
|||
</a>
|
||||
<a href="https://discord.gg/FwvNkvu5dy" class="contact-link">
|
||||
<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>
|
||||
<span>Discord : OmégaKube</span>
|
||||
</a>
|
||||
|
|
@ -347,7 +388,9 @@
|
|||
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',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
|
|
@ -376,7 +419,41 @@
|
|||
|
||||
<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/pages/realisations.js" defer></script>
|
||||
<script src="assets/js/home.js" defer></script>
|
||||
</body>
|
||||
</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>
|
||||
<html lang="fr" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 — Page non trouvée | OmégaKube</title>
|
||||
<template data-include="/testarea/omegakube/v1bis/components/favicons.html"></template>
|
||||
<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">
|
||||
<link rel="stylesheet" href="<?php echo $base; ?>/assets/css/404.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-grid"></div>
|
||||
|
|
@ -32,21 +33,31 @@
|
|||
|
||||
<div class="container">
|
||||
<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 class="error-code">404</div>
|
||||
<h1 class="error-title">Dimension introuvable</h1>
|
||||
<p class="error-message">
|
||||
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 ?
|
||||
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 ?
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<a href="/testarea/omegakube/v1bis/index.html" class="btn btn-primary">
|
||||
<i data-feather="home"></i>
|
||||
<a href="<?php echo $base; ?>/index.html" class="btn btn-primary">
|
||||
<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
|
||||
</a>
|
||||
<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
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -55,9 +66,9 @@
|
|||
<div class="footer">
|
||||
<span class="logo-mini">ΩK</span>
|
||||
<span>•</span>
|
||||
<a href="/testarea/omegakube/v1bis/index.html">OmégaKube</a>
|
||||
<a href="<?php echo $base; ?>/index.html">OmégaKube</a>
|
||||
</div>
|
||||
|
||||
<script src="/testarea/omegakube/v1bis/assets/js/pages/404.js"></script>
|
||||
<script src="<?php echo $base; ?>/assets/js/pages/404.js"></script>
|
||||
</body>
|
||||
</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>
|
||||
</section>
|
||||
|
||||
<!-- Projets Phares - Bento Grid -->
|
||||
<!-- Projets Phares - Bento Grid (dynamique depuis projects.json) -->
|
||||
<section style="padding-top: 2rem;">
|
||||
<div class="section-header reveal">
|
||||
<p class="section-tag">En vedette</p>
|
||||
|
|
@ -78,169 +78,8 @@
|
|||
<p class="section-desc">Nos réalisations les plus marquantes</p>
|
||||
</div>
|
||||
|
||||
<div class="bento-grid" style="padding: 0 2rem;">
|
||||
|
||||
<!-- 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 class="bento-grid" style="padding: 0 2rem;" id="featured-bento-grid">
|
||||
<!-- Rendu dynamique depuis ../assets/data/projects.json par realisations.js -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<!-- Baground géométrique -->
|
||||
<div data-include="../components\background.html"></div>
|
||||
<div data-include="../components/background.html"></div>
|
||||
|
||||
<!-- Header / Menu -->
|
||||
<div data-include="../components/header.html"></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue