commit c38fde40b06e52de951cf448dccd64ab96d5f77e Author: Aboubacar M'houmadi Date: Fri Feb 6 18:36:03 2026 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f34793 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Ignore all Markdown files +*.md diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..7e619f0 --- /dev/null +++ b/.htaccess @@ -0,0 +1,5 @@ +# Sécurisation minimale +Options -Indexes + +# Page 404 personnalisée +ErrorDocument 404 omegakube/pages/404.html diff --git a/assets/css/404.css b/assets/css/404.css new file mode 100644 index 0000000..28adba4 --- /dev/null +++ b/assets/css/404.css @@ -0,0 +1,331 @@ +:root { + --bg-deep: #0a0a0f; + --bg-primary: #12121a; + --bg-secondary: #1a1a25; + --accent-primary: #8b3a62; + --accent-secondary: #5e2a8b; + --accent-light: #d4a5c9; + --text-primary: #ffffff; + --text-secondary: #b8b8c8; + --text-muted: #6b6b7b; + --glass-bg: rgba(255, 255, 255, 0.03); + --glass-border: rgba(255, 255, 255, 0.08); +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + font-family: 'Montserrat', sans-serif; + background: var(--bg-deep); + color: var(--text-primary); + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + overflow: hidden; + position: relative; +} + +.bg-grid { + position: fixed; + inset: 0; + background-image: + linear-gradient(rgba(139, 58, 98, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(139, 58, 98, 0.03) 1px, transparent 1px); + background-size: 50px 50px; + pointer-events: none; +} + +.bg-glow { + position: fixed; + width: 600px; + height: 600px; + border-radius: 50%; + filter: blur(150px); + opacity: 0.15; + pointer-events: none; +} + +.bg-glow-1 { + top: -200px; + left: -200px; + background: var(--accent-primary); + animation: float 8s ease-in-out infinite; +} + +.bg-glow-2 { + bottom: -200px; + right: -200px; + background: var(--accent-secondary); + animation: float 8s ease-in-out infinite reverse; +} + +@keyframes float { + 0%, 100% { transform: translate(0, 0); } + 50% { transform: translate(30px, 30px); } +} + +.particles { + position: fixed; + inset: 0; + pointer-events: none; + overflow: hidden; +} + +.particle { + position: absolute; + width: 4px; + height: 4px; + background: var(--accent-light); + border-radius: 50%; + opacity: 0.3; + animation: rise 10s linear infinite; +} + +@keyframes rise { + 0% { transform: translateY(100vh) scale(0); opacity: 0; } + 10% { opacity: 0.3; } + 90% { opacity: 0.3; } + 100% { transform: translateY(-100vh) scale(1); opacity: 0; } +} + +.container { + text-align: center; + z-index: 10; + padding: 2rem; + max-width: 600px; +} + +.error-code { + font-size: clamp(8rem, 25vw, 14rem); + font-weight: 700; + line-height: 1; + position: relative; + margin-bottom: 1rem; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary), var(--accent-light)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: glitch 3s infinite; + cursor: pointer; + user-select: none; + transition: transform 0.3s ease, filter 0.3s ease; +} + +.error-code:hover { + transform: scale(1.05); + filter: brightness(1.2); +} + +.error-code:active { + transform: scale(0.98); +} + +.error-code::before, +.error-code::after { + content: '404'; + position: absolute; + top: 0; + left: 0; + right: 0; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + opacity: 0.8; +} + +.error-code::before { + animation: glitch-1 0.3s infinite linear alternate-reverse; + clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%); +} + +.error-code::after { + animation: glitch-2 0.3s infinite linear alternate-reverse; + clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%); +} + +@keyframes glitch { + 0%, 90%, 100% { transform: translate(0); } + 92% { transform: translate(-2px, 2px); } + 94% { transform: translate(2px, -2px); } + 96% { transform: translate(-2px, -2px); } + 98% { transform: translate(2px, 2px); } +} + +@keyframes glitch-1 { + 0% { transform: translate(0); } + 100% { transform: translate(-3px, 1px); } +} + +@keyframes glitch-2 { + 0% { transform: translate(0); } + 100% { transform: translate(3px, -1px); } +} + +.error-icon { + width: 80px; + height: 80px; + margin: 0 auto 2rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + animation: pulse-icon 2s ease-in-out infinite; +} + +.error-icon svg { + width: 40px; + height: 40px; + color: var(--accent-light); +} + +@keyframes pulse-icon { + 0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(139, 58, 98, 0.4); } + 50% { transform: scale(1.05); box-shadow: 0 0 0 15px rgba(139, 58, 98, 0); } +} + +.error-title { + font-size: 1.75rem; + font-weight: 600; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.error-message { + font-size: 1rem; + color: var(--text-muted); + line-height: 1.7; + margin-bottom: 2.5rem; +} + +.error-message span { + color: var(--accent-light); +} + +.buttons { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; +} + +.btn { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.875rem 1.75rem; + border-radius: 8px; + font-size: 0.95rem; + font-weight: 600; + text-decoration: none; + transition: all 0.3s ease; + cursor: pointer; + border: none; +} + +.btn svg { width: 18px; height: 18px; } + +.btn-primary { + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + color: white; + box-shadow: 0 4px 20px rgba(139, 58, 98, 0.3); +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 30px rgba(139, 58, 98, 0.4); +} + +.btn-secondary { + background: var(--glass-bg); + border: 1px solid var(--glass-border); + color: var(--text-secondary); +} + +.btn-secondary:hover { + background: rgba(255, 255, 255, 0.08); + border-color: var(--accent-primary); + color: var(--text-primary); +} + +.portal { + position: absolute; + width: 300px; + height: 300px; + pointer-events: none; + opacity: 0.1; +} + +.portal-left { left: -100px; top: 50%; transform: translateY(-50%); } +.portal-right { right: -100px; top: 50%; transform: translateY(-50%); } + +.portal-ring { + position: absolute; + inset: 0; + border: 2px solid var(--accent-primary); + border-radius: 50%; + animation: rotate 10s linear infinite; +} + +.portal-ring:nth-child(2) { inset: 20px; border-color: var(--accent-secondary); animation-duration: 8s; animation-direction: reverse; } +.portal-ring:nth-child(3) { inset: 40px; border-color: var(--accent-light); animation-duration: 6s; } + +@keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } + +.footer { + position: fixed; + bottom: 2rem; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-muted); + font-size: 0.85rem; + z-index: 10; +} + +.footer a { color: var(--accent-light); text-decoration: none; transition: color 0.3s ease; } +.footer a:hover { color: var(--text-primary); } + +.logo-mini { + font-weight: 700; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Responsive 404 */ +@media (max-width: 768px) { + .error-code { font-size: clamp(5rem, 20vw, 8rem); } + .error-icon { width: 60px; height: 60px; margin-bottom: 1.5rem; } + .error-icon svg { width: 30px; height: 30px; } + .error-title { font-size: 1.4rem; } + .error-message { font-size: 0.9rem; padding: 0 1rem; } + .buttons { flex-direction: column; width: 100%; padding: 0 1rem; } + .btn { width: 100%; justify-content: center; padding: 1rem 1.5rem; } + .portal { display: none; } + .footer { bottom: 1rem; font-size: 0.75rem; } +} + +@media (max-width: 480px) { + .container { padding: 1rem; } + .error-code { font-size: clamp(4rem, 18vw, 6rem); margin-bottom: 0.5rem; } + .error-icon { width: 50px; height: 50px; margin-bottom: 1rem; } + .error-icon svg { width: 24px; height: 24px; } + .error-title { font-size: 1.2rem; } + .error-message { font-size: 0.85rem; margin-bottom: 2rem; } + .btn { font-size: 0.9rem; padding: 0.875rem 1.25rem; } +} + +@media (max-height: 600px) { + .error-icon { display: none; } + .error-code { margin-bottom: 0.5rem; } + .error-title { font-size: 1.2rem; margin-bottom: 0.5rem; } + .error-message { margin-bottom: 1.5rem; } +} \ No newline at end of file diff --git a/assets/css/legal.css b/assets/css/legal.css new file mode 100644 index 0000000..3322083 --- /dev/null +++ b/assets/css/legal.css @@ -0,0 +1,33 @@ +/* assets/css/legal.css - minimal rules for legal pages */ + +/* Layout background patterns */ +.geo-bg { position:absolute; inset:0; background:radial-gradient(#0f1724 0%, transparent 40%); z-index:0; } +.grid-pattern { position:absolute; inset:0; background-image: repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 20px); z-index:0; } + +/* Hero */ +.page-hero { display:flex; align-items:center; justify-content:center; padding:3rem 1rem; background:var(--hero-bg); color:var(--text-on-hero); } +.page-hero .hero-content { max-width:1100px; text-align:center; } + +/* Legal content (text-first layout) */ +.legal-content { padding:2.25rem 1rem; background:transparent; color:var(--text); } +.legal-container { max-width:900px; margin:0 auto; display:block; } +.legal-section { margin-bottom:1.25rem; } +.legal-section h2 { font-size:1.1rem; margin:0 0 .5rem 0; font-weight:700; display:block; } +.legal-card { background:transparent; padding:0; border-radius:0; box-shadow:none; border:none; } +.legal-card p, .legal-card ul { margin:0 0 .75rem 0; line-height:1.6; color:var(--text); } +.page-hero { padding:1.5rem 1rem; background:transparent; color:var(--text); text-align:left; } +.page-hero .hero-content h1 { font-size:1.6rem; margin-bottom:.25rem; } +.page-hero .hero-content p { margin:0 0 .5rem 0; } + + +/* Footer */ +footer .footer-content { display:flex; gap:1rem; flex-wrap:wrap; align-items:center; } +.footer-bottom { text-align:center; padding:1rem 0; font-size:.9rem; } + +/* Nav / responsive */ +#navbar.scrolled { backdrop-filter: blur(4px); box-shadow:0 6px 18px rgba(2,6,23,.12); } +.nav-links { display:flex; gap:1rem; list-style:none; } +.nav-links a.active, .nav-links a[aria-current="true"] { font-weight:700; color:var(--accent); } +.nav-links.active { display:block; } +.mobile-toggle { display:none; /* show via media query */ } +@media (max-width:880px) { .nav-links { display:none; } .mobile-toggle { display:block; } } diff --git a/assets/css/main.css b/assets/css/main.css new file mode 100644 index 0000000..255ab96 --- /dev/null +++ b/assets/css/main.css @@ -0,0 +1,3552 @@ +/* ============================================ + OmégaKube - Styles Globaux + ============================================ */ + +/* Custom Fonts */ +@font-face { + font-family: 'OmegaLarge'; + src: url('../fonts/custom_font_large.otf') format('opentype'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'OmegaSmall'; + src: url('../fonts/custom_font_small.otf') format('opentype'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +/* ============================================ + Variables & Thèmes + ============================================ */ +:root { + --bg-deep: #1a0f1f; + --bg-secondary: #251528; + --bg-tertiary: #2d1b33; + --accent-primary: #8b3a62; + --accent-secondary: #a64d79; + --accent-light: #c77daa; + --accent-glow: #d4a5c9; + --text-primary: #ffffff; + --text-secondary: #c9b8d0; + --text-muted: #8a7a91; + --glass-bg: rgba(45, 27, 51, 0.7); + --glass-border: rgba(167, 77, 121, 0.3); + --card-bg: rgba(37, 21, 40, 0.8); + --input-bg: #1a0f1f; + --shadow-color: rgba(139, 58, 98, 0.3); + --shape-color: rgba(255, 255, 255, 0.08); + --shape-border: rgba(255, 255, 255, 0.15); +} + +[data-theme="light"] { + --bg-deep: #faf7fb; + --bg-secondary: #f3edf5; + --bg-tertiary: #ebe3ee; + --accent-primary: #8b3a62; + --accent-secondary: #a64d79; + --accent-light: #c77daa; + --accent-glow: #8b3a62; + --text-primary: #1a0f1f; + --text-secondary: #4a3a52; + --text-muted: #6a5a72; + --glass-bg: rgba(255, 255, 255, 0.9); + --glass-border: rgba(139, 58, 98, 0.2); + --card-bg: rgba(255, 255, 255, 0.95); + --input-bg: #ffffff; + --shadow-color: rgba(139, 58, 98, 0.15); + --shape-color: rgba(139, 58, 98, 0.06); + --shape-border: rgba(139, 58, 98, 0.12); +} + +/* ============================================ + Reset & Base + ============================================ */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'OmegaSmall', 'Segoe UI', sans-serif; + background: var(--bg-deep); + color: var(--text-primary); + overflow-x: hidden; + line-height: 1.6; + transition: background 0.4s ease, color 0.4s ease; +} + +::-webkit-scrollbar { + width: 8px; +} +::-webkit-scrollbar-track { + background: var(--bg-deep); +} +::-webkit-scrollbar-thumb { + background: var(--accent-primary); + border-radius: 4px; +} + +/* ============================================ + Background Effects + ============================================ */ +.geo-bg { + position: fixed; + inset: 0; + z-index: -2; + background: linear-gradient(135deg, var(--bg-deep) 0%, var(--bg-secondary) 50%, var(--bg-tertiary) 100%); +} + +.grid-pattern { + position: fixed; + inset: 0; + z-index: -1; + background-image: radial-gradient(circle, var(--shape-border) 1px, transparent 1px); + background-size: 40px 40px; + opacity: 0.5; +} + +.geo-shapes { + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; + overflow: hidden; +} + +.shape { + position: absolute; + opacity: 0.6; + animation: floatShape 20s ease-in-out infinite; +} + +.shape-triangle { + width: 0; + height: 0; + border-left: 30px solid transparent; + border-right: 30px solid transparent; + border-bottom: 52px solid var(--shape-color); +} + +.shape-triangle-outline { + width: 60px; + height: 52px; + background: transparent; + border: 2px solid var(--shape-border); + clip-path: polygon(50% 0%, 0% 100%, 100% 100%); +} + +.shape-square { + width: 40px; + height: 40px; + background: var(--shape-color); +} + +.shape-square-outline { + width: 50px; + height: 50px; + background: transparent; + border: 2px solid var(--shape-border); +} + +.shape-rect { + width: 80px; + height: 30px; + background: var(--shape-color); +} + +.shape-diamond { + width: 40px; + height: 40px; + background: var(--shape-color); + transform: rotate(45deg); +} + +.shape-dots { + display: grid; + grid-template-columns: repeat(4, 8px); + grid-template-rows: repeat(4, 8px); + gap: 6px; +} + +.shape-dots span { + width: 4px; + height: 4px; + background: var(--shape-border); + border-radius: 50%; +} + +.shape:nth-child(1) { top: 10%; left: 5%; animation-delay: 0s; } +.shape:nth-child(2) { top: 20%; right: 10%; animation-delay: -3s; } +.shape:nth-child(3) { top: 60%; left: 8%; animation-delay: -6s; } +.shape:nth-child(4) { top: 75%; right: 5%; animation-delay: -9s; } +.shape:nth-child(5) { top: 40%; left: 3%; animation-delay: -12s; } +.shape:nth-child(6) { top: 85%; left: 15%; animation-delay: -15s; } +.shape:nth-child(7) { top: 15%; right: 20%; animation-delay: -4s; } +.shape:nth-child(8) { top: 50%; right: 3%; animation-delay: -8s; } +.shape:nth-child(9) { top: 30%; left: 12%; animation-delay: -11s; } +.shape:nth-child(10) { top: 90%; right: 15%; animation-delay: -14s; } + +@keyframes floatShape { + 0%, 100% { transform: translateY(0) rotate(0deg); } + 25% { transform: translateY(-20px) rotate(5deg); } + 50% { transform: translateY(-10px) rotate(-3deg); } + 75% { transform: translateY(-25px) rotate(3deg); } +} + +/* ============================================ + Navigation + ============================================ */ +nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + padding: 1.5rem 2rem; + transition: all 0.4s ease; +} + +nav.scrolled { + background: var(--glass-bg); + backdrop-filter: blur(20px); + border-bottom: 1px solid var(--glass-border); + padding: 1rem 2rem; +} + +.nav-container { + max-width: 1400px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.6rem; + text-decoration: none; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 0.75rem; + transition: color 0.3s ease; +} + +.logo-icon { + width: 45px; + height: 45px; + flex-shrink: 0; +} + +.logo-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.logo-text { + display: flex; + align-items: baseline; + gap: 0.15rem; +} + +.logo-text .omega { color: var(--text-primary); } +.logo-text .kube { color: var(--accent-light); } +.logo-short { display: none; } + +.nav-right { + display: flex; + align-items: center; + gap: 2rem; +} + +.nav-links { + display: flex; + gap: 2.5rem; + list-style: none; +} + +.nav-links a { + color: var(--text-secondary); + text-decoration: none; + font-weight: 500; + font-size: 0.95rem; + transition: color 0.3s ease; + position: relative; +} + +.nav-links a::after { + content: ''; + position: absolute; + bottom: -5px; + left: 0; + width: 0; + height: 2px; + background: var(--accent-primary); + transition: width 0.3s ease; +} + +.nav-links a:hover, +.nav-links a.active { + color: var(--text-primary); +} + +.nav-links a:hover::after, +.nav-links a.active::after { + width: 100%; +} + +/* Theme Toggle */ +.theme-toggle { + width: 56px; + height: 30px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 50px; + cursor: pointer; + position: relative; + transition: all 0.3s ease; + display: flex; + align-items: center; + padding: 4px; + flex-shrink: 0; +} + +.theme-toggle:hover { + border-color: var(--accent-primary); +} + +.theme-toggle-thumb { + width: 22px; + height: 22px; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-radius: 50%; + transition: transform 0.3s ease; + box-shadow: 0 2px 10px var(--shadow-color); +} + +[data-theme="light"] .theme-toggle-thumb { + transform: translateX(26px); +} + +.theme-toggle .icon-moon, +.theme-toggle .icon-sun { + position: absolute; + width: 14px; + height: 14px; + transition: opacity 0.3s ease; +} + +.theme-toggle .icon-moon { + left: 8px; + opacity: 1; + color: var(--text-muted); +} + +.theme-toggle .icon-sun { + right: 8px; + opacity: 0.4; + color: var(--text-muted); +} + +[data-theme="light"] .theme-toggle .icon-moon { opacity: 0.4; } +[data-theme="light"] .theme-toggle .icon-sun { opacity: 1; } + +.mobile-toggle { + display: none; + flex-direction: column; + gap: 5px; + cursor: pointer; + padding: 5px; + flex-shrink: 0; +} + +.mobile-toggle span { + width: 25px; + height: 2px; + background: var(--text-primary); + transition: all 0.3s ease; +} + +/* Responsive: show mobile toggle and hide nav links on small screens */ +@media (max-width:880px) { + .nav-links { + display: none; + position: absolute; + right: 2rem; + top: 100%; + background: var(--glass-bg); + padding: 0.75rem 1rem; + border-radius: 8px; + box-shadow: 0 6px 18px rgba(2,6,23,.12); + flex-direction: column; + gap: 1rem; + width: auto; + min-width: 160px; + z-index: 1002; + } + .nav-links.active { display: block; } + .mobile-toggle { display: flex; } + + /* Debug helpers: make elements clearly visible during testing */ + /* .mobile-toggle { outline: 2px solid rgba(255,0,0,0.9) !important; } + .nav-links { outline: 2px dashed rgba(0,128,255,0.9) !important; } + .nav-links { z-index: 1100 !important; } */ +} + +/* ============================================ + Hero Sections + ============================================ */ +.hero { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + position: relative; + padding: 6rem 2rem 4rem; +} + +.page-hero { + min-height: 50vh; + display: flex; + align-items: center; + justify-content: center; + position: relative; + padding: 8rem 2rem 4rem; + text-align: center; +} + +.hero-content { + text-align: center; + max-width: 900px; + z-index: 1; +} + +.hero-badge { + display: inline-flex; + align-items: center; + gap: 0.5rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + padding: 0.5rem 1.25rem; + border-radius: 4px; + font-size: 0.85rem; + color: var(--accent-light); + margin-bottom: 2rem; + backdrop-filter: blur(10px); + animation: fadeInUp 0.8s ease forwards; +} + +.hero-badge::before { + content: ''; + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 10px solid var(--accent-light); +} + +.hero h1, +.page-hero h1 { + font-family: 'OmegaLarge', sans-serif; + font-size: clamp(3rem, 8vw, 5.5rem); + font-weight: normal; + line-height: 1.1; + margin-bottom: 1.5rem; + animation: fadeInUp 0.8s ease 0.1s forwards; + opacity: 0; +} + +.page-hero h1 { + font-size: clamp(2.5rem, 6vw, 4rem); + opacity: 1; + animation: fadeInUp 0.8s ease forwards; +} + +.hero h1 .highlight, +.page-hero h1 .highlight { + color: var(--accent-light); +} + +.hero p, +.page-hero p { + font-size: 1.25rem; + color: var(--text-secondary); + max-width: 600px; + margin: 0 auto 2.5rem; + animation: fadeInUp 0.8s ease 0.2s forwards; + opacity: 0; +} + +.page-hero p { + margin-bottom: 0; +} + +.hero-buttons { + display: flex; + gap: 1rem; + justify-content: center; + flex-wrap: wrap; + animation: fadeInUp 0.8s ease 0.3s forwards; + opacity: 0; +} + +/* ============================================ + Buttons + ============================================ */ +.btn { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 1rem 2rem; + border-radius: 4px; + font-family: 'OmegaSmall', sans-serif; + font-weight: 500; + font-size: 1rem; + text-decoration: none; + transition: all 0.3s ease; + cursor: pointer; + border: none; +} + +.btn-primary { + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + color: white; + box-shadow: 0 4px 20px var(--shadow-color); +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 30px var(--shadow-color); +} + +.btn-secondary { + background: var(--glass-bg); + color: var(--text-primary); + border: 1px solid var(--glass-border); + backdrop-filter: blur(10px); +} + +.btn-secondary:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ============================================ + Sections + ============================================ */ +section { + padding: 8rem 2rem; + position: relative; +} + +.section-header { + text-align: center; + max-width: 700px; + margin: 0 auto 5rem; +} + +.section-tag { + font-size: 0.85rem; + color: var(--accent-light); + text-transform: uppercase; + letter-spacing: 3px; + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.section-tag::before, +.section-tag::after { + content: ''; + width: 30px; + height: 1px; + background: var(--accent-primary); +} + +.section-title { + font-family: 'OmegaLarge', sans-serif; + font-size: clamp(2rem, 5vw, 3rem); + font-weight: normal; + margin-bottom: 1rem; +} + +.section-desc { + color: var(--text-secondary); + font-size: 1.1rem; +} + +/* ============================================ + About Section + ============================================ */ +.about { + background: linear-gradient(180deg, transparent 0%, var(--bg-secondary) 50%, transparent 100%); +} + +.about-grid { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; +} + +.about-content h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.5rem; + margin-bottom: 1.5rem; + color: var(--accent-light); +} + +.about-content p { + color: var(--text-secondary); + margin-bottom: 1.5rem; + font-size: 1.1rem; +} + +.about-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-top: 2rem; +} + +.stat-item { + text-align: center; + padding: 1.5rem; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + backdrop-filter: blur(10px); + position: relative; + overflow: hidden; +} + +.stat-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 3px; + background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); +} + +.stat-number { + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + color: var(--accent-light); +} + +.stat-label { + font-size: 0.85rem; + color: var(--text-muted); + margin-top: 0.25rem; +} + +.about-card { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + padding: 3rem; + backdrop-filter: blur(20px); + position: relative; + overflow: hidden; +} + +.about-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + background: linear-gradient(180deg, var(--accent-primary), var(--accent-secondary)); +} + +.about-card h4 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.25rem; + margin-bottom: 1rem; +} + +.about-card p { + color: var(--text-secondary); + line-height: 1.8; +} + +.deco-triangle { + position: absolute; + width: 0; + height: 0; + border-left: 20px solid transparent; + border-right: 20px solid transparent; + border-bottom: 35px solid var(--accent-primary); + opacity: 0.3; + bottom: 20px; + right: 20px; +} + +/* ============================================ + Services + ============================================ */ +.services-grid { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.service-card { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + padding: 2.5rem; + backdrop-filter: blur(10px); + transition: all 0.4s ease; + position: relative; + overflow: hidden; +} + +.service-card::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); + transform: scaleX(0); + transition: transform 0.4s ease; +} + +.service-card:hover { + transform: translateY(-10px); + border-color: var(--accent-primary); + box-shadow: 0 20px 40px var(--shadow-color); +} + +.service-card:hover::after { + transform: scaleX(1); +} + +.service-icon { + width: 70px; + height: 70px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.5rem; +} + +.service-icon::before { + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); +} + +.service-icon svg { + width: 28px; + height: 28px; + color: white; + position: relative; + z-index: 1; +} + +.service-card h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.25rem; + margin-bottom: 1rem; +} + +.service-card p { + color: var(--text-secondary); + margin-bottom: 1.5rem; +} + +.service-features { + list-style: none; +} + +.service-features li { + display: flex; + align-items: center; + gap: 0.75rem; + color: var(--text-secondary); + margin-bottom: 0.75rem; + font-size: 0.95rem; +} + +.service-features li::before { + content: ''; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 8px solid var(--accent-light); + flex-shrink: 0; +} + +/* Service Detail */ +.service-detail { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + padding: 3rem; + margin-bottom: 3rem; + position: relative; + overflow: hidden; +} + +.service-detail::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 4px; + background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); +} + +.service-detail-header { + display: flex; + align-items: center; + gap: 2rem; + margin-bottom: 2rem; +} + +.service-detail-icon { + width: 100px; + height: 100px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.service-detail-icon::before { + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); +} + +.service-detail-icon svg { + width: 40px; + height: 40px; + color: white; + position: relative; + z-index: 1; +} + +.service-detail h2 { + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + margin-bottom: 0.5rem; +} + +.service-detail h2 + p { + color: var(--text-secondary); + font-size: 1.1rem; +} + +.service-detail-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; +} + +.service-detail-content h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.25rem; + margin-bottom: 1rem; + color: var(--accent-light); +} + +.service-detail-content p { + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: 1.5rem; +} + +.service-detail-content ul { + list-style: none; +} + +.service-detail-content ul li { + display: flex; + align-items: center; + gap: 0.75rem; + color: var(--text-secondary); + margin-bottom: 0.75rem; +} + +.service-detail-content ul li::before { + content: ''; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 8px solid var(--accent-light); + flex-shrink: 0; +} + +/* ============================================ + Team Carousel + ============================================ */ +.team { + background: linear-gradient(180deg, transparent 0%, var(--bg-secondary) 50%, transparent 100%); +} + +.team-carousel-container { + max-width: 1200px; + margin: 0 auto; + position: relative; + overflow: hidden; + padding: 0.5rem 0; +} + +.team-carousel { + display: flex; + transition: transform 0.5s ease; +} + +.team-card { + flex: 0 0 calc(33.333% - 1.33rem); + margin: 0 1rem; + text-align: center; + padding: 2rem; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + backdrop-filter: blur(10px); + transition: all 0.4s ease; +} + +.team-card:hover { + border-color: var(--accent-primary); + box-shadow: 0 5px 20px var(--shadow-color); + cursor: pointer; +} + +.team-avatar { + width: 120px; + height: 120px; + margin: 0 auto 1.5rem; + position: relative; +} + +.team-avatar::before { + content: ''; + 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%); +} + +.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%); + display: flex; + align-items: center; + justify-content: center; + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + overflow: hidden; + transition: background 0.3s ease; +} + +.team-avatar-inner img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.team-card h4 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.1rem; + margin-bottom: 0.25rem; +} + +.team-role { + color: var(--accent-light); + font-size: 0.9rem; + margin-bottom: 1rem; +} + +.team-desc { + color: var(--text-muted); + font-size: 0.9rem; +} + +/* Carousel Controls */ +.carousel-controls { + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + margin-top: 2rem; +} + +.carousel-btn { + width: 50px; + height: 50px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s ease; + color: var(--text-primary); +} + +.carousel-btn:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.carousel-btn svg { + width: 24px; + height: 24px; +} + +.carousel-dots { + display: flex; + gap: 0.5rem; +} + +.carousel-dot { + width: 10px; + height: 10px; + background: var(--glass-border); + border-radius: 50%; + cursor: pointer; + transition: all 0.3s ease; +} + +.carousel-dot.active { + background: var(--accent-primary); + width: 30px; + border-radius: 5px; +} + +/* ============================================ + Portfolio + ============================================ */ +.portfolio-grid { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.portfolio-item { + position: relative; + border-radius: 4px; + overflow: hidden; + aspect-ratio: 4/3; + background: var(--card-bg); + border: 1px solid var(--glass-border); + cursor: pointer; + transition: all 0.4s ease; +} + +.portfolio-item:hover { + transform: scale(1.02); + box-shadow: 0 20px 40px var(--shadow-color); +} + +.portfolio-item.hidden { + display: none; +} + +.portfolio-bg { + position: absolute; + inset: 0; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + opacity: 0.4; +} + +.portfolio-content { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 2rem; + background: linear-gradient(180deg, transparent 0%, var(--bg-deep) 100%); +} + +.portfolio-tag { + font-size: 0.75rem; + color: var(--accent-light); + text-transform: uppercase; + letter-spacing: 2px; + margin-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.portfolio-tag::before { + content: ''; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 7px solid var(--accent-light); +} + +.portfolio-item h4 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.25rem; +} + +.portfolio-item p { + color: var(--text-muted); + font-size: 0.9rem; + margin-top: 0.5rem; +} + +/* Portfolio Detail */ +.portfolio-detail { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + overflow: hidden; + margin-bottom: 3rem; +} + +.portfolio-detail-image { + width: 100%; + height: 300px; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); +} + +.portfolio-detail-content { + padding: 2.5rem; +} + +.portfolio-detail-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 2rem; + flex-wrap: wrap; + gap: 1rem; +} + +.portfolio-detail h2 { + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + margin-bottom: 0.5rem; +} + +.portfolio-detail-meta { + display: flex; + gap: 2rem; + color: var(--text-muted); + font-size: 0.9rem; +} + +.portfolio-detail-meta span { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.portfolio-detail-meta svg { + width: 16px; + height: 16px; +} + +.portfolio-detail p { + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: 1.5rem; +} + +.portfolio-detail-features { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-top: 2rem; + list-style: none; +} + +.portfolio-detail-features li { + display: flex; + align-items: center; + gap: 0.75rem; + color: var(--text-secondary); +} + +.portfolio-detail-features li::before { + content: ''; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 8px solid var(--accent-light); + flex-shrink: 0; +} + +/* ============================================ + Contact + ============================================ */ +.contact { + background: linear-gradient(180deg, transparent 0%, var(--bg-secondary) 100%); +} + +.contact-wrapper { + max-width: 1000px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; +} + +.contact-info h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.5rem; + margin-bottom: 1.5rem; +} + +.contact-info p { + color: var(--text-secondary); + margin-bottom: 2rem; + font-size: 1.1rem; +} + +.contact-links { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.contact-link { + display: flex; + align-items: center; + gap: 1rem; + color: var(--text-secondary); + text-decoration: none; + transition: color 0.3s ease; +} + +.contact-link:hover { + color: var(--accent-light); +} + +.contact-link-icon { + width: 50px; + height: 50px; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.contact-link:hover .contact-link-icon { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.contact-form { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + padding: 2.5rem; + backdrop-filter: blur(10px); + position: relative; +} + +.contact-form::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 4px; + background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; + color: var(--text-secondary); +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 1rem; + background: var(--input-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + color: var(--text-primary); + font-family: 'OmegaSmall', sans-serif; + font-size: 1rem; + transition: all 0.3s ease; +} + +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--accent-primary); + box-shadow: 0 0 0 3px var(--shadow-color); +} + +.form-group textarea { + resize: vertical; + min-height: 120px; +} + +.form-submit { + width: 100%; +} + +/* ============================================ + Footer + ============================================ */ +footer { + padding: 4rem 2rem 2rem; + border-top: 1px solid var(--glass-border); +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 2rem; +} + +.footer-links { + display: flex; + gap: 2rem; +} + +.footer-links a { + color: var(--text-muted); + text-decoration: none; + transition: color 0.3s ease; +} + +.footer-links a:hover { + color: var(--text-primary); +} + +.footer-social { + display: flex; + gap: 1rem; +} + +.footer-social a { + width: 44px; + height: 44px; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.footer-social a:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +.footer-bottom { + max-width: 1200px; + margin: 2rem auto 0; + padding-top: 2rem; + border-top: 1px solid var(--glass-border); + text-align: center; + color: var(--text-muted); + font-size: 0.9rem; +} + +/* ============================================ + Animations + ============================================ */ +.reveal { + opacity: 0; + transform: translateY(40px); + transition: all 0.8s ease; +} + +.reveal.active { + opacity: 1; + transform: translateY(0); +} + +.reveal-delay-1 { transition-delay: 0.1s; } +.reveal-delay-2 { transition-delay: 0.2s; } +.reveal-delay-3 { transition-delay: 0.3s; } + +/* ============================================ + Responsive + ============================================ */ +@media (max-width: 1024px) { + .services-grid, + .portfolio-grid { + grid-template-columns: repeat(2, 1fr); + } + + .team-card { + flex: 0 0 calc(50% - 1rem); + } +} + +@media (max-width: 768px) { + nav { + padding: 1rem; + } + + nav.scrolled { + padding: 0.75rem 1rem; + } + + .nav-links { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--glass-bg); + backdrop-filter: blur(20px); + flex-direction: column; + padding: 2rem; + gap: 1.5rem; + border-bottom: 1px solid var(--glass-border); + } + + .nav-links.active { + display: flex; + } + + .mobile-toggle { + display: flex; + } + + .mobile-toggle.active span:nth-child(1) { + transform: rotate(45deg) translate(5px, 5px); + } + + .mobile-toggle.active span:nth-child(2) { + opacity: 0; + } + + .mobile-toggle.active span:nth-child(3) { + transform: rotate(-45deg) translate(5px, -5px); + } + + .nav-right { + gap: 0.75rem; + } + + .logo-text .logo-full { + display: none; + } + + .logo-short { + display: flex !important; + align-items: baseline; + gap: 0.1rem; + } + + .logo-short .omega-symbol { + color: var(--text-primary); + } + + .logo-short .kube-short { + color: var(--accent-light); + } + + .logo-icon { + width: 35px; + height: 35px; + } + + .logo { + font-size: 1.3rem; + gap: 0.5rem; + } + + .about-grid, + .contact-wrapper { + grid-template-columns: 1fr; + gap: 3rem; + } + + .services-grid, + .portfolio-grid { + grid-template-columns: 1fr; + } + + .service-detail-content, + .portfolio-detail-features { + grid-template-columns: 1fr; + } + + .service-detail-header { + flex-direction: column; + text-align: center; + } + + .team-card { + flex: 0 0 calc(100% - 2rem); + } + + section { + padding: 5rem 1.5rem; + } + + .hero { + padding: 8rem 1.5rem 4rem; + } + + .footer-content { + flex-direction: column; + text-align: center; + } + + .footer-links { + flex-wrap: wrap; + justify-content: center; + } +} + +@media (max-width: 480px) { + .about-stats { + grid-template-columns: 1fr; + gap: 1rem; + } + + .theme-toggle { + width: 48px; + height: 26px; + } + + .theme-toggle-thumb { + width: 18px; + height: 18px; + } + + [data-theme="light"] .theme-toggle-thumb { + transform: translateX(22px); + } + + .carousel-controls { + flex-wrap: wrap; + } +} + +/* ============================================ + Team Member Modal + ============================================ */ +.team-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(10px); + z-index: 2000; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.team-modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.team-modal { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + max-width: 500px; + width: 100%; + position: relative; + transform: scale(0.9) translateY(20px); + transition: transform 0.3s ease; + overflow: hidden; +} + +.team-modal-overlay.active .team-modal { + transform: scale(1) translateY(0); +} + +.team-modal-header { + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + padding: 2rem; + text-align: center; + position: relative; +} + +.team-modal-close { + position: absolute; + top: 1rem; + right: 1rem; + width: 36px; + height: 36px; + background: rgba(255, 255, 255, 0.2); + border: none; + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: white; + transition: all 0.3s ease; +} + +.team-modal-close:hover { + background: rgba(255, 255, 255, 0.3); + transform: rotate(90deg); +} + +.team-modal-close svg { + width: 20px; + height: 20px; +} + +.team-modal-avatar { + width: 120px; + height: 120px; + margin: 0 auto; + position: relative; +} + +.team-modal-avatar::before { + content: ''; + 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%); +} + +.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%); + display: flex; + align-items: center; + justify-content: center; + font-family: 'OmegaLarge', sans-serif; + font-size: 2.5rem; + color: var(--text-primary); +} + +.team-modal-name { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.5rem; + color: white; + margin-top: 1rem; +} + +.team-modal-role { + color: rgba(255, 255, 255, 0.8); + font-size: 0.95rem; + margin-top: 0.25rem; +} + +.team-modal-content { + padding: 2rem; +} + +.team-modal-bio { + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: 1.5rem; +} + +.team-modal-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin-bottom: 1.5rem; +} + +.team-modal-stat { + text-align: center; + padding: 1rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; +} + +.team-modal-stat-value { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.25rem; + color: var(--accent-light); +} + +.team-modal-stat-label { + font-size: 0.75rem; + color: var(--text-muted); + margin-top: 0.25rem; +} + +.team-modal-skills { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.team-modal-skill { + padding: 0.5rem 1rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + font-size: 0.85rem; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.team-modal-skill:hover { + border-color: var(--accent-primary); + color: var(--accent-light); +} + +.team-modal-social { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid var(--glass-border); +} + +.team-modal-social a { + width: 40px; + height: 40px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.team-modal-social a:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +.team-modal-social a svg { + width: 18px; + height: 18px; +} + +@media (max-width: 480px) { + .team-modal-stats { + grid-template-columns: 1fr; + } + + .team-modal { + margin: 1rem; + } + + .team-modal-header { + padding: 1.5rem; + } + + .team-modal-content { + padding: 1.5rem; + } +} + +/* ============================================ + Pagination + ============================================ */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + margin-top: 4rem; + flex-wrap: wrap; +} + +.pagination-btn { + width: 44px; + height: 44px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: var(--text-secondary); + font-family: 'OmegaSmall', sans-serif; + font-size: 0.95rem; + transition: all 0.3s ease; +} + +.pagination-btn:hover:not(:disabled) { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +.pagination-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.pagination-btn.active { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +.pagination-btn svg { + width: 20px; + height: 20px; +} + +.pagination-arrow { + width: 50px; +} + +.pagination-ellipsis { + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + font-size: 1.2rem; + letter-spacing: 2px; +} + +.pagination-info { + width: 100%; + text-align: center; + margin-top: 1rem; + color: var(--text-muted); + font-size: 0.9rem; +} + +@media (max-width: 480px) { + .pagination-btn { + width: 38px; + height: 38px; + font-size: 0.85rem; + } + + .pagination-arrow { + width: 44px; + } +} + +/* ============================================ + Bento Grid - Projets Phares + ============================================ */ +.bento-grid { + display: grid; + grid-template-columns: repeat(12, 1fr); + grid-template-rows: auto; + gap: 1.5rem; + max-width: 1200px; + margin: 0 auto; +} + +.bento-item { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + overflow: hidden; + transition: all 0.4s ease; + position: relative; +} + +.bento-item:hover { + border-color: var(--accent-primary); + box-shadow: 0 20px 40px var(--shadow-color); + transform: translateY(-5px); +} + +/* Bento Project Layout */ +.bento-project { + grid-column: span 12; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto; + gap: 1.5rem; + padding: 0; + background: transparent; + border: none; +} + +.bento-project:hover { + transform: none; + box-shadow: none; +} + +.bento-project .bento-image { + grid-column: 1; + grid-row: 1 / 3; + min-height: 400px; + background-size: cover; + background-position: center; + border-radius: 8px; + position: relative; + overflow: hidden; +} + +.bento-project .bento-image::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(139, 58, 98, 0.3), rgba(45, 27, 51, 0.5)); +} + +.bento-project .bento-image::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 50%; + background: linear-gradient(to top, var(--bg-deep), transparent); +} + +.bento-image-content { + position: absolute; + bottom: 2rem; + left: 2rem; + right: 2rem; + z-index: 1; +} + +.bento-image-content .portfolio-tag { + margin-bottom: 0.75rem; +} + +.bento-image-content h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + margin-bottom: 0.5rem; +} + +.bento-image-content p { + color: var(--text-secondary); + font-size: 1rem; +} + +/* Stats Grid */ +.bento-stats { + grid-column: 2; + grid-row: 1; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.bento-stat { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + padding: 1.5rem; + text-align: center; + transition: all 0.3s ease; +} + +.bento-stat:hover { + border-color: var(--accent-primary); + background: var(--glass-bg); +} + +.bento-stat-icon { + width: 50px; + height: 50px; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; +} + +.bento-stat-icon svg { + width: 24px; + height: 24px; + color: white; +} + +.bento-stat-value { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.75rem; + color: var(--accent-light); + margin-bottom: 0.25rem; +} + +.bento-stat-label { + font-size: 0.85rem; + color: var(--text-muted); +} + +/* Info Card */ +.bento-info { + grid-column: 2; + grid-row: 2; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + padding: 2rem; + display: flex; + flex-direction: column; +} + +.bento-info h4 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.1rem; + color: var(--accent-light); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.bento-info h4::before { + content: ''; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 8px solid var(--accent-light); +} + +.bento-info p { + color: var(--text-secondary); + line-height: 1.7; + margin-bottom: 1.5rem; + flex-grow: 1; +} + +.bento-features { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.bento-feature { + padding: 0.4rem 0.8rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + font-size: 0.8rem; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.bento-feature:hover { + border-color: var(--accent-primary); + color: var(--accent-light); +} + +/* Tech Stack */ +.bento-tech { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + padding: 1.5rem; +} + +.bento-tech h4 { + font-family: 'OmegaLarge', sans-serif; + font-size: 0.9rem; + color: var(--accent-light); + margin-bottom: 1rem; +} + +.bento-tech-list { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.bento-tech-item { + padding: 0.5rem 1rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + font-size: 0.85rem; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 0.5rem; + transition: all 0.3s ease; +} + +.bento-tech-item:hover { + border-color: var(--accent-primary); + color: var(--accent-light); +} + +.bento-tech-item svg { + width: 16px; + height: 16px; +} + +/* Alternate Layout (Image Right) */ +.bento-project.reverse .bento-image { + grid-column: 2; + grid-row: 1 / 3; +} + +.bento-project.reverse .bento-stats { + grid-column: 1; + grid-row: 1; +} + +.bento-project.reverse .bento-info { + grid-column: 1; + grid-row: 2; +} + +/* Bento Divider */ +.bento-divider { + grid-column: span 12; + height: 1px; + background: linear-gradient(90deg, transparent, var(--glass-border), transparent); + margin: 2rem 0; +} + +@media (max-width: 1024px) { + .bento-project { + grid-template-columns: 1fr; + } + + .bento-project .bento-image { + grid-column: 1; + grid-row: 1; + min-height: 300px; + } + + .bento-project .bento-stats { + grid-column: 1; + grid-row: 2; + } + + .bento-project .bento-info { + grid-column: 1; + grid-row: 3; + } + + .bento-project.reverse .bento-image, + .bento-project.reverse .bento-stats, + .bento-project.reverse .bento-info { + grid-column: 1; + } + + .bento-project.reverse .bento-image { grid-row: 1; } + .bento-project.reverse .bento-stats { grid-row: 2; } + .bento-project.reverse .bento-info { grid-row: 3; } +} + +@media (max-width: 480px) { + .bento-stats { + grid-template-columns: 1fr; + } + + .bento-project .bento-image { + min-height: 250px; + } + + .bento-image-content h3 { + font-size: 1.5rem; + } +} + +/* ============================================ + Project Modal - Galerie + ============================================ */ +.project-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.9); + backdrop-filter: blur(10px); + z-index: 2000; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + +.project-modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.project-modal { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 12px; + max-width: 900px; + width: 100%; + max-height: 90vh; + overflow: hidden; + position: relative; + transform: scale(0.9) translateY(20px); + transition: transform 0.3s ease; + display: flex; + flex-direction: column; +} + +.project-modal-overlay.active .project-modal { + transform: scale(1) translateY(0); +} + +.project-modal-close { + position: absolute; + top: 1rem; + right: 1rem; + width: 44px; + height: 44px; + background: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: white; + transition: all 0.3s ease; + z-index: 10; +} + +.project-modal-close:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.project-modal-close svg { + width: 24px; + height: 24px; +} + +/* Gallery */ +.project-gallery { + position: relative; + width: 100%; + height: 350px; + overflow: hidden; +} + +.project-gallery-slides { + display: flex; + width: 100%; + height: 100%; + transition: transform 0.5s ease; +} + +.project-gallery-slide { + min-width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.project-gallery-slide::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(139, 58, 98, 0.4), rgba(45, 27, 51, 0.6)); +} + +.gallery-nav { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 44px; + height: 44px; + background: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: white; + transition: all 0.3s ease; + z-index: 5; +} + +.gallery-nav:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.gallery-nav svg { + width: 20px; + height: 20px; +} + +.gallery-nav.prev { left: 1rem; } +.gallery-nav.next { right: 1rem; } + +.gallery-dots { + position: absolute; + bottom: 1rem; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 0.5rem; + z-index: 5; +} + +.gallery-dot { + width: 10px; + height: 10px; + background: rgba(255, 255, 255, 0.4); + border-radius: 50%; + cursor: pointer; + transition: all 0.3s ease; +} + +.gallery-dot.active { + background: white; + width: 30px; + border-radius: 5px; +} + +/* Modal Content */ +.project-modal-content { + padding: 2rem; + overflow-y: auto; + flex-grow: 1; +} + +.project-modal-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 1.5rem; + flex-wrap: wrap; + gap: 1rem; +} + +.project-modal-header h2 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.75rem; + margin-bottom: 0.5rem; +} + +.project-modal-meta { + display: flex; + gap: 1.5rem; + color: var(--text-muted); + font-size: 0.9rem; +} + +.project-modal-meta span { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.project-modal-meta svg { + width: 16px; + height: 16px; +} + +.project-modal-desc { + color: var(--text-secondary); + line-height: 1.8; + margin-bottom: 2rem; +} + +/* Modal Tabs */ +.project-modal-tabs { + display: flex; + gap: 0.5rem; + margin-bottom: 1.5rem; + border-bottom: 1px solid var(--glass-border); + padding-bottom: 1rem; +} + +.project-modal-tab { + padding: 0.75rem 1.25rem; + background: transparent; + border: 1px solid var(--glass-border); + border-radius: 4px; + color: var(--text-secondary); + font-family: 'OmegaSmall', sans-serif; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.3s ease; +} + +.project-modal-tab:hover, +.project-modal-tab.active { + background: var(--accent-primary); + border-color: var(--accent-primary); + color: white; +} + +.project-modal-tab-content { + display: none; +} + +.project-modal-tab-content.active { + display: block; +} + +/* Features Grid */ +.project-features-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.project-feature-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 4px; + color: var(--text-secondary); + transition: all 0.3s ease; +} + +.project-feature-item:hover { + border-color: var(--accent-primary); +} + +.project-feature-item::before { + content: ''; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 8px solid var(--accent-light); + flex-shrink: 0; +} + +/* Tech Stack Grid */ +.project-tech-grid { + display: flex; + flex-wrap: wrap; + 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: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); +} + +@media (max-width: 768px) { + .project-modal { + max-height: 95vh; + margin: 0.5rem; + } + + .project-gallery { + height: 250px; + } + + .project-modal-content { + padding: 1.5rem; + } + + .project-features-grid, + .project-stats-grid { + grid-template-columns: 1fr; + } + + .project-modal-tabs { + flex-wrap: wrap; + } +} + +/* Portfolio Item Click State */ +.portfolio-item { + cursor: pointer; +} + +.portfolio-item::before { + content: ''; + position: absolute; + inset: 0; + background: transparent; + z-index: 1; + transition: background 0.3s ease; +} + +.portfolio-item:hover::before { + background: rgba(139, 58, 98, 0.1); +} + +.portfolio-item:active { + transform: scale(0.98); +} + +/* ============================================ + Process Timeline + ============================================ */ +.process-section { + padding: 6rem 2rem; + background: linear-gradient(180deg, transparent 0%, var(--bg-secondary) 50%, transparent 100%); +} + +.process-timeline { + max-width: 1000px; + margin: 0 auto; + display: flex; + justify-content: space-between; + position: relative; + padding: 2rem 0; +} + +.process-timeline::before { + content: ''; + position: absolute; + top: 50%; + left: 10%; + right: 10%; + height: 3px; + background: var(--glass-border); + transform: translateY(-50%); + z-index: 0; +} + +.process-timeline::after { + content: ''; + position: absolute; + top: 50%; + left: 10%; + height: 3px; + background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); + transform: translateY(-50%); + z-index: 1; + width: 0%; + transition: width 1s ease; +} + +.process-timeline.animated::after { + width: 80%; +} + +.process-step { + display: flex; + flex-direction: column; + align-items: center; + position: relative; + z-index: 2; + flex: 1; +} + +.process-step-number { + width: 70px; + height: 70px; + background: var(--card-bg); + border: 3px solid var(--glass-border); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-family: 'OmegaLarge', sans-serif; + font-size: 1.5rem; + color: var(--text-muted); + margin-bottom: 1.5rem; + transition: all 0.5s ease; + position: relative; +} + +.process-step-number::before { + content: ''; + position: absolute; + inset: -6px; + border-radius: 50%; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + opacity: 0; + z-index: -1; + transition: opacity 0.5s ease; +} + +.process-step.active .process-step-number { + border-color: var(--accent-primary); + color: var(--accent-light); + background: var(--bg-deep); +} + +.process-step.active .process-step-number::before { + opacity: 1; +} + +.process-step-icon { + position: absolute; + width: 30px; + height: 30px; + color: var(--text-muted); + transition: color 0.5s ease; +} + +.process-step.active .process-step-icon { + color: white; +} + +.process-step-content { + text-align: center; + max-width: 150px; +} + +.process-step-title { + font-family: 'OmegaLarge', sans-serif; + font-size: 1rem; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.process-step-desc { + font-size: 0.85rem; + color: var(--text-muted); + line-height: 1.5; +} + +@media (max-width: 768px) { + .process-timeline { + flex-direction: column; + gap: 2rem; + padding: 0; + } + + .process-timeline::before, + .process-timeline::after { + top: 10%; + bottom: 10%; + left: 35px; + right: auto; + width: 3px; + height: auto; + transform: none; + } + + .process-timeline::after { + height: 0%; + width: 3px; + } + + .process-timeline.animated::after { + height: 80%; + width: 3px; + } + + .process-step { + flex-direction: row; + gap: 1.5rem; + } + + .process-step-number { + margin-bottom: 0; + flex-shrink: 0; + } + + .process-step-content { + text-align: left; + max-width: none; + } +} + +/* ============================================ + Service Tabs + ============================================ */ +.service-tabs-section { + padding: 6rem 2rem; +} + +.service-tabs-container { + max-width: 1200px; + margin: 0 auto; +} + +.service-tabs-nav { + display: flex; + justify-content: center; + gap: 1rem; + margin-bottom: 3rem; + flex-wrap: wrap; +} + +.service-tab-btn { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 1.5rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + color: var(--text-secondary); + font-family: 'OmegaSmall', sans-serif; + font-size: 0.95rem; + cursor: pointer; + transition: all 0.3s ease; +} + +.service-tab-btn:hover { + border-color: var(--accent-primary); + color: var(--text-primary); +} + +.service-tab-btn.active { + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-color: transparent; + color: white; +} + +.service-tab-btn svg { + width: 20px; + height: 20px; +} + +/* Service Tags / Stamps */ +.service-stamp { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.3rem 0.6rem; + font-family: 'OmegaLarge', sans-serif; + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: 4px; + font-weight: bold; + transform: rotate(12deg); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + animation: stampPop 0.4s ease; +} + +@keyframes stampPop { + 0% { transform: rotate(12deg) scale(0); opacity: 0; } + 50% { transform: rotate(12deg) scale(1.2); } + 100% { transform: rotate(12deg) scale(1); opacity: 1; } +} + +.service-stamp-free { + background: linear-gradient(135deg, #2ecc71, #27ae60); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-new { + background: linear-gradient(135deg, #3498db, #2980b9); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-popular { + background: linear-gradient(135deg, #e74c3c, #c0392b); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-promo { + background: linear-gradient(135deg, #f39c12, #e67e22); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-best { + background: linear-gradient(135deg, #9b59b6, #8e44ad); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +/* Stamp on tab button */ +.service-tab-btn { + position: relative; +} + +.service-tab-btn .service-stamp { + position: absolute; + top: -10px; + right: -10px; + font-size: 0.55rem; + padding: 0.2rem 0.5rem; + z-index: 10; +} + +/* Large stamp for service visual */ +.service-visual-stamp { + position: absolute; + top: 20px; + right: 20px; + z-index: 10; + padding: 0.5rem 1rem; + font-size: 0.85rem; + transform: rotate(12deg); + border: 3px dashed rgba(255, 255, 255, 0.6); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.service-visual-stamp::before { + content: ''; + position: absolute; + inset: -6px; + border: 2px solid currentColor; + border-radius: 6px; + opacity: 0.3; +} + +/* Stamp in header - inline with transparency */ +.service-tab-header { + position: relative; +} + +.service-tab-header .service-stamp-inline { + display: inline-flex; + margin-left: 1rem; + vertical-align: middle; + transform: rotate(12deg); + opacity: 0.7; + font-size: 0.55rem; + padding: 0.2rem 0.45rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + transition: opacity 0.3s ease; +} + +.service-tab-header .service-stamp-inline:hover { + opacity: 1; +} + +/* Animated stamp effect */ +.service-stamp-animated { + animation: stampBounce 2s ease infinite; +} + +@keyframes stampBounce { + 0%, 100% { transform: rotate(12deg) scale(1); } + 50% { transform: rotate(12deg) scale(1.05); } +} + +/* Ribbon style stamp */ +.service-ribbon { + position: absolute; + top: 20px; + left: -35px; + background: linear-gradient(135deg, #2ecc71, #27ae60); + color: white; + padding: 0.5rem 3rem; + font-family: 'OmegaLarge', sans-serif; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 1px; + transform: rotate(-45deg); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + z-index: 10; +} + +.service-ribbon::before, +.service-ribbon::after { + content: ''; + position: absolute; + bottom: -5px; + border: 5px solid #1e8449; + border-bottom-color: transparent; +} + +.service-ribbon::before { + left: 0; + border-left-color: transparent; +} + +.service-ribbon::after { + right: 0; + border-right-color: transparent; +} + +/* Tab Content */ +.service-tab-content { + display: none; + animation: fadeInTab 0.5s ease; +} + +.service-tab-content.active { + display: block; +} + +@keyframes fadeInTab { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.service-tab-grid { + display: grid; + grid-template-columns: 1fr 1.2fr; + gap: 3rem; + align-items: center; +} + +/* Service Visual */ +.service-visual { + position: relative; + height: 400px; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 12px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +.service-visual::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + opacity: 0.1; +} + +.service-visual-icon { + position: relative; + z-index: 1; +} + +.service-visual-icon .icon-main { + width: 120px; + height: 120px; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-radius: 24px; + display: flex; + align-items: center; + justify-content: center; + animation: floatIcon 3s ease-in-out infinite; + box-shadow: 0 20px 60px var(--shadow-color); +} + +.service-visual-icon .icon-main svg { + width: 60px; + height: 60px; + color: white; +} + +@keyframes floatIcon { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-15px); } +} + +/* Floating elements around icon */ +.service-visual-particles { + position: absolute; + inset: 0; + pointer-events: none; +} + +.particle { + position: absolute; + background: var(--accent-primary); + border-radius: 50%; + opacity: 0.3; + animation: particleFloat 4s ease-in-out infinite; +} + +.particle:nth-child(1) { width: 10px; height: 10px; top: 20%; left: 20%; animation-delay: 0s; } +.particle:nth-child(2) { width: 15px; height: 15px; top: 30%; right: 25%; animation-delay: -1s; } +.particle:nth-child(3) { width: 8px; height: 8px; bottom: 25%; left: 30%; animation-delay: -2s; } +.particle:nth-child(4) { width: 12px; height: 12px; bottom: 20%; right: 20%; animation-delay: -3s; } +.particle:nth-child(5) { width: 6px; height: 6px; top: 50%; left: 15%; animation-delay: -0.5s; } +.particle:nth-child(6) { width: 14px; height: 14px; top: 15%; right: 35%; animation-delay: -1.5s; } + +@keyframes particleFloat { + 0%, 100% { transform: translate(0, 0) scale(1); opacity: 0.3; } + 50% { transform: translate(10px, -20px) scale(1.2); opacity: 0.6; } +} + +/* Service Details */ +.service-tab-details { + padding: 1rem 0; +} + +.service-tab-header { + margin-bottom: 2rem; +} + +.service-tab-header h2 { + font-family: 'OmegaLarge', sans-serif; + font-size: 2.25rem; + margin-bottom: 0.75rem; +} + +.service-tab-header p { + color: var(--text-secondary); + font-size: 1.1rem; + line-height: 1.7; +} + +.service-tab-features { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-bottom: 2rem; +} + +.service-feature-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + transition: all 0.3s ease; +} + +.service-feature-item:hover { + border-color: var(--accent-primary); + transform: translateX(5px); +} + +.service-feature-icon { + width: 40px; + height: 40px; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.service-feature-icon svg { + width: 20px; + height: 20px; + color: white; +} + +.service-feature-text h4 { + font-size: 0.95rem; + margin-bottom: 0.2rem; +} + +.service-feature-text p { + font-size: 0.8rem; + color: var(--text-muted); +} + +/* Service CTA */ +.service-tab-cta { + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +/* Service Highlights */ +.service-highlights { + display: flex; + gap: 2rem; + margin-bottom: 2rem; + padding: 1.5rem; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; +} + +.service-highlight { + text-align: center; + flex: 1; +} + +.service-highlight-value { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.75rem; + color: var(--accent-light); + margin-bottom: 0.25rem; +} + +.service-highlight-label { + font-size: 0.8rem; + color: var(--text-muted); +} + +@media (max-width: 1024px) { + .service-tab-grid { + grid-template-columns: 1fr; + gap: 2rem; + } + + .service-visual { + height: 300px; + } +} + +@media (max-width: 600px) { + .service-tab-features { + grid-template-columns: 1fr; + } + + .service-highlights { + flex-direction: column; + gap: 1rem; + } + + .service-tab-btn { + padding: 0.75rem 1rem; + font-size: 0.85rem; + } + + .service-tab-btn span { + display: none; + } +} + +/* ============================================ + Why Choose Us Section + ============================================ */ +.why-us-grid { + max-width: 1000px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.why-us-card { + text-align: center; + padding: 2.5rem 2rem; + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 8px; + transition: all 0.3s ease; +} + +.why-us-card:hover { + border-color: var(--accent-primary); + transform: translateY(-5px); +} + +.why-us-icon { + width: 80px; + height: 80px; + margin: 0 auto 1.5rem; + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + border-radius: 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.why-us-icon svg { + width: 36px; + height: 36px; + color: white; +} + +.why-us-card h3 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.2rem; + margin-bottom: 0.75rem; +} + +.why-us-card p { + color: var(--text-secondary); + font-size: 0.95rem; + line-height: 1.6; +} + +@media (max-width: 768px) { + .why-us-grid { + grid-template-columns: 1fr; + } +} + +/* |||||||||||||||||||||||||||||||||||||||||||||| + |||||||||||||||||||||||||||||||||||||||||||||| + |||||||||||||||||||||||||||||||||||||||||||||| */ + +/* ============================================ + Service Stamps / Tags + ============================================ */ +.service-stamp { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.3rem 0.6rem; + font-family: 'OmegaLarge', sans-serif; + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: 4px; + font-weight: bold; + transform: rotate(12deg); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); +} + +.service-stamp-free { + background: linear-gradient(135deg, #2ecc71, #27ae60); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-new { + background: linear-gradient(135deg, #3498db, #2980b9); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-popular { + background: linear-gradient(135deg, #e74c3c, #c0392b); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-stamp-promo { + background: linear-gradient(135deg, #f39c12, #e67e22); + color: white; + border: 2px dashed rgba(255, 255, 255, 0.5); +} + +.service-tab-btn .service-stamp { + position: absolute; + top: -10px; + right: -10px; + font-size: 0.55rem; + padding: 0.2rem 0.5rem; + z-index: 10; +} + +.service-visual-stamp { + position: absolute; + top: 20px; + right: 20px; + z-index: 10; + padding: 0.5rem 1rem; + font-size: 0.85rem; + transform: rotate(12deg); + border: 3px dashed rgba(255, 255, 255, 0.6); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.service-tab-header .service-stamp-inline { + display: inline-flex; + margin-left: 1rem; + vertical-align: middle; + transform: rotate(12deg); + opacity: 0.75; + font-size: 0.75rem; + padding: 0.35rem 0.7rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + transition: opacity 0.3s ease, transform 0.3s ease; +} + +.service-tab-header .service-stamp-inline:hover { + opacity: 1; + transform: rotate(12deg) scale(1.1); +} + +.service-stamp-animated { + animation: stampBounce 2s ease infinite; +} + +@keyframes stampBounce { + 0%, 100% { transform: rotate(12deg) scale(1); } + 50% { transform: rotate(12deg) scale(1.05); } +} + +/* ============================================ + Partners Section - Infinite Logo Carousel + ============================================ */ +.partners-section { + padding: 4rem 0; +} + +.partners-section .section-header { + margin-bottom: 2.5rem; +} + +.partners-marquee { + position: relative; + width: 100%; + overflow: hidden; + padding: 2rem 0 3rem; + -webkit-mask-image: linear-gradient(90deg, transparent 0%, white 8%, white 92%, transparent 100%); + mask-image: linear-gradient(90deg, transparent 0%, white 8%, white 92%, transparent 100%); +} + +.partners-track { + display: flex; + gap: 5rem; + width: max-content; + animation: marqueeInfinite 35s linear infinite; + will-change: transform; + backface-visibility: hidden; + perspective: 1000px; +} + +.partners-track:hover { + animation-play-state: paused; +} + +@keyframes marqueeInfinite { + 0% { + transform: translate3d(0, 0, 0); + } + 100% { + transform: translate3d(-50%, 0, 0); + } +} + +.partner-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + flex-shrink: 0; + cursor: pointer; + transition: transform 0.3s ease; +} + +.partner-item:hover { + transform: translateY(-8px); +} + +.partner-logo-icon { + width: 100px; + height: 100px; + display: flex; + align-items: center; + justify-content: center; + font-family: 'OmegaLarge', sans-serif; + font-size: 2rem; + color: var(--text-muted); + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 20px; + transition: all 0.3s ease; +} + +.partner-item:hover .partner-logo-icon { + color: var(--accent-light); + border-color: var(--accent-primary); + background: var(--glass-bg); + box-shadow: 0 15px 40px rgba(139, 58, 98, 0.3); +} + +.partner-item span { + font-size: 0.95rem; + color: var(--text-muted); + font-weight: 500; + transition: color 0.3s ease; + white-space: nowrap; +} + +.partner-item:hover span { + color: var(--text-primary); +} + +@media (max-width: 768px) { + .partners-track { + gap: 3rem; + animation-duration: 25s; + } + + .partner-logo-icon { + width: 75px; + height: 75px; + font-size: 1.5rem; + border-radius: 14px; + } + + .partner-item span { + font-size: 0.8rem; + } +} + +/* ============================================ + Legal Pages Styles + ============================================ */ +.legal-content { + padding: 4rem 2rem 6rem; + max-width: 900px; + margin: 0 auto; +} + +.legal-container { + display: flex; + flex-direction: column; + gap: 2.5rem; +} + +.legal-section h2 { + font-family: 'OmegaLarge', sans-serif; + font-size: 1.3rem; + color: var(--text-primary); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.legal-section h2 svg { + width: 22px; + height: 22px; + color: var(--accent-light); +} + +.legal-card { + background: var(--card-bg); + border: 1px solid var(--glass-border); + border-radius: 12px; + padding: 1.5rem 2rem; +} + +.legal-card p { + color: var(--text-secondary); + line-height: 1.7; + margin-bottom: 1rem; +} + +.legal-card p:last-child { + margin-bottom: 0; +} + +.legal-card strong { + color: var(--text-primary); +} + +.legal-card ul { + margin: 1rem 0; + padding-left: 1.5rem; + color: var(--text-secondary); +} + +.legal-card ul li { + margin-bottom: 0.5rem; + line-height: 1.6; +} + +.legal-card ul li::marker { + color: var(--accent-light); +} + +@media (max-width: 768px) { + .legal-content { + padding: 2rem 1rem 4rem; + } + + .legal-card { + padding: 1.25rem 1.5rem; + } + + .legal-section h2 { + font-size: 1.1rem; + } +} diff --git a/assets/data/projects.json b/assets/data/projects.json new file mode 100644 index 0000000..a126358 --- /dev/null +++ b/assets/data/projects.json @@ -0,0 +1,290 @@ +[ + { + "id": 1, + "category": "bot", + "title": "Medieval RP Bot", + "desc": "Un système RP médiéval complet avec économie, métiers, combat stratégique et gestion de royaumes. Développé pour une communauté de 5000+ joueurs actifs.", + "gradient": "linear-gradient(135deg, var(--accent-primary), var(--accent-secondary))", + "year": "2024", + "duration": "3 mois", + "features": ["Système d'économie", "Combat tour par tour", "Gestion de royaumes", "Quêtes automatisées", "Inventaire avancé", "Système de métiers"], + "tech": ["Python", "Discord.py", "PostgreSQL", "Redis", "FastAPI"], + "stats": [{ "value": "5000+", "label": "Joueurs" }, { "value": "150+", "label": "Commandes" }, { "value": "99.9%", "label": "Uptime" }] + }, + { + "id": 2, + "category": "map", + "title": "Cyberpunk City", + "desc": "Ville futuriste immersive avec 5 quartiers distincts, plus de 100 bâtiments accessibles et un système jour/nuit dynamique. Optimisée pour 50 joueurs.", + "gradient": "linear-gradient(135deg, #5E2040, #8b3a62)", + "year": "2024", + "duration": "2 mois", + "features": ["5 quartiers uniques", "100+ bâtiments", "Système jour/nuit", "Zones scriptées", "Éclairage néon", "Optimisé 50 joueurs"], + "tech": ["Fortnite Creative", "UEFN", "Verse", "Lighting"], + "stats": [{ "value": "50K+", "label": "Parties" }, { "value": "100+", "label": "Bâtiments" }, { "value": "50", "label": "Joueurs max" }] + }, + { + "id": 3, + "category": "bot", + "title": "Space Station RP", + "desc": "Bot Sci-Fi avec économie spatiale, gestion de vaisseaux, exploration de planètes et système de factions interstellaires.", + "gradient": "linear-gradient(135deg, #3a1f5e, #8b3a62)", + "year": "2024", + "duration": "4 mois", + "features": ["Économie spatiale", "Gestion de vaisseaux", "Exploration", "Factions", "Combat spatial", "Crafting"], + "tech": ["Python", "Discord.py", "MongoDB", "Docker"], + "stats": [{ "value": "3000+", "label": "Joueurs" }, { "value": "200+", "label": "Commandes" }, { "value": "50+", "label": "Planètes" }] + }, + { + "id": 4, + "category": "graphisme", + "title": "Branding Nova RP", + "desc": "Identité visuelle complète incluant logo, bannières, emojis personnalisés et templates pour un serveur RP majeur.", + "gradient": "linear-gradient(135deg, #2d1b5e, #5E2040)", + "year": "2024", + "duration": "3 semaines", + "features": ["Logo principal", "Déclinaisons logo", "Bannières Discord", "Emojis custom", "Templates", "Guide de style"], + "tech": ["Photoshop", "Illustrator", "After Effects", "Figma"], + "stats": [{ "value": "50+", "label": "Éléments" }, { "value": "8", "label": "Bannières" }, { "value": "30+", "label": "Emojis" }] + }, + { + "id": 5, + "category": "map", + "title": "Tokyo Night", + "desc": "Reconstitution d'un quartier de Tokyo nocturne avec ruelles, néons, boutiques et ambiance authentique pour du RP urbain japonais.", + "gradient": "linear-gradient(135deg, #1a3a5e, #3a5e8b)", + "year": "2024", + "duration": "6 semaines", + "features": ["Ruelles détaillées", "Néons japonais", "Boutiques", "Metro", "Appartements", "Rooftops"], + "tech": ["Fortnite Creative", "UEFN", "Custom Props"], + "stats": [{ "value": "30K+", "label": "Parties" }, { "value": "80+", "label": "Boutiques" }, { "value": "40", "label": "Joueurs max" }] + }, + { + "id": 6, + "category": "bot", + "title": "Western Frontier", + "desc": "Système RP Far West complet avec duels, shérif, hors-la-loi, prospection d'or et gestion de ranch.", + "gradient": "linear-gradient(135deg, #5e3a1a, #8b5e3a)", + "year": "2023", + "duration": "3 mois", + "features": ["Système de duels", "Rôles Sheriff/Outlaw", "Prospection d'or", "Gestion de ranch", "Économie western", "Wanted system"], + "tech": ["Python", "Discord.py", "SQLite", "PIL"], + "stats": [{ "value": "2500+", "label": "Joueurs" }, { "value": "100+", "label": "Commandes" }, { "value": "15+", "label": "Métiers" }] + }, + { + "id": 7, + "category": "graphisme", + "title": "EcoVerse Assets", + "desc": "Pack complet d'icônes et emojis sur thème écologique/nature pour serveur communautaire gaming.", + "gradient": "linear-gradient(135deg, #1a5e3a, #3a8b5e)", + "year": "2024", + "duration": "2 semaines", + "features": ["Pack icônes", "Emojis animés", "Badges ranks", "Séparateurs", "Backgrounds", "Overlays"], + "tech": ["Illustrator", "After Effects", "Photoshop"], + "stats": [{ "value": "100+", "label": "Icônes" }, { "value": "50+", "label": "Emojis" }, { "value": "20", "label": "Badges" }] + }, + { + "id": 8, + "category": "map", + "title": "Apocalypse Zone", + "desc": "Monde post-apocalyptique avec zones de survie, bunkers, véhicules abandonnés et ambiance désolée.", + "gradient": "linear-gradient(135deg, #5e1a3a, #8b3a5e)", + "year": "2023", + "duration": "2 mois", + "features": ["Zones dévastées", "Bunkers secrets", "Véhicules props", "Camps survivants", "Ressources", "Zone radioactive"], + "tech": ["Fortnite Creative", "UEFN", "Environmental Design"], + "stats": [{ "value": "25K+", "label": "Parties" }, { "value": "10", "label": "Zones" }, { "value": "30", "label": "Joueurs max" }] + }, + { + "id": 9, + "category": "bot", + "title": "Academy RP", + "desc": "Système scolaire interactif avec cours, examens, clubs, dortoirs et système de réputation étudiant.", + "gradient": "linear-gradient(135deg, #3a1a5e, #5e3a8b)", + "year": "2024", + "duration": "2 mois", + "features": ["Système de cours", "Examens", "Clubs étudiants", "Dortoirs", "Réputation", "Events scolaires"], + "tech": ["Python", "Discord.py", "PostgreSQL"], + "stats": [{ "value": "4000+", "label": "Étudiants" }, { "value": "50+", "label": "Cours" }, { "value": "20+", "label": "Clubs" }] + }, + { + "id": 10, + "category": "graphisme", + "title": "Golden Crown", + "desc": "Bannières et visuels premium style royal/luxe pour serveur RP médiéval haut de gamme.", + "gradient": "linear-gradient(135deg, #5e5e1a, #8b8b3a)", + "year": "2024", + "duration": "1 mois", + "features": ["Bannières premium", "Logo royal", "Ornements", "Cadres dorés", "Typography", "Animations"], + "tech": ["Photoshop", "Illustrator", "After Effects"], + "stats": [{ "value": "30+", "label": "Visuels" }, { "value": "5", "label": "Animations" }, { "value": "100%", "label": "Satisfaction" }] + }, + { + "id": 11, + "category": "map", + "title": "Mystic Village", + "desc": "Village fantastique médiéval avec magie, créatures et architecture féerique pour RP fantasy.", + "gradient": "linear-gradient(135deg, #1a1a5e, #3a3a8b)", + "year": "2023", + "duration": "5 semaines", + "features": ["Village médiéval", "Tour du mage", "Forêt enchantée", "Taverne", "Marché", "Arène"], + "tech": ["Fortnite Creative", "Custom Lighting"], + "stats": [{ "value": "20K+", "label": "Parties" }, { "value": "50+", "label": "Bâtiments" }, { "value": "35", "label": "Joueurs max" }] + }, + { + "id": 12, + "category": "bot", + "title": "Crime City", + "desc": "Bot GTA-like avec gangs, territoire, trafics, police et système judiciaire complet.", + "gradient": "linear-gradient(135deg, #5e1a1a, #8b3a3a)", + "year": "2024", + "duration": "4 mois", + "features": ["Système de gangs", "Territoires", "Trafics", "Police/Justice", "Prison", "Braquages"], + "tech": ["Python", "Discord.py", "PostgreSQL", "Redis"], + "stats": [{ "value": "6000+", "label": "Joueurs" }, { "value": "250+", "label": "Commandes" }, { "value": "10+", "label": "Gangs" }] + }, + { + "id": 13, + "category": "bot", + "title": "Pirate Bay RP", + "desc": "Aventure pirate complète avec navigation, batailles navales, trésors et commerce maritime.", + "gradient": "linear-gradient(135deg, #1a4a5e, #3a6a8b)", + "year": "2023", + "duration": "3 mois", + "features": ["Navigation", "Batailles navales", "Chasse au trésor", "Commerce", "Équipages", "Îles secrètes"], + "tech": ["Python", "Discord.py", "MongoDB"], + "stats": [{ "value": "3500+", "label": "Pirates" }, { "value": "100+", "label": "Îles" }, { "value": "50+", "label": "Navires" }] + }, + { + "id": 14, + "category": "map", + "title": "Neon District", + "desc": "Quartier cyberpunk détaillé avec clubs, appartements high-tech et ambiance nocturne.", + "gradient": "linear-gradient(135deg, #4a1a5e, #6a3a8b)", + "year": "2024", + "duration": "4 semaines", + "features": ["Nightclubs", "Appartements", "Ruelles néon", "Boutiques tech", "Garages", "Rooftop bars"], + "tech": ["Fortnite Creative", "UEFN", "Neon Effects"], + "stats": [{ "value": "15K+", "label": "Parties" }, { "value": "60+", "label": "Intérieurs" }, { "value": "25", "label": "Joueurs max" }] + }, + { + "id": 15, + "category": "graphisme", + "title": "Cyber UI Pack", + "desc": "Interface futuriste complète avec HUD, menus et éléments interactifs style sci-fi.", + "gradient": "linear-gradient(135deg, #1a5e5e, #3a8b8b)", + "year": "2024", + "duration": "3 semaines", + "features": ["HUD elements", "Menus", "Boutons", "Barres de vie", "Minimap style", "Notifications"], + "tech": ["Figma", "Illustrator", "After Effects"], + "stats": [{ "value": "80+", "label": "Éléments" }, { "value": "10", "label": "Animations" }, { "value": "3", "label": "Thèmes" }] + }, + { + "id": 16, + "category": "bot", + "title": "Dragon Realm", + "desc": "Univers fantasy avec dragons apprivoisables, magie élémentaire et quêtes épiques.", + "gradient": "linear-gradient(135deg, #5e2a1a, #8b4a3a)", + "year": "2023", + "duration": "5 mois", + "features": ["Dragons", "Magie élémentaire", "Quêtes épiques", "Guildes", "Donjons", "Boss fights"], + "tech": ["Python", "Discord.py", "PostgreSQL", "PIL"], + "stats": [{ "value": "4500+", "label": "Joueurs" }, { "value": "30+", "label": "Dragons" }, { "value": "100+", "label": "Quêtes" }] + }, + { + "id": 17, + "category": "map", + "title": "Space Colony", + "desc": "Station spatiale habitable avec modules, serres, hangars et vue sur l'espace.", + "gradient": "linear-gradient(135deg, #1a2a5e, #3a4a8b)", + "year": "2024", + "duration": "6 semaines", + "features": ["Modules habitables", "Serres spatiales", "Hangars", "Passerelles", "Vue espace", "Gravité zéro"], + "tech": ["Fortnite Creative", "UEFN", "Sci-fi Props"], + "stats": [{ "value": "12K+", "label": "Parties" }, { "value": "20", "label": "Modules" }, { "value": "20", "label": "Joueurs max" }] + }, + { + "id": 18, + "category": "graphisme", + "title": "Retro Wave Pack", + "desc": "Style années 80 revisité avec néons, grilles et esthétique synthwave.", + "gradient": "linear-gradient(135deg, #5e1a4a, #8b3a6a)", + "year": "2023", + "duration": "2 semaines", + "features": ["Bannières retro", "Grilles 80s", "Néons rose/bleu", "Typography", "Sun effects", "VHS overlays"], + "tech": ["Photoshop", "After Effects", "Illustrator"], + "stats": [{ "value": "40+", "label": "Éléments" }, { "value": "15", "label": "Animations" }, { "value": "5", "label": "Templates" }] + }, + { + "id": 19, + "category": "bot", + "title": "Zombie Survival", + "desc": "Système survie zombie avec crafting, bases, hordes et coopération entre survivants.", + "gradient": "linear-gradient(135deg, #2a5e1a, #4a8b3a)", + "year": "2024", + "duration": "3 mois", + "features": ["Système de survie", "Crafting", "Construction bases", "Hordes zombies", "Coop", "Ressources"], + "tech": ["Python", "Discord.py", "MongoDB", "Redis"], + "stats": [{ "value": "5500+", "label": "Survivants" }, { "value": "50+", "label": "Items craft" }, { "value": "1000+", "label": "Bases" }] + }, + { + "id": 20, + "category": "map", + "title": "Desert Oasis", + "desc": "Monde désertique immersif avec oasis, ruines anciennes et caravanes nomades.", + "gradient": "linear-gradient(135deg, #5e4a1a, #8b6a3a)", + "year": "2023", + "duration": "5 semaines", + "features": ["Désert vaste", "Oasis centrale", "Ruines anciennes", "Camp nomade", "Tempêtes sable", "Grottes"], + "tech": ["Fortnite Creative", "Environmental FX"], + "stats": [{ "value": "18K+", "label": "Parties" }, { "value": "8", "label": "Zones" }, { "value": "30", "label": "Joueurs max" }] + }, + { + "id": 21, + "category": "bot", + "title": "Mafia Empire", + "desc": "Organisation criminelle RP avec familles, territoires, business illégaux et omerta.", + "gradient": "linear-gradient(135deg, #3a3a3a, #5a5a5a)", + "year": "2024", + "duration": "4 mois", + "features": ["Familles mafieuses", "Territoires", "Business", "Omerta", "Hiérarchie", "Contrats"], + "tech": ["Python", "Discord.py", "PostgreSQL"], + "stats": [{ "value": "3800+", "label": "Membres" }, { "value": "8", "label": "Familles" }, { "value": "150+", "label": "Commandes" }] + }, + { + "id": 22, + "category": "graphisme", + "title": "Fantasy Icons", + "desc": "200+ icônes médiévales pour items, skills, classes et interface de jeu RP.", + "gradient": "linear-gradient(135deg, #4a3a1a, #6a5a3a)", + "year": "2024", + "duration": "1 mois", + "features": ["Icônes items", "Icônes skills", "Icônes classes", "Cadres", "Rarités", "Animations"], + "tech": ["Illustrator", "Photoshop"], + "stats": [{ "value": "200+", "label": "Icônes" }, { "value": "6", "label": "Rarités" }, { "value": "12", "label": "Classes" }] + }, + { + "id": 23, + "category": "map", + "title": "Underwater City", + "desc": "Cité sous-marine style Atlantis avec dômes, tunnels et créatures marines.", + "gradient": "linear-gradient(135deg, #1a3a4a, #3a5a6a)", + "year": "2024", + "duration": "7 semaines", + "features": ["Dômes habitables", "Tunnels verre", "Jardins marins", "Palais central", "Sous-marins", "Créatures"], + "tech": ["Fortnite Creative", "UEFN", "Underwater FX"], + "stats": [{ "value": "22K+", "label": "Parties" }, { "value": "15", "label": "Dômes" }, { "value": "25", "label": "Joueurs max" }] + }, + { + "id": 24, + "category": "bot", + "title": "Superhero League", + "desc": "Univers super-héros avec pouvoirs, missions, vilains et quartier général.", + "gradient": "linear-gradient(135deg, #1a1a4a, #3a3a6a)", + "year": "2023", + "duration": "4 mois", + "features": ["Pouvoirs custom", "Missions", "Vilains IA", "QG héros", "Réputation", "Team-ups"], + "tech": ["Python", "Discord.py", "PostgreSQL", "PIL"], + "stats": [{ "value": "4200+", "label": "Héros" }, { "value": "80+", "label": "Pouvoirs" }, { "value": "200+", "label": "Missions" }] + } +] diff --git a/assets/data/team.json b/assets/data/team.json new file mode 100644 index 0000000..296eaab --- /dev/null +++ b/assets/data/team.json @@ -0,0 +1,86 @@ +[ + { + "name": "Alexandre", + "role": "Développeur Principal", + "avatar": "A", + "bio": "Passionné de développement depuis l'adolescence, Alexandre a découvert le monde du RP Discord en 2019. Depuis, il a développé plus de 50 bots pour des communautés allant de quelques centaines à plusieurs milliers de membres. Son expertise couvre les systèmes d'économie, les mécaniques de jeu complexes et l'optimisation des performances.", + "stats": [ + { "value": "5", "label": "Ans d'exp." }, + { "value": "50+", "label": "Projets" }, + { "value": "★★★", "label": "Expert" } + ], + "skills": ["Python", "Discord.py", "JavaScript", "Node.js", "MongoDB", "API REST"] + }, + { + "name": "Sophie", + "role": "Designer Fortnite", + "avatar": "S", + "bio": "Sophie combine sa passion pour le level design et le storytelling pour créer des environnements Fortnite uniques. Formée en architecture, elle apporte une attention particulière aux détails et à l'ambiance de chaque map. Ses créations ont été jouées par plus de 100 000 joueurs.", + "stats": [ + { "value": "3", "label": "Ans d'exp." }, + { "value": "25+", "label": "Maps" }, + { "value": "100K", "label": "Joueurs" } + ], + "skills": ["Fortnite Creative", "UEFN", "Level Design", "3D", "Lighting", "Scripting"] + }, + { + "name": "Thomas", + "role": "Community Manager", + "avatar": "T", + "bio": "Thomas est le lien entre OmégaKube et ses communautés. Ancien admin de plusieurs serveurs RP majeurs, il comprend les besoins des joueurs et des staff. Il s'assure que chaque projet répond aux attentes et accompagne les clients dans le développement de leur communauté.", + "stats": [ + { "value": "4", "label": "Ans d'exp." }, + { "value": "20K+", "label": "Membres" }, + { "value": "24/7", "label": "Dispo" } + ], + "skills": ["Community Management", "Discord", "Modération", "Events", "Support"] + }, + { + "name": "Marie", + "role": "Graphiste", + "avatar": "M", + "bio": "Marie donne vie aux identités visuelles des projets RP. Diplômée en design graphique, elle maîtrise aussi bien la création de logos que le motion design. Son style unique mélange esthétique gaming et professionnalisme pour des visuels qui marquent les esprits.", + "stats": [ + { "value": "6", "label": "Ans d'exp." }, + { "value": "200+", "label": "Créations" }, + { "value": "★★★", "label": "Expert" } + ], + "skills": ["Photoshop", "Illustrator", "After Effects", "Figma", "Branding", "UI Design"] + }, + { + "name": "Lucas", + "role": "Développeur Backend", + "avatar": "L", + "bio": "Lucas est l'architecte invisible derrière les systèmes les plus robustes d'OmégaKube. Spécialisé dans les bases de données et les API, il conçoit des systèmes d'économie capables de gérer des millions de transactions sans ralentissement.", + "stats": [ + { "value": "4", "label": "Ans d'exp." }, + { "value": "30+", "label": "Systèmes" }, + { "value": "99.9%", "label": "Uptime" } + ], + "skills": ["Python", "PostgreSQL", "Redis", "FastAPI", "Docker", "AWS"] + }, + { + "name": "Emma", + "role": "Scénariste RP", + "avatar": "E", + "bio": "Emma crée les univers et les histoires qui donnent vie aux sessions RP. Autrice de formation, elle conçoit des scénarios immersifs, des personnages mémorables et des quêtes captivantes. Elle aide aussi les clients à développer le lore de leur serveur.", + "stats": [ + { "value": "3", "label": "Ans d'exp." }, + { "value": "40+", "label": "Scénarios" }, + { "value": "∞", "label": "Créativité" } + ], + "skills": ["Écriture", "Worldbuilding", "Game Design", "Narration", "Dialogue"] + }, + { + "name": "Julien", + "role": "Testeur QA", + "avatar": "J", + "bio": "Julien est le gardien de la qualité chez OmégaKube. Aucun bug ne lui échappe. Il teste méticuleusement chaque fonctionnalité, chaque commande, chaque interaction pour garantir une expérience sans accroc aux utilisateurs finaux.", + "stats": [ + { "value": "2", "label": "Ans d'exp." }, + { "value": "500+", "label": "Bugs trouvés" }, + { "value": "0", "label": "Bug raté" } + ], + "skills": ["Testing", "Bug Tracking", "Documentation", "Discord", "Feedback"] + } +] \ No newline at end of file diff --git a/assets/fonts/custom_font_large.otf b/assets/fonts/custom_font_large.otf new file mode 100644 index 0000000..74819b8 Binary files /dev/null and b/assets/fonts/custom_font_large.otf differ diff --git a/assets/fonts/custom_font_small.otf b/assets/fonts/custom_font_small.otf new file mode 100644 index 0000000..8c5eafe Binary files /dev/null and b/assets/fonts/custom_font_small.otf differ diff --git a/assets/images/favicon/android-chrome-192x192.png b/assets/images/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..0bfecf2 Binary files /dev/null and b/assets/images/favicon/android-chrome-192x192.png differ diff --git a/assets/images/favicon/android-chrome-512x512.png b/assets/images/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..ea4db2d Binary files /dev/null and b/assets/images/favicon/android-chrome-512x512.png differ diff --git a/assets/images/favicon/apple-touch-icon.png b/assets/images/favicon/apple-touch-icon.png new file mode 100644 index 0000000..64df795 Binary files /dev/null and b/assets/images/favicon/apple-touch-icon.png differ diff --git a/assets/images/favicon/favicon-16x16.png b/assets/images/favicon/favicon-16x16.png new file mode 100644 index 0000000..3b8f32d Binary files /dev/null and b/assets/images/favicon/favicon-16x16.png differ diff --git a/assets/images/favicon/favicon-32x32.png b/assets/images/favicon/favicon-32x32.png new file mode 100644 index 0000000..e1ad569 Binary files /dev/null and b/assets/images/favicon/favicon-32x32.png differ diff --git a/assets/images/favicon/favicon.ico b/assets/images/favicon/favicon.ico new file mode 100644 index 0000000..a28de91 Binary files /dev/null and b/assets/images/favicon/favicon.ico differ diff --git a/assets/images/favicon/site.webmanifest b/assets/images/favicon/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/assets/images/favicon/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..c870bcf Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/old-logo.png b/assets/images/old-logo.png new file mode 100644 index 0000000..fbf01ec Binary files /dev/null and b/assets/images/old-logo.png differ diff --git a/assets/js/home.js b/assets/js/home.js new file mode 100644 index 0000000..8be00d0 --- /dev/null +++ b/assets/js/home.js @@ -0,0 +1,171 @@ +// Home page specific JS: team carousel + modal +document.addEventListener('DOMContentLoaded', () => { + // Fetch team data and render + fetch('assets/data/team.json') + .then(res => res.json()) + .then(members => { + initTeamSection(members); + }) + .catch(err => { + console.error('Failed to load team data', err); + }); + + 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) => ` +
+
+
${m.avatar}
+
+

${m.name}

+

${m.role}

+

${(m.bio || '').split('.').shift()}.

+
+ `).join(''); + + // Carousel logic + const cardsContainer = carousel; // the track + let cards = carousel.querySelectorAll('.team-card'); + let currentIndex = 0; + let cardsPerView = 3; + let autoPlayInterval; + + function updateCardsPerView() { + if (window.innerWidth <= 768) cardsPerView = 1; + else if (window.innerWidth <= 1024) cardsPerView = 2; + else cardsPerView = 3; + } + + function createDots() { + dotsContainer.innerHTML = ''; + const totalDots = Math.max(1, Math.ceil(cards.length / cardsPerView)); + for (let i = 0; i < totalDots; i++) { + const dot = document.createElement('div'); + dot.classList.add('carousel-dot'); + if (i === 0) dot.classList.add('active'); + dot.addEventListener('click', () => goToSlide(i)); + dotsContainer.appendChild(dot); + } + } + + function updateCarousel() { + cards = carousel.querySelectorAll('.team-card'); + if (cards.length === 0) return; + const cardStyle = window.getComputedStyle(cards[0]); + const gap = 32; // matches previous implementation + 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)); + } + + function goToSlide(dotIndex) { + currentIndex = dotIndex * cardsPerView; + if (currentIndex > cards.length - cardsPerView) currentIndex = Math.max(0, cards.length - cardsPerView); + updateCarousel(); + resetAutoPlay(); + } + + function nextSlide() { + currentIndex++; + if (currentIndex > cards.length - cardsPerView) currentIndex = 0; + updateCarousel(); + } + + function prevSlide() { + currentIndex--; + if (currentIndex < 0) currentIndex = Math.max(0, cards.length - cardsPerView); + updateCarousel(); + } + + 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(); + }); + + // Modal logic + const modalOverlay = document.getElementById('team-modal-overlay'); + const modalClose = document.getElementById('team-modal-close'); + const modalAvatar = document.getElementById('modal-avatar'); + const modalName = document.getElementById('modal-name'); + const modalRole = document.getElementById('modal-role'); + const modalBio = document.getElementById('modal-bio'); + const modalStats = document.getElementById('modal-stats'); + const modalSkills = document.getElementById('modal-skills'); + const modalSocial = document.getElementById('modal-social'); + + 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 => ` +
+
${stat.value}
+
${stat.label}
+
+ `).join(''); + + modalSkills.innerHTML = (member.skills || []).map(s => `${s}`).join(''); + + modalSocial.innerHTML = ` + + + `; + + if (window.feather && feather.replace) feather.replace(); + + modalOverlay.classList.add('active'); + document.body.style.overflow = 'hidden'; + } + + function closeMemberModal() { + modalOverlay.classList.remove('active'); + 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); + }); + }); + + 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(); }); + } +}); \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..9e7fe37 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,393 @@ +// OmégaKube - shared scripts + +// Feather icons loader (centralized) +// loads https://unpkg.com/feather-icons when needed and exposes window.ensureFeather() +const FEATHER_URL = 'https://unpkg.com/feather-icons'; +function ensureFeather() { + if (window.__featherLoading) return window.__featherLoading; + if (window.feather && window.feather.replace) return Promise.resolve(window.feather); + window.__featherLoading = new Promise((resolve, reject) => { + const s = document.createElement('script'); + s.src = FEATHER_URL; + s.async = true; + s.onload = () => resolve(window.feather); + s.onerror = () => reject(new Error('Failed to load feather-icons')); + document.head.appendChild(s); + }); + return window.__featherLoading; +} +window.ensureFeather = ensureFeather; + +document.addEventListener('DOMContentLoaded', async () => { + // ensure feather icons are available and replace icons in document + try { await ensureFeather(); if (window.feather && feather.replace) try { feather.replace(); } catch(e) {} } catch (e) {} + + // Load HTML includes (components header/footer) and fix relative paths when necessary + async function loadIncludes() { + const includeSelector = '[data-include]'; + const nodes = Array.from(document.querySelectorAll(includeSelector)); + if (!nodes.length) return; + await Promise.all(nodes.map(async el => { + const url = el.getAttribute('data-include'); + try { + const res = await fetch(url); + if (!res.ok) throw new Error(res.statusText); + const text = await res.text(); + const template = document.createElement('template'); + template.innerHTML = text; + const fragment = template.content; + // adjust relative paths for assets and page links when included from /pages/ + fragment.querySelectorAll('[href], [src]').forEach(node => { + ['href','src'].forEach(attr => { + if (!node.hasAttribute(attr)) return; + const original = node.getAttribute(attr); + + // leave absolute URLs and anchors untouched + if (/^(https?:|\/|#)/.test(original)) return; + + // compute useful bases + const includeUrl = new URL(url, document.baseURI); + const includeDir = includeUrl.pathname.substring(0, includeUrl.pathname.lastIndexOf('/') + 1); + const docUrl = new URL(document.location.href); + const docDir = docUrl.pathname.substring(0, docUrl.pathname.lastIndexOf('/') + 1); + + // find common prefix between include location and current document to infer site root + const includeParts = includeDir.split('/'); + const docParts = docDir.split('/'); + let i = 0; + while (i < includeParts.length && i < docParts.length && includeParts[i] === docParts[i]) i++; + const baseRoot = includeParts.slice(0, i).join('/') || '/'; + + let newVal; + if (original.startsWith('./') || original.startsWith('../')) { + // explicit relative paths should resolve relative to the include file + const resolved = new URL(original, includeUrl); + newVal = resolved.pathname + resolved.search + resolved.hash; + } else { + // treat plain paths (e.g. "assets/..." or "pages/...") as site-root relative + newVal = (baseRoot.endsWith('/') ? baseRoot : baseRoot + '/') + original; + } + + node.setAttribute(attr, newVal); + }); + }); + el.replaceWith(fragment); + } catch (err) { + console.error('Failed to load include', url, err); + } + })); + if (typeof ensureFeather === 'function') { + ensureFeather().then(() => { try { if (window.feather && feather.replace) feather.replace(); } catch (e) {} }).catch(()=>{}); + } else if (window.feather && feather.replace) { + try { feather.replace(); } catch (e) {} + } + } + + // wait for includes to be loaded so subsequent initialization can bind to elements inside them + await loadIncludes(); + + const html = document.documentElement; + + function getPreferredTheme() { + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) return savedTheme; + return window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; + } + + // Apply preferred theme + try { + html.setAttribute('data-theme', getPreferredTheme()); + } catch (e) {} + + // Theme toggle (robust) + const themeToggle = document.getElementById('theme-toggle'); + if (themeToggle) { + // sync aria state + themeToggle.setAttribute('aria-pressed', html.getAttribute('data-theme') === 'dark' ? 'false' : 'true'); + + themeToggle.addEventListener('click', () => { + const currentTheme = html.getAttribute('data-theme'); + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + html.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + themeToggle.setAttribute('aria-pressed', newTheme === 'dark' ? 'false' : 'true'); + }); + } + + // Navbar scroll effect + const navbar = document.getElementById('navbar'); + function updateNavbarOnScroll() { + if (!navbar) return; + navbar.classList.toggle('scrolled', window.scrollY > 50); + } + window.addEventListener('scroll', updateNavbarOnScroll); + window.addEventListener('load', updateNavbarOnScroll); + + // Mobile menu + const mobileToggle = document.getElementById('mobile-toggle'); + const navLinks = document.getElementById('nav-links'); + + // mobile menu initialization (debug logs removed) + + // create a toggle function that is safe even if elements are added later + const toggleMenu = () => { + const mt = document.getElementById('mobile-toggle'); + const nl = document.getElementById('nav-links'); + // console.log('[DEBUG] toggleMenu called', { mt: !!mt, nl: !!nl, mtClass: mt ? mt.className : null, nlClass: nl ? nl.className : null }); + if (!mt || !nl) { + // elements missing: abort toggle + // console.log('[DEBUG] toggleMenu aborted: missing elements', { mt, nl }); + return; + } + mt.classList.toggle('active'); + nl.classList.toggle('active'); + // console.log('[DEBUG] toggleMenu toggled', { mtClass: mt.className, nlClass: nl.className }); + const expanded = nl.classList.contains('active') ? 'true' : 'false'; + mt.setAttribute('aria-expanded', expanded); + }; + + // |||||||||||||| ça fait buguer le menu mobile |||||||||||||| + // direct binding if elements exist + // if (mobileToggle && navLinks) { + // if (!mobileToggle.hasAttribute('tabindex')) mobileToggle.setAttribute('tabindex', '0'); + // mobileToggle.setAttribute('aria-expanded', navLinks.classList.contains('active') ? 'true' : 'false'); + + // mobileToggle.addEventListener('click', (e) => { + // console.log('[DEBUG] mobileToggle click handler fired', e.target, e); + // e.preventDefault(); + // toggleMenu(); + // }); + + // mobileToggle.addEventListener('keydown', (e) => { + // if (e.key === 'Enter' || e.key === ' ') { + // console.log('[DEBUG] mobileToggle keydown:', e.key); + // e.preventDefault(); + // toggleMenu(); + // } + // }); + + // navLinks.querySelectorAll('a').forEach(link => { + // link.addEventListener('click', () => { + // console.log('[DEBUG] nav link clicked in mobile menu', link.getAttribute('href')); + // // collapse on link click + // const mt = document.getElementById('mobile-toggle'); + // const nl = document.getElementById('nav-links'); + // if (mt && nl) { + // mt.classList.remove('active'); + // nl.classList.remove('active'); + // mt.setAttribute('aria-expanded', 'false'); + // } + // }); + // }); + // } + + // Global delegation: catch clicks anywhere on page for '#mobile-toggle' (works even if element is replaced) + document.addEventListener('click', (e) => { + const mt = e.target.closest ? e.target.closest('#mobile-toggle') : null; + if (mt) { + // console.log('[DEBUG] delegated click caught for #mobile-toggle', e.target); + e.preventDefault(); + toggleMenu(); + } + }); + + // expose helper for console debugging + window.toggleMobileMenu = toggleMenu; + + // Smooth scroll for anchor links + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + const href = this.getAttribute('href'); + if (!href || href === '#') return; + const target = document.querySelector(href); + if (target) { + e.preventDefault(); + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }); + }); + + // Highlight active nav link based on current URL + function normalizePath(path) { + if (!path) return '/'; + // treat both '/' and '/index.html' as root + if (path.endsWith('index.html')) { + const p = path.slice(0, -'index.html'.length); + return p === '' ? '/' : p; + } + return path.endsWith('/') ? path : path; + } + + function setActiveNavLink() { + const links = document.querySelectorAll('#nav-links a[href]'); + if (!links.length) return; + const currentPath = normalizePath(window.location.pathname); + const currentHash = window.location.hash || ''; + + let bestMatch = null; + let bestScore = -1; + + links.forEach(link => { + const href = link.getAttribute('href'); + let linkUrl; + try { + linkUrl = new URL(href, document.location.href); + } catch (e) { + return; + } + const linkPath = normalizePath(linkUrl.pathname); + const linkHash = linkUrl.hash || ''; + + let score = 0; + if (linkPath === currentPath) score += 2; + if (linkHash && linkHash === currentHash) score += 4; + // Prefer exact hash matches, then exact path matches + if (score > bestScore) { + bestScore = score; + bestMatch = link; + } + }); + + // Fallback: if on root (/) and there is an index link, pick it + if (!bestMatch) { + links.forEach(link => { + if (!bestMatch && /index\.html$|\/$/.test(link.getAttribute('href') || '')) { + bestMatch = link; + } + }); + } + + links.forEach(l => { l.classList.remove('active'); l.removeAttribute('aria-current'); }); + if (bestMatch) { bestMatch.classList.add('active'); bestMatch.setAttribute('aria-current', 'page'); } + } + + // initialize and listen for navigation/hash changes + setActiveNavLink(); + window.addEventListener('hashchange', setActiveNavLink); + window.addEventListener('popstate', setActiveNavLink); + + // Home page: observe sections so #apropos and #contact become active when visible, + // and revert to "Accueil" when neither section is in view. + let homeSectionObserver = null; + let homeVisibility = {}; + + function initHomeSectionObserver() { + // clean existing observer + if (homeSectionObserver) { + homeSectionObserver.disconnect(); + homeSectionObserver = null; + homeVisibility = {}; + } + + // Instead of requiring a strict '/' path, detect whether the current document + // actually contains any sections referenced by nav links (e.g. #apropos, #contact). + const links = Array.from(document.querySelectorAll('#nav-links a[href]')); + const sectionLinks = links.filter(link => { + try { + const u = new URL(link.getAttribute('href'), document.location.href); + if (!u.hash) return false; + return !!document.querySelector(u.hash); + } catch (e) { + return false; + } + }); + + if (!sectionLinks.length) return; + + const sections = sectionLinks.map(link => { + const hash = new URL(link.getAttribute('href'), document.location.href).hash; + const el = document.querySelector(hash); + return el ? { id: el.id, el, link } : null; + }).filter(Boolean); + + if (!sections.length) return; + + // setup observer + homeSectionObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + homeVisibility[entry.target.id] = entry.intersectionRatio || 0; + }); + + // pick the section with highest intersection ratio + let bestId = null; let bestRatio = 0; + Object.entries(homeVisibility).forEach(([id, ratio]) => { + if (ratio > bestRatio) { bestRatio = ratio; bestId = id; } + }); + + const navLinks = Array.from(document.querySelectorAll('#nav-links a[href]')); + // if a section is sufficiently visible, activate its link + if (bestId && bestRatio > 0.15) { + const targetLink = navLinks.find(l => { + try { + return new URL(l.getAttribute('href'), document.location.href).hash === '#' + bestId; + } catch (e) { return false; } + }); + if (targetLink) { + navLinks.forEach(l => { l.classList.remove('active'); l.removeAttribute('aria-current'); }); + targetLink.classList.add('active'); + targetLink.setAttribute('aria-current', 'page'); + return; + } + } + + // fallback: set Accueil active when no section is visible + const indexLink = Array.from(navLinks).find(l => /index\.html$|\/$/.test(l.getAttribute('href') || '')); + navLinks.forEach(l => { l.classList.remove('active'); l.removeAttribute('aria-current'); }); + if (indexLink) { indexLink.classList.add('active'); indexLink.setAttribute('aria-current', 'page'); } + + }, { root: null, threshold: [0, 0.15, 0.3, 0.6] }); + + sections.forEach(s => { + homeVisibility[s.id] = 0; + homeSectionObserver.observe(s.el); + }); + } + + // initialize observer on load and update when navigation changes + initHomeSectionObserver(); + window.addEventListener('resize', () => { if (homeSectionObserver) initHomeSectionObserver(); }); + // re-init observer when URL path changes (i.e., leave/enter index) + const originalSetActive = setActiveNavLink; + function wrappedSetActive() { + // if leaving index, disconnect observer + const path = normalizePath(window.location.pathname); + if (path !== '/') { + if (homeSectionObserver) { homeSectionObserver.disconnect(); homeSectionObserver = null; homeVisibility = {}; } + originalSetActive(); + return; + } + // on index, ensure observer is active and let it control the highlight where possible + initHomeSectionObserver(); + originalSetActive(); + } + + // replace handlers so navigation updates use our wrapped variant + window.removeEventListener('hashchange', setActiveNavLink); + window.removeEventListener('popstate', setActiveNavLink); + window.addEventListener('hashchange', wrappedSetActive); + window.addEventListener('popstate', wrappedSetActive); + + // Reveal on scroll + const revealElements = document.querySelectorAll('.reveal'); + function revealOnScroll() { + if (!revealElements.length) return; + revealElements.forEach(el => { + const elementTop = el.getBoundingClientRect().top; + if (elementTop < window.innerHeight - 100) { + el.classList.add('active'); + } + }); + } + window.addEventListener('scroll', revealOnScroll); + 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(); + }); + } +}); \ No newline at end of file diff --git a/assets/js/pages/404.js b/assets/js/pages/404.js new file mode 100644 index 0000000..ad32a23 --- /dev/null +++ b/assets/js/pages/404.js @@ -0,0 +1,209 @@ +feather.replace(); + +// Particles +const particlesContainer = document.getElementById('particles'); +for (let i = 0; i < 30; i++) { + const particle = document.createElement('div'); + particle.classList.add('particle'); + particle.style.left = Math.random() * 100 + '%'; + particle.style.animationDelay = Math.random() * 10 + 's'; + particle.style.animationDuration = (Math.random() * 5 + 8) + 's'; + particle.style.width = (Math.random() * 4 + 2) + 'px'; + particle.style.height = particle.style.width; + particlesContainer.appendChild(particle); +} + +// Easter Egg: Konami Code (desktop) ou 10 taps (mobile) +const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a']; +let konamiIndex = 0; +let tapCount = 0; +let tapTimer = null; +const REQUIRED_TAPS = 10; +const errorCodeEl = document.querySelector('.error-code'); + +// Desktop: Konami Code +document.addEventListener('keydown', (e) => { + if (e.key === konamiCode[konamiIndex]) { + konamiIndex++; + if (konamiIndex === konamiCode.length) { + startPortalTransition(); + konamiIndex = 0; + } + } else { + konamiIndex = 0; + } +}); + +// Mobile: 10 taps +function handleTap(e) { + if (e.type === 'touchend') e.preventDefault(); + tapCount++; + + errorCodeEl.style.transform = 'scale(0.95)'; + setTimeout(() => errorCodeEl.style.transform = '', 100); + + if (tapCount >= 3 && tapCount < REQUIRED_TAPS) { + showTapCounter(tapCount, REQUIRED_TAPS); + } + + if (tapTimer) clearTimeout(tapTimer); + tapTimer = setTimeout(() => { tapCount = 0; hideTapCounter(); }, 500); + + if (tapCount >= REQUIRED_TAPS) { + tapCount = 0; + hideTapCounter(); + startPortalTransition(); + } +} + +function showTapCounter(current, total) { + let counter = document.getElementById('tap-counter'); + if (!counter) { + counter = document.createElement('div'); + counter.id = 'tap-counter'; + counter.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);background:rgba(139,58,98,0.9);color:white;padding:0.5rem 1rem;border-radius:20px;font-size:0.85rem;font-weight:600;z-index:1000;'; + document.body.appendChild(counter); + } + counter.textContent = current + ' / ' + total + ' 🔓'; +} + +function hideTapCounter() { + const counter = document.getElementById('tap-counter'); + if (counter) counter.remove(); +} + +errorCodeEl.addEventListener('click', handleTap); +errorCodeEl.addEventListener('touchend', handleTap); + +// Portal Transition +function startPortalTransition() { + const transition = document.createElement('div'); + transition.id = 'portal-transition'; + transition.innerHTML = '
ENTRÉE DANS LA MATRICE
'; + document.body.appendChild(transition); + + const style = document.createElement('style'); + style.id = 'transition-styles'; + style.textContent = '#portal-transition{position:fixed;inset:0;z-index:10000;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden}.warp-container{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;perspective:500px}.warp-tunnel{position:absolute;width:200vmax;height:200vmax;background:repeating-conic-gradient(from 0deg,transparent 0deg 10deg,rgba(139,58,98,0.1) 10deg 20deg);animation:tunnelSpin 2s linear infinite,tunnelZoom 3s ease-in forwards}@keyframes tunnelSpin{to{transform:rotate(360deg)}}@keyframes tunnelZoom{0%{transform:rotate(0)scale(1);opacity:0}20%{opacity:1}100%{transform:rotate(720deg)scale(20);opacity:0}}.warp-stars{position:absolute;inset:0;background-image:radial-gradient(2px 2px at 20px 30px,#fff,transparent),radial-gradient(2px 2px at 40px 70px,rgba(255,255,255,0.8),transparent),radial-gradient(1px 1px at 90px 40px,#fff,transparent);background-size:350px 200px;animation:starsWarp 3s ease-in forwards}@keyframes starsWarp{0%{transform:scale(1);opacity:0}30%{opacity:1}100%{transform:scale(50);opacity:0}}.warp-center{position:relative;width:150px;height:150px;animation:centerPulse 0.5s ease-in-out infinite,centerGrow 2.5s ease-in forwards}@keyframes centerPulse{0%,100%{filter:brightness(1)}50%{filter:brightness(1.5)}}@keyframes centerGrow{0%{transform:scale(0);opacity:0}20%{transform:scale(1);opacity:1}80%{transform:scale(1);opacity:1}100%{transform:scale(100);opacity:1}}.warp-ring{position:absolute;inset:0;border:4px solid #8b3a62;border-radius:50%;animation:ringPulse 1s ease-in-out infinite;box-shadow:0 0 30px #8b3a62,inset 0 0 30px #8b3a62}.warp-ring:nth-child(2){inset:20px;border-color:#5e2a8b;animation-delay:0.2s;box-shadow:0 0 30px #5e2a8b,inset 0 0 30px #5e2a8b}.warp-ring:nth-child(3){inset:40px;border-color:#d4a5c9;animation-delay:0.4s;box-shadow:0 0 30px #d4a5c9,inset 0 0 30px #d4a5c9}@keyframes ringPulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.1);opacity:0.8}}.warp-flash{position:absolute;inset:0;background:#fff;opacity:0;animation:flashBang 0.5s ease-out 2.5s forwards}@keyframes flashBang{0%{opacity:0}50%{opacity:1}100%{opacity:0}}.warp-text{position:absolute;font-size:1.5rem;font-weight:700;color:#fff;letter-spacing:0.3rem;z-index:10;opacity:0;animation:textAppear 0.5s ease 0.5s forwards,textFade 0.5s ease 2s forwards}@keyframes textAppear{to{opacity:1}}@keyframes textFade{to{opacity:0;transform:scale(2)}}'; + document.head.appendChild(style); + + setTimeout(() => { + transition.remove(); + style.remove(); + launchGameOfLife(); + }, 3000); +} + +// Game of Life +function launchGameOfLife() { + const gameContainer = document.createElement('div'); + gameContainer.id = 'game-of-life'; + gameContainer.innerHTML = '

🧬 Dimension Secrète

Le Jeu de la Vie de Conway

Règles : Cellule vivante avec 2-3 voisins survit. Cellule morte avec 3 voisins naît.

30px
0
0
0×0
'; + document.body.appendChild(gameContainer); + + const gameStyles = document.createElement('style'); + gameStyles.id = 'gol-styles'; + gameStyles.textContent = '#game-of-life{position:fixed;inset:0;z-index:10000;background:#050508;animation:golIn 0.5s ease}@keyframes golIn{from{opacity:0}to{opacity:1}}#gol-canvas{position:absolute;inset:0;width:100%;height:100%;cursor:crosshair}.gol-ui{position:absolute;top:20px;left:20px;background:rgba(10,10,15,0.95);backdrop-filter:blur(10px);border:1px solid rgba(139,58,98,0.3);border-radius:16px;padding:1rem;min-width:280px;box-shadow:0 20px 60px rgba(0,0,0,0.5)}.gol-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:0.75rem}.gol-header-text h1{font-size:1.1rem;margin-bottom:0.1rem;background:linear-gradient(135deg,#d4a5c9,#8b3a62);-webkit-background-clip:text;-webkit-text-fill-color:transparent}.gol-header-text p{font-size:0.7rem;color:#6b6b7b}.gol-minimize{width:32px;height:32px;border:1px solid rgba(255,255,255,0.1);background:rgba(255,255,255,0.05);border-radius:8px;color:#b8b8c8;cursor:pointer;display:flex;align-items:center;justify-content:center}.gol-minimize:hover{background:rgba(139,58,98,0.3);border-color:#8b3a62;color:#fff}.gol-minimize svg{width:16px;height:16px}.gol-minimize .icon-max{display:none}.gol-ui.minimized .gol-body,.gol-ui.minimized .gol-exit{display:none}.gol-ui.minimized .gol-minimize .icon-min{display:none}.gol-ui.minimized .gol-minimize .icon-max{display:block}.gol-ui.minimized{min-width:auto}.gol-body{display:flex;flex-direction:column;gap:0.75rem}.gol-description{background:rgba(139,58,98,0.1);border:1px solid rgba(139,58,98,0.2);border-radius:8px;padding:0.5rem}.gol-description p{font-size:0.7rem;color:#b8b8c8;margin:0}.gol-description strong{color:#d4a5c9}.gol-controls{display:flex;gap:0.4rem;flex-wrap:wrap}.gol-btn{width:40px;height:40px;border:1px solid rgba(255,255,255,0.1);background:rgba(255,255,255,0.05);border-radius:8px;color:#b8b8c8;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s}.gol-btn:hover{background:rgba(139,58,98,0.3);border-color:#8b3a62;color:#fff;transform:translateY(-2px)}.gol-btn svg{width:18px;height:18px}.gol-btn.active{background:linear-gradient(135deg,#8b3a62,#5e2a8b);border-color:transparent;color:#fff}.gol-btn .icon-play{display:none}.gol-btn .icon-pause{display:block}.gol-btn:not(.active) .icon-play{display:block}.gol-btn:not(.active) .icon-pause{display:none}.gol-btn-sm{width:32px;height:32px;font-size:1.2rem;font-weight:bold}.gol-zoom-row,.gol-speed-row{display:flex;align-items:center;gap:0.5rem}.gol-zoom-row span{color:#d4a5c9;font-family:monospace;font-size:0.85rem;min-width:40px;text-align:center}.gol-speed-row{flex-direction:column;align-items:stretch}.gol-speed-row label{font-size:0.75rem;color:#b8b8c8}.gol-speed-row label span{color:#d4a5c9;font-family:monospace}.gol-speed-row input{width:100%;height:6px;-webkit-appearance:none;background:rgba(255,255,255,0.1);border-radius:3px;cursor:pointer}.gol-speed-row input::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;background:linear-gradient(135deg,#8b3a62,#5e2a8b);border-radius:50%;cursor:pointer}.gol-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem}.gol-stat{text-align:center;padding:0.4rem;background:rgba(255,255,255,0.03);border-radius:6px}.gol-stat span{font-size:1rem;font-weight:700;color:#d4a5c9;font-family:monospace;display:block}.gol-stat label{font-size:0.6rem;color:#6b6b7b;text-transform:uppercase}.gol-exit{width:100%;margin-top:0.5rem;padding:0.6rem;background:transparent;border:1px solid rgba(255,255,255,0.1);border-radius:8px;color:#6b6b7b;font-size:0.8rem;cursor:pointer;transition:all 0.2s}.gol-exit:hover{background:rgba(255,50,50,0.2);border-color:rgba(255,50,50,0.5);color:#ff6b6b}@media(max-width:768px){.gol-ui{top:auto;bottom:0;left:0;right:0;border-radius:16px 16px 0 0;max-height:50vh;overflow-y:auto;min-width:auto}.gol-header-text p,.gol-description{display:none}.gol-ui.minimized{max-height:none}}@media(max-width:480px){.gol-ui{padding:0.75rem}.gol-btn{width:36px;height:36px}.gol-btn-sm{width:28px;height:28px}}'; + document.head.appendChild(gameStyles); + + const canvas = document.getElementById('gol-canvas'); + const ctx = canvas.getContext('2d'); + const MAX_CELLS = 150, MIN_SIZE = 4, MAX_SIZE = 30; + let cellSize = MAX_SIZE, minCellSize = MIN_SIZE, cols, rows, grid, nextGrid; + let isRunning = true, generation = 0, speed = 15, lastTime = 0, isDrawing = false, drawValue = 1; + + function initCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + minCellSize = Math.max(MIN_SIZE, Math.ceil(canvas.width / MAX_CELLS), Math.ceil(canvas.height / MAX_CELLS)); + cellSize = Math.max(minCellSize, Math.min(MAX_SIZE, cellSize)); + cols = Math.ceil(canvas.width / cellSize); + rows = Math.ceil(canvas.height / cellSize); + grid = Array(cols).fill(null).map(() => Array(rows).fill(0)); + nextGrid = Array(cols).fill(null).map(() => Array(rows).fill(0)); + updateUI(); + } + + function addGlider(sx, sy, dir) { + const p = [[[0,1],[1,2],[2,0],[2,1],[2,2]],[[0,1],[1,0],[2,0],[2,1],[2,2]],[[0,0],[0,1],[0,2],[1,2],[2,1]],[[0,0],[0,1],[0,2],[1,0],[2,1]]]; + p[dir % 4].forEach(([dx, dy]) => { const x = (sx + dx) % cols, y = (sy + dy) % rows; if (x >= 0 && x < cols && y >= 0 && y < rows) grid[x][y] = 1; }); + } + + function addGliders(n) { + grid = Array(cols).fill(null).map(() => Array(rows).fill(0)); + for (let i = 0; i < n; i++) addGlider(Math.floor(Math.random() * (cols - 10)) + 5, Math.floor(Math.random() * (rows - 10)) + 5, Math.floor(Math.random() * 4)); + generation = 0; updateUI(); + } + + function randomize() { for (let x = 0; x < cols; x++) for (let y = 0; y < rows; y++) grid[x][y] = Math.random() > 0.7 ? 1 : 0; generation = 0; updateUI(); } + function clear() { for (let x = 0; x < cols; x++) for (let y = 0; y < rows; y++) grid[x][y] = 0; generation = 0; updateUI(); } + + function step() { + for (let x = 0; x < cols; x++) for (let y = 0; y < rows; y++) { + let n = 0; + for (let i = -1; i <= 1; i++) for (let j = -1; j <= 1; j++) if (i || j) n += grid[(x + i + cols) % cols][(y + j + rows) % rows]; + nextGrid[x][y] = grid[x][y] ? (n === 2 || n === 3 ? 1 : 0) : (n === 3 ? 1 : 0); + } + [grid, nextGrid] = [nextGrid, grid]; generation++; updateUI(); + } + + function draw() { + ctx.fillStyle = '#0a0a0f'; ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.strokeStyle = 'rgba(139,58,98,0.1)'; ctx.lineWidth = 0.5; + for (let x = 0; x <= cols * cellSize; x += cellSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, rows * cellSize); ctx.stroke(); } + for (let y = 0; y <= rows * cellSize; y += cellSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(cols * cellSize, y); ctx.stroke(); } + ctx.shadowColor = 'rgba(139,58,98,0.5)'; ctx.shadowBlur = 8; + for (let x = 0; x < cols; x++) for (let y = 0; y < rows; y++) if (grid[x][y]) { + const g = ctx.createRadialGradient(x * cellSize + cellSize / 2, y * cellSize + cellSize / 2, 0, x * cellSize + cellSize / 2, y * cellSize + cellSize / 2, cellSize); + g.addColorStop(0, '#d4a5c9'); g.addColorStop(0.5, '#8b3a62'); g.addColorStop(1, '#5e2a8b'); + ctx.fillStyle = g; ctx.fillRect(x * cellSize + 1, y * cellSize + 1, cellSize - 2, cellSize - 2); + } + ctx.shadowBlur = 0; + } + + function updateUI() { + let pop = 0; for (let x = 0; x < cols; x++) for (let y = 0; y < rows; y++) pop += grid[x][y]; + document.getElementById('gol-gen').textContent = generation; + document.getElementById('gol-pop').textContent = pop; + document.getElementById('gol-dim').textContent = cols + '×' + rows; + document.getElementById('gol-zoom-val').textContent = cellSize + 'px'; + document.getElementById('gol-speed-val').textContent = speed; + } + + function handleZoom(d) { + const old = cellSize; cellSize = Math.max(minCellSize, Math.min(MAX_SIZE, cellSize + d)); + if (cellSize !== old) { const og = grid, oc = cols, or = rows; initCanvas(); for (let x = 0; x < Math.min(oc, cols); x++) for (let y = 0; y < Math.min(or, rows); y++) grid[x][y] = og[x][y]; updateUI(); } + } + + function getCell(e) { + const r = canvas.getBoundingClientRect(), cx = e.touches ? e.touches[0].clientX : e.clientX, cy = e.touches ? e.touches[0].clientY : e.clientY; + return { x: Math.max(0, Math.min(cols - 1, Math.floor((cx - r.left) / cellSize))), y: Math.max(0, Math.min(rows - 1, Math.floor((cy - r.top) / cellSize))) }; + } + + function gameLoop(t) { if (isRunning && t - lastTime > 1000 / speed) { step(); lastTime = t; } draw(); requestAnimationFrame(gameLoop); } + + canvas.addEventListener('mousedown', e => { isDrawing = true; const c = getCell(e); drawValue = grid[c.x][c.y] ? 0 : 1; grid[c.x][c.y] = drawValue; }); + canvas.addEventListener('mousemove', e => { if (isDrawing) { const c = getCell(e); grid[c.x][c.y] = drawValue; } }); + canvas.addEventListener('mouseup', () => isDrawing = false); + canvas.addEventListener('mouseleave', () => isDrawing = false); + canvas.addEventListener('touchstart', e => { e.preventDefault(); isDrawing = true; const c = getCell(e); drawValue = grid[c.x][c.y] ? 0 : 1; grid[c.x][c.y] = drawValue; }, { passive: false }); + canvas.addEventListener('touchmove', e => { e.preventDefault(); if (isDrawing) { const c = getCell(e); grid[c.x][c.y] = drawValue; } }, { passive: false }); + canvas.addEventListener('touchend', () => isDrawing = false); + canvas.addEventListener('wheel', e => { e.preventDefault(); handleZoom(e.deltaY > 0 ? -1 : 1); }); + + document.getElementById('gol-play').addEventListener('click', function() { isRunning = !isRunning; this.classList.toggle('active', isRunning); }); + document.getElementById('gol-step').addEventListener('click', () => { if (!isRunning) step(); }); + document.getElementById('gol-random').addEventListener('click', randomize); + document.getElementById('gol-clear').addEventListener('click', clear); + document.getElementById('gol-gliders').addEventListener('click', () => addGliders(3)); + document.getElementById('gol-zoom-in').addEventListener('click', () => handleZoom(2)); + document.getElementById('gol-zoom-out').addEventListener('click', () => handleZoom(-2)); + document.getElementById('gol-speed').addEventListener('input', function() { speed = parseInt(this.value); updateUI(); }); + document.getElementById('gol-minimize').addEventListener('click', () => document.querySelector('.gol-ui').classList.toggle('minimized')); + document.getElementById('gol-exit').addEventListener('click', () => { document.getElementById('game-of-life').remove(); document.getElementById('gol-styles').remove(); }); + window.addEventListener('resize', () => { const og = grid, oc = cols, or = rows; initCanvas(); for (let x = 0; x < Math.min(oc, cols); x++) for (let y = 0; y < Math.min(or, rows); y++) grid[x][y] = og[x][y]; updateUI(); }); + + initCanvas(); + addGliders(3); + requestAnimationFrame(gameLoop); +} \ No newline at end of file diff --git a/assets/js/pages/legal.js b/assets/js/pages/legal.js new file mode 100644 index 0000000..67b345f --- /dev/null +++ b/assets/js/pages/legal.js @@ -0,0 +1,31 @@ +// assets/js/pages/legal.js - minimal fallback for legal pages +document.addEventListener('DOMContentLoaded', () => { + // replace feather icons if available (use centralized loader if present) + if (typeof window.ensureFeather === 'function') { + window.ensureFeather().then(() => { try { if (window.feather && feather.replace) feather.replace(); } catch (e) {} }).catch(()=>{}); + } else if (window.feather && feather.replace) { + try { feather.replace(); } catch (e) { /* ignore */ } + } + + // theme fallback (will be overwritten by assets/js/main.js if present) + const html = document.documentElement; + if (!html.getAttribute('data-theme')) html.setAttribute('data-theme', localStorage.getItem('theme') || 'dark'); + const themeToggle = document.getElementById('theme-toggle'); + if (themeToggle) themeToggle.addEventListener('click', () => { + const newTheme = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; + html.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + }); + + // navbar scrolled + const navbar = document.getElementById('navbar'); + if (navbar) window.addEventListener('scroll', () => navbar.classList.toggle('scrolled', window.scrollY > 50)); + + // mobile toggle (minimal) + const mobileToggle = document.getElementById('mobile-toggle'); + const navLinks = document.getElementById('nav-links'); + if (mobileToggle && navLinks) mobileToggle.addEventListener('click', () => { + mobileToggle.classList.toggle('active'); + navLinks.classList.toggle('active'); + }); +}); \ No newline at end of file diff --git a/assets/js/pages/realisations.js b/assets/js/pages/realisations.js new file mode 100644 index 0000000..eb95d55 --- /dev/null +++ b/assets/js/pages/realisations.js @@ -0,0 +1,228 @@ +// Page script for /pages/realisations.html + +document.addEventListener('DOMContentLoaded', async () => { + const dataUrl = '../assets/data/projects.json'; + let allProjects = []; + + try { + const res = await fetch(dataUrl); + if (!res.ok) throw new Error(res.statusText); + allProjects = await res.json(); + } catch (err) { + console.error('Failed to load projects data:', err); + return; + } + + const ITEMS_PER_PAGE = 9; + let currentPage = 1; + let currentFilter = 'all'; + + const portfolioGrid = document.getElementById('portfolio-grid'); + const paginationContainer = document.getElementById('pagination'); + const filterTabs = document.querySelectorAll('.filter-tab'); + + const modalOverlay = document.getElementById('project-modal-overlay'); + const modalClose = document.getElementById('project-modal-close'); + const gallerySlides = document.getElementById('gallery-slides'); + const galleryPrev = document.getElementById('gallery-prev'); + const galleryNext = document.getElementById('gallery-next'); + + const modalTitle = document.getElementById('modal-title'); + const modalCategory = document.getElementById('modal-category'); + const modalDesc = document.getElementById('modal-desc'); + const modalMeta = document.getElementById('modal-meta'); + const modalFeatures = document.getElementById('modal-features'); + const modalTech = document.getElementById('modal-tech'); + const modalStats = document.getElementById('modal-stats'); + + function getFilteredProjects() { + if (currentFilter === 'all') return [...allProjects]; + return allProjects.filter(p => p.category === currentFilter); + } + + function getTotalPages(filtered) { + return Math.ceil(filtered.length / ITEMS_PER_PAGE) || 1; + } + + function getProjectsForPage(filtered, page) { + const start = (page - 1) * ITEMS_PER_PAGE; + return filtered.slice(start, start + ITEMS_PER_PAGE); + } + + function getCategoryLabel(category) { + const labels = { 'bot': 'Bot Discord', 'map': 'Fortnite Map', 'graphisme': 'Graphisme' }; + return labels[category] || category; + } + + function renderProjects() { + const filtered = getFilteredProjects(); + const projects = getProjectsForPage(filtered, currentPage); + + portfolioGrid.innerHTML = projects.map(project => ` +
+
+
+ ${getCategoryLabel(project.category)} +

${project.title}

+

${project.desc.substring(0, 80)}...

+
+
+ `).join(''); + + // activate reveals & icons + setTimeout(() => { + document.querySelectorAll('.portfolio-item.reveal').forEach(el => el.classList.add('active')); + }, 80); + + // ensure icons are replaced for newly injected content + if (window.feather && feather.replace) { + try { feather.replace(); } catch (e) {} + } + } + + function renderPagination() { + const filtered = getFilteredProjects(); + const totalPages = getTotalPages(filtered); + + if (totalPages <= 1) { + paginationContainer.innerHTML = ''; + return; + } + + let html = ``; + + const maxVisible = 5; + let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2)); + let endPage = Math.min(totalPages, startPage + maxVisible - 1); + if (endPage - startPage < maxVisible - 1) startPage = Math.max(1, endPage - maxVisible + 1); + + if (startPage > 1) { + html += ``; + if (startPage > 2) html += `...`; + } + + for (let i = startPage; i <= endPage; i++) { + html += ``; + } + + if (endPage < totalPages) { + if (endPage < totalPages - 1) html += `...`; + html += ``; + } + + html += ``; + + const startItem = (currentPage - 1) * ITEMS_PER_PAGE + 1; + const endItem = Math.min(currentPage * ITEMS_PER_PAGE, filtered.length); + html += `
Affichage ${startItem}-${endItem} sur ${filtered.length} projets
`; + + paginationContainer.innerHTML = html; + + if (window.feather && feather.replace) { + try { feather.replace(); } catch (e) {} + } + } + + function goToPage(page) { + const filtered = getFilteredProjects(); + const totalPages = getTotalPages(filtered); + if (page < 1 || page > totalPages) return; + currentPage = page; + renderProjects(); + renderPagination(); + portfolioGrid.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + + function openProjectModal(projectId) { + const project = allProjects.find(p => p.id === Number(projectId)); + if (!project) return; + + modalCategory.textContent = getCategoryLabel(project.category); + modalTitle.textContent = project.title; + modalDesc.textContent = project.desc; + + modalMeta.innerHTML = ` ${project.year} ${project.duration}`; + + modalFeatures.innerHTML = project.features.map(f => `
${f}
`).join(''); + modalTech.innerHTML = project.tech.map(t => `
${t}
`).join(''); + modalStats.innerHTML = project.stats.map(s => ` +
+
${s.value}
+
${s.label}
+
+ `).join(''); + + // gallery slides (use gradient as placeholder slides like original) + gallerySlides.innerHTML = ` + + + + `; + + // reset gallery state + galleryIndex = 0; + updateGallery(); + + if (modalOverlay) modalOverlay.classList.add('active'); + + if (window.feather && feather.replace) { + try { feather.replace(); } catch (e) {} + } + } + + function closeProjectModal() { + if (modalOverlay) modalOverlay.classList.remove('active'); + } + + // Gallery helpers + let galleryIndex = 0; + function updateGallery() { + const slides = gallerySlides ? Array.from(gallerySlides.children) : []; + slides.forEach((s, i) => { + s.style.transform = `translateX(${(i - galleryIndex) * 100}%)`; + s.classList.toggle('active', i === galleryIndex); + }); + } + + 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(); } + + // Event bindings + filterTabs.forEach(tab => tab.addEventListener('click', (e) => { + filterTabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + currentFilter = tab.dataset.filter || 'all'; + currentPage = 1; + renderProjects(); + renderPagination(); + })); + + paginationContainer.addEventListener('click', (e) => { + const btn = e.target.closest('button[data-page]'); + if (!btn) return; + const page = Number(btn.getAttribute('data-page')); + goToPage(page); + }); + + portfolioGrid.addEventListener('click', (e) => { + const item = e.target.closest('.portfolio-item'); + if (!item) return; + const id = item.getAttribute('data-project-id'); + openProjectModal(id); + }); + + if (modalClose) modalClose.addEventListener('click', closeProjectModal); + if (modalOverlay) modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeProjectModal(); }); + window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeProjectModal(); }); + + if (galleryPrev) galleryPrev.addEventListener('click', prevSlide); + if (galleryNext) galleryNext.addEventListener('click', nextSlide); + + // Expose for compatibility + window.openProjectModal = openProjectModal; + window.goToPage = goToPage; + + // Initial render + renderProjects(); + renderPagination(); +}); diff --git a/assets/js/pages/services.js b/assets/js/pages/services.js new file mode 100644 index 0000000..494b7cd --- /dev/null +++ b/assets/js/pages/services.js @@ -0,0 +1,57 @@ +// Services page scripts (moved from inline in pages/services.html) +// Depends on ../main.js which initializes feather and other globals + +(function() { + document.addEventListener('DOMContentLoaded', () => { + // Timeline Animation + const timeline = document.getElementById('process-timeline'); + const steps = document.querySelectorAll('.process-step'); + + function animateTimeline() { + if (!timeline) return; + const timelineTop = timeline.getBoundingClientRect().top; + + if (timelineTop < window.innerHeight - 200) { + timeline.classList.add('animated'); + + // Animate steps sequentially + steps.forEach((step, index) => { + setTimeout(() => { + step.classList.add('active'); + }, index * 400); + }); + } + } + + window.addEventListener('scroll', animateTimeline); + window.addEventListener('load', animateTimeline); + + // Service Tabs + const tabBtns = document.querySelectorAll('.service-tab-btn'); + const tabContents = document.querySelectorAll('.service-tab-content'); + + if (tabBtns.length && tabContents.length) { + tabBtns.forEach(btn => { + btn.addEventListener('click', () => { + const tabId = btn.dataset.tab; + + // Update buttons + tabBtns.forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + + // Update content + tabContents.forEach(content => { + content.classList.remove('active'); + if (content.id === 'tab-' + tabId) { + content.classList.add('active'); + } + }); + + // Re-init feather icons for new content (safe if feather is loaded) + if (window.feather) window.feather.replace(); + }); + }); + } + + }); +})(); diff --git a/components/favicons.html b/components/favicons.html new file mode 100644 index 0000000..de4e503 --- /dev/null +++ b/components/favicons.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/components/footer.html b/components/footer.html new file mode 100644 index 0000000..b407fe5 --- /dev/null +++ b/components/footer.html @@ -0,0 +1,39 @@ + + diff --git a/components/header.html b/components/header.html new file mode 100644 index 0000000..7d7ef22 --- /dev/null +++ b/components/header.html @@ -0,0 +1,39 @@ + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..d7d2142 --- /dev/null +++ b/index.html @@ -0,0 +1,289 @@ + + + + + + OmégaKube — L'univers du Role Play réinventé + + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+ + +
+
+
+ Créateurs d'univers RP +
+

+ L'univers du
+ Role Play
+ réinventé +

+

Bots Discord personnalisés, maps Fortnite immersives — des expériences sur mesure pour vos communautés gaming.

+ +
+
+ + +
+
+ +

Qui sommes-nous ?

+

Une équipe passionnée dédiée à la création d'expériences RP uniques

+
+
+
+

Notre Mission

+

OmégaKube est né de la passion du Role Play et de l'envie de créer des outils innovants pour les communautés gaming. Nous développons des solutions sur mesure qui transforment l'expérience de jeu.

+

Notre expertise s'étend des bots Discord complexes aux environnements Fortnite immersifs, en passant par la création graphique. Chaque projet est une nouvelle aventure créative.

+
+
+
50+
+
Projets réalisés
+
+
+
10K+
+
Joueurs actifs
+
+
+
3
+
Ans d'expérience
+
+
+
+
+
+

Notre Objectif

+

Créer des outils et environnements RP simples d'utilisation, immersifs et parfaitement adaptés à tous types de serveurs et projets. Chaque communauté mérite une expérience unique qui reflète son identité.

+
+
+
+
+
+ + +
+
+ +

Ce que nous créons

+

Des solutions complètes pour vos projets Role Play

+
+
+
+
+ +
+

Bots Discord RP

+

Des bots entièrement personnalisés pour animer et gérer vos serveurs RP.

+
    +
  • Systèmes d'économie avancés
  • +
  • Gestion des stats et inventaires
  • +
  • Commandes RP personnalisées
  • +
+
+
+
+ +
+

Maps Fortnite RP

+

Des environnements uniques conçus pour des sessions RP mémorables.

+
    +
  • Design d'environnement immersif
  • +
  • Scénarios narratifs intégrés
  • +
  • Optimisation performance
  • +
+
+
+
+ +
+

Créations Graphiques

+

Identité visuelle et assets graphiques pour vos projets.

+
    +
  • Logos et branding
  • +
  • Bannières et visuels
  • +
  • Assets personnalisés
  • +
+
+
+ +
+ + +
+
+ +

Les créateurs

+

Cliquez sur un membre pour en savoir plus

+
+ +
+ + +
+
+
+ +
+ +
+ + +
+
+ + + + +
+
+
+ + +
+
+ +

Nos réalisations

+

Découvrez quelques-uns de nos projets récents

+
+
+
+
+
+ Bot Discord +

Medieval RP Bot

+

Système complet pour serveur fantasy

+
+
+
+
+
+ Fortnite Map +

Cyberpunk City

+

Environnement urbain futuriste

+
+
+
+
+
+ Bot Discord +

Space Station RP

+

Bot Sci-Fi avec économie spatiale

+
+
+
+ +
+ + +
+
+ +

Parlons de votre projet

+

Un projet RP en tête ? Contactez-nous !

+
+
+
+

Discutons ensemble

+

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.

+ +
+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+ + + + + diff --git a/pages/404.html b/pages/404.html new file mode 100644 index 0000000..aecd028 --- /dev/null +++ b/pages/404.html @@ -0,0 +1,61 @@ + + + + + + 404 — Page non trouvée | OmégaKube + + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
404
+

Dimension introuvable

+

+ Oups ! Il semblerait que vous ayez traversé un portail défectueux. + Cette page n'existe pas dans notre univers... ou peut-être a-t-elle été téléportée ailleurs ? +

+ +
+ + + + + + diff --git a/pages/cgv.html b/pages/cgv.html new file mode 100644 index 0000000..eb2c76a --- /dev/null +++ b/pages/cgv.html @@ -0,0 +1,256 @@ + + + + + + Conditions Générales de Vente — OmégaKube + + + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ + +
+ +
+
+

Conditions Générales de Vente

+

Conditions applicables aux prestations de l'association OmégaKube

+
+
+ + + +
+ + + + + + diff --git a/pages/mentions-legales.html b/pages/mentions-legales.html new file mode 100644 index 0000000..f312a48 --- /dev/null +++ b/pages/mentions-legales.html @@ -0,0 +1,163 @@ + + + + + + Mentions Légales — OmégaKube + + + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ + +
+ +
+
+

Mentions Légales

+

Informations légales relatives à l'association OmégaKube

+
+
+ + + +
+ + + + + + diff --git a/pages/realisations.html b/pages/realisations.html new file mode 100644 index 0000000..327051a --- /dev/null +++ b/pages/realisations.html @@ -0,0 +1,334 @@ + + + + + + Nos Réalisations — OmégaKube + + + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+ + +
+
+
+ Notre portfolio +
+

Nos Réalisations

+

Découvrez les projets que nous avons concrétisés pour nos clients et partenaires.

+
+
+ + +
+
+ +

Projets phares

+

Nos réalisations les plus marquantes

+
+ +
+ + +
+
+
+ Bot Discord +

Medieval RP Bot

+

Un système RP médiéval complet pour serveur fantasy

+
+
+
+
+
+ +
+
5000+
+
Joueurs actifs
+
+
+
+ +
+
3 mois
+
Développement
+
+
+
+ +
+
150+
+
Commandes
+
+
+
+ +
+
99.9%
+
Uptime
+
+
+
+

Description

+

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.

+
+ Économie + Combat + Métiers + Royaumes + Quêtes + Events +
+
+
+ +
+ + +
+
+
+ Fortnite Map +

Cyberpunk City

+

Une ville futuriste immersive pour le RP urbain

+
+
+
+
+
+ +
+
50K+
+
Parties jouées
+
+
+
+ +
+
100+
+
Bâtiments
+
+
+
+ +
+
5
+
Quartiers
+
+
+
+ +
+
50
+
Joueurs max
+
+
+
+

Description

+

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.

+
+ Néons + Jour/Nuit + Scripts + Optimisé + Immersif +
+
+
+ +
+ + +
+
+
+ Pack Complet +

Nova RP - Lancement

+

Accompagnement complet pour un lancement réussi

+
+
+
+
+
+ +
+
8000+
+
Membres
+
+
+
+ +
+
0→8K
+
En 3 mois
+
+
+
+ +
+
4
+
Services inclus
+
+
+
+ +
+
Top 10
+
Serveur FR
+
+
+
+

Description

+

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.

+
+ Bot RP + Map Fortnite + Branding + Stratégie + Support +
+
+
+ +
+
+ + +
+
+ +

Tous nos projets

+

Cliquez sur un projet pour voir les détails

+
+ + +
+
+ + + + +
+
+ + +
+ +
+ + + +
+ + +
+
+ + + + +
+
+
+ Bot Discord + +
+ +
+ + + +
+ + + +
+ +
+
+
+
+
+
+ + +
+
+ +

Prêt à créer ensemble ?

+

Votre projet pourrait être le prochain dans notre portfolio

+
+ +
+ +
+ + + + + diff --git a/pages/services.html b/pages/services.html new file mode 100644 index 0000000..e178a80 --- /dev/null +++ b/pages/services.html @@ -0,0 +1,741 @@ + + + + + + Nos Services — OmégaKube + + + + + + +
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+ + +
+
+
+ Ce que nous proposons +
+

Nos Services

+

Des solutions complètes et sur mesure pour donner vie à vos projets Role Play les plus ambitieux.

+
+
+ + +
+
+ +

Comment ça marche ?

+

Un accompagnement clair et structuré, de l'idée à la livraison

+
+ +
+
+
+ +
+
+

Contact

+

Discutons de votre projet et de vos besoins

+
+
+ +
+
+ +
+
+

Devis

+

Proposition détaillée et validation ensemble

+
+
+ +
+
+ +
+
+

Création

+

Développement avec suivi régulier

+
+
+ +
+
+ +
+
+

Livraison

+

Tests, ajustements et support inclus

+
+
+
+
+ + +
+
+ +

Choisissez votre service

+

Cliquez sur un service pour découvrir tous les détails

+
+ +
+ +
+ + + + + +
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Bots Discord RP

+

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.

+
+ +
+
+
2-4
+
Semaines de dev
+
+
+
1 mois
+
Support inclus
+
+
+
100%
+
Personnalisable
+
+
+ +
+
+
+ +
+
+

Économie virtuelle

+

Monnaie, banque, transactions

+
+
+
+
+ +
+
+

Système de métiers

+

Jobs, skills, progression

+
+
+
+
+ +
+
+

Combat & Stats

+

PvP, PvE, attributs

+
+
+
+
+ +
+
+

Inventaire avancé

+

Items, crafting, échanges

+
+
+
+
+ +
+
+

Factions & Guildes

+

Clans, territoires, guerres

+
+
+
+
+ +
+
+

Panel Admin

+

Interface web de gestion

+
+
+
+ + +
+
+
+ + +
+
+
+ ✦ New +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Maps Fortnite RP

+

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.

+
+ +
+
+
3-6
+
Semaines de création
+
+
+
50+
+
Joueurs supportés
+
+
+
UEFN
+
Compatible
+
+
+ +
+
+
+ +
+
+

Bâtiments détaillés

+

Intérieurs accessibles et déco

+
+
+
+
+ +
+
+

Cycle jour/nuit

+

Ambiance dynamique

+
+
+
+
+ +
+
+

Zones interactives

+

Scripts et triggers

+
+
+
+
+ +
+
+

Optimisation

+

Performance garantie

+
+
+
+
+ +
+
+

Points d'intérêt

+

Lieux uniques et mémorables

+
+
+
+
+ +
+
+

Modifications

+

Ajustements post-livraison

+
+
+
+ + +
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Créations Graphiques

+

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é.

+
+ +
+
+
1-2
+
Semaines
+
+
+
∞
+
Révisions
+
+
+
HD
+
Qualité
+
+
+ +
+
+
+ +
+
+

Logo & Branding

+

Identité visuelle complète

+
+
+
+
+ +
+
+

Bannières Discord

+

Server banner, splash, icône

+
+
+
+
+ +
+
+

Emojis custom

+

Pack emojis personnalisés

+
+
+
+
+ +
+
+

Icônes & Assets

+

Items, skills, badges

+
+
+
+
+ +
+
+

Animations

+

GIFs et motion design

+
+
+
+
+ +
+
+

Guide de style

+

Charte graphique fournie

+
+
+
+ + +
+
+
+ + +
+
+
+ ✓ Gratuit +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Consulting & Accompagnement Gratuit

+

Bénéficiez de notre expérience pour structurer, lancer et développer votre projet RP. Conseils stratégiques et accompagnement personnalisé.

+
+ +
+
+
50+
+
Projets accompagnés
+
+
+
3 ans
+
D'expérience
+
+
+
24/7
+
Support Discord
+
+
+ +
+
+
+ +
+
+

Audit serveur

+

Analyse de l'existant

+
+
+
+
+ +
+
+

Stratégie croissance

+

Plan de développement

+
+
+
+
+ +
+
+

Formation staff

+

Équipe modération

+
+
+
+
+ +
+
+

Expérience joueur

+

Optimisation UX

+
+
+
+
+ +
+
+

Planning events

+

Calendrier d'animations

+
+
+
+
+ +
+
+

Monétisation

+

Conseils revenus

+
+
+
+ + +
+
+
+ + +
+
+
+ ★ Populaire +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Pack Serveur Complet -20%

+

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.

+
+ +
+
+
-20%
+
VS services séparés
+
+
+
3 mois
+
Support inclus
+
+
+
Clé en main
+
Prêt à lancer
+
+
+ +
+
+
+ +
+
+

Bot Discord RP

+

Système complet personnalisé

+
+
+
+
+ +
+
+

Map Fortnite

+

Environnement sur mesure

+
+
+
+
+ +
+
+

Pack graphique

+

Logo, bannières, emojis

+
+
+
+
+ +
+
+

Config Discord

+

Serveur prêt à l'emploi

+
+
+
+
+ +
+
+

Documentation

+

Guides et tutoriels

+
+
+
+
+ +
+
+

Support 3 mois

+

Accompagnement lancement

+
+
+
+ + +
+
+
+ +
+
+ + +
+
+ +

Pourquoi nous choisir ?

+

Ce qui fait la différence OmégaKube

+
+ +
+
+
+ +
+

Expertise RP

+

3 ans d'expérience dans l'écosystème Role Play. Nous connaissons vos besoins car nous sommes aussi des joueurs.

+
+
+
+ +
+

Suivi continu

+

Communication régulière pendant le développement. Vous suivez l'avancement et validez chaque étape.

+
+
+
+ +
+

Support inclus

+

Maintenance et assistance après livraison. Nous restons disponibles pour les ajustements et questions.

+
+
+
+ + +
+
+ +

Discutons de votre projet

+

Contactez-nous pour un devis gratuit et personnalisé

+
+ +
+ +
+ + + + +