commit 56db6fac8c81d977079e82ed28526c246011227c Author: Titouan Roger Date: Tue Feb 3 19:36:22 2026 +0100 Initial commit diff --git a/assets/avatars/florian-moutouvirin.jpeg b/assets/avatars/florian-moutouvirin.jpeg new file mode 100644 index 0000000..e6ed549 Binary files /dev/null and b/assets/avatars/florian-moutouvirin.jpeg differ diff --git a/assets/avatars/titouan-roger.jpeg b/assets/avatars/titouan-roger.jpeg new file mode 100644 index 0000000..621a88a Binary files /dev/null and b/assets/avatars/titouan-roger.jpeg differ diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..571d5ed --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,264 @@ +:root { + --color-primary: #2563eb; + --color-secondary: #0f172a; + --color-accent: #3b82f6; +} + +* { + font-family: 'Inter', sans-serif; +} + +.gradient-bg { + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); +} + +.card-hover { + transition: all 0.3s ease; +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.smooth-scroll { + scroll-behavior: smooth; +} + +.btn-primary { + background-color: var(--color-primary); + transition: all 0.3s ease; +} + +.btn-primary:hover { + background-color: var(--color-accent); + transform: scale(1.05); +} + +.nav-link { + position: relative; + transition: color 0.3s ease; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + background-color: var(--color-primary); + transition: width 0.3s ease; +} + +.nav-link:hover::after { + width: 100%; +} + +.section-title { + position: relative; + display: inline-block; +} + +.section-title::after { + content: ''; + position: absolute; + bottom: -10px; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 4px; + background-color: var(--color-primary); + border-radius: 2px; +} + +/* Animations et reveals */ +@media (prefers-reduced-motion: reduce) { + + .card-hover, + .btn-primary, + .animate-fade-in, + .reveal { + transition: none !important; + animation: none !important; + } +} + +.reveal { + opacity: 0; + transform: translateY(12px); + transition: opacity 0.6s ease, transform 0.6s ease; + will-change: opacity, transform; +} + +.animate-fade-in { + opacity: 1 !important; + transform: translateY(0) !important; +} + +/* Header scrolled state */ +.header-scrolled { + background-color: rgba(255, 255, 255, 0.98); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08); + backdrop-filter: blur(6px); +} + +/* Service icon micro-interaction */ +.service-icon { + transition: transform 0.35s ease; + display: inline-block; +} + +.card-hover:hover .service-icon { + transform: translateY(-6px) rotate(-6deg) scale(1.05); +} + +/* Buttons */ +.btn-primary { + box-shadow: 0 8px 20px rgba(37, 99, 235, 0.12); +} + +.btn-primary:focus { + outline: 3px solid rgba(37, 99, 235, 0.12); + outline-offset: 2px; +} + +/* Ensure avatars have a minimum height to avoid layout shift */ +#team-container .aspect-square { + min-height: 260px; +} + +/* Background gradient + subtle noise */ +body { + background-image: radial-gradient(circle at 10% 10%, rgba(59, 130, 246, 0.06), transparent 10%), + radial-gradient(circle at 90% 90%, rgba(99, 102, 241, 0.04), transparent 12%), + linear-gradient(180deg, #fbfdff 0%, #f8fafc 100%); + background-attachment: fixed; +} + +/* Animated floating blobs */ +.bg-blob { + position: absolute; + filter: blur(40px); + opacity: 0.18; + transform-origin: center; +} + +.blob-1 { + width: 420px; + height: 420px; + left: -80px; + top: -60px; + background: linear-gradient(135deg, rgba(37, 99, 235, 0.9), rgba(59, 130, 246, 0.6)); + animation: floatY 8s ease-in-out infinite; +} + +.blob-2 { + width: 300px; + height: 300px; + right: -60px; + bottom: -80px; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(99, 102, 241, 0.6)); + animation: floatX 9s ease-in-out infinite; +} + +@keyframes floatY { + 0% { + transform: translateY(0) + } + + 50% { + transform: translateY(18px) + } + + 100% { + transform: translateY(0) + } +} + +@keyframes floatX { + 0% { + transform: translateX(0) + } + + 50% { + transform: translateX(-18px) + } + + 100% { + transform: translateX(0) + } +} + +/* Hero illustration container */ +.hero-illustration { + position: relative; + width: 100%; + height: 420px; + display: flex; + align-items: center; + justify-content: center; +} + +.hero-illustration svg { + width: 100%; + height: 100%; + max-width: 560px; +} + +/* Testimonials */ +.testimonials { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.9)); + padding: 4rem 0; + border-radius: 12px; +} + +.testimonial-slide { + display: none; +} + +.testimonial-slide.active { + display: block; +} + +.testimonials .controls { + display: flex; + gap: 12px; + align-items: center; + justify-content: center; + margin-top: 1rem +} + +.testimonials .dot { + width: 10px; + height: 10px; + border-radius: 999px; + background: rgba(15, 23, 42, 0.12); +} + +.testimonials .dot.active { + background: var(--color-primary); + box-shadow: 0 6px 18px rgba(37, 99, 235, 0.14); +} + +/* CTA micro-animation */ +.cta-anim { + transition: transform 0.35s cubic-bezier(.2, .9, .2, 1), box-shadow 0.35s; +} + +.cta-anim:hover { + transform: translateY(-6px); + box-shadow: 0 18px 36px rgba(37, 99, 235, 0.14); +} + +/* Background blobs and hero micro styles */ +.bg-blob { + will-change: transform, opacity; +} + +/* Hero responsive */ +@media (max-width: 768px) { + .hero-illustration { + height: 320px; + } +} \ No newline at end of file diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 0000000..435657b --- /dev/null +++ b/assets/js/app.js @@ -0,0 +1,442 @@ +// Application JavaScript pour MRM & Co +// Génération dynamique des sections depuis config.js + +document.addEventListener('DOMContentLoaded', function () { + // Initialisation + initializeColors(); + renderServices(); + renderTeam(); + updateContactInfo(); + setCurrentYear(); + initializeMobileMenu(); + initializeContactForm(); + initializeSmoothScroll(); + initializeHeaderScroll(); + initializeHeroParallax(); + initializeTestimonials(); +}); + +/** + * Applique les couleurs personnalisées depuis la configuration + */ +function initializeColors() { + if (typeof config !== 'undefined' && config.colors) { + document.documentElement.style.setProperty('--color-primary', config.colors.primary); + document.documentElement.style.setProperty('--color-secondary', config.colors.secondary); + document.documentElement.style.setProperty('--color-accent', config.colors.accent); + } +} + +/** + * Génère dynamiquement la section Services + */ +function renderServices() { + const servicesContainer = document.getElementById('services-container'); + + if (!servicesContainer || typeof config === 'undefined' || !config.services) { + console.error('Configuration des services non trouvée'); + return; + } + + servicesContainer.innerHTML = config.services.map((service, idx) => { + var iconId = 'icon-' + (service.icon || 'web'); + return ` +
+
+ +
+

${service.title}

+

${service.description}

+
+ `; + }).join(''); +} + +/** + * Génère dynamiquement la section Équipe + */ +function renderTeam() { + const teamContainer = document.getElementById('team-container'); + + if (!teamContainer || typeof config === 'undefined' || !config.team) { + console.error('Configuration de l\'équipe non trouvée'); + return; + } + + teamContainer.innerHTML = config.team.map(function (member, idx) { + // fallback avatar via ui-avatars (utilisé si l'image externe échoue) + var primaryColor = (config.colors && config.colors.primary) ? config.colors.primary.replace('#', '') : '2563eb'; + var avatarFallback = 'https://ui-avatars.com/api/?name=' + encodeURIComponent(member.name) + '&background=ffffff&color=' + primaryColor + '&size=512'; + + return ` +
+
+ ${member.name} +
+
+

${member.name}

+

${member.role}

+

${member.description}

+ +
+
+ `; + }).map((html, i) => html.replace('card-hover', 'card-hover reveal')).join(''); + + // Attacher les handlers d'erreur d'image après insertion pour éviter les attributs inline + attachImageFallbacks(); +} + +function attachImageFallbacks() { + document.querySelectorAll('#team-container img[data-fallback]').forEach(function (img) { + if (img.__fallbackAttached) return; + img.addEventListener('error', function () { + var fb = img.getAttribute('data-fallback'); + if (fb) img.src = fb; + }); + img.__fallbackAttached = true; + }); +} + +/** + * Met à jour les informations de contact depuis la configuration + */ +function updateContactInfo() { + if (typeof config === 'undefined' || !config.company) { + return; + } + + // Mise à jour du hero + const heroDesc = document.getElementById('hero-description'); + if (heroDesc && config.company.description) { + heroDesc.textContent = config.company.description; + } + + // Mise à jour des coordonnées + const emailEl = document.getElementById('contact-email'); + if (emailEl && config.company.email) { + emailEl.textContent = config.company.email; + } + + const addressEl = document.getElementById('contact-address'); + if (addressEl && config.company.address) { + addressEl.textContent = config.company.address; + } + + // Mise à jour du footer + const footerDesc = document.getElementById('footer-description'); + if (footerDesc && config.company.tagline) { + footerDesc.textContent = config.company.tagline; + } +} + +/** + * Initialise le menu mobile responsive + */ +function initializeMobileMenu() { + const mobileMenuBtn = document.getElementById('mobile-menu-btn'); + const mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenuBtn && mobileMenu) { + mobileMenuBtn.addEventListener('click', function () { + mobileMenu.classList.toggle('hidden'); + }); + + // Fermer le menu lors du clic sur un lien + const mobileLinks = mobileMenu.querySelectorAll('a'); + mobileLinks.forEach(link => { + link.addEventListener('click', function () { + mobileMenu.classList.add('hidden'); + }); + }); + } +} + +/** + * Gestion du formulaire de contact + */ +function initializeContactForm() { + const contactForm = document.getElementById('contact-form'); + + if (contactForm) { + contactForm.addEventListener('submit', function (e) { + e.preventDefault(); + + // Récupération des données du formulaire + const formData = { + name: document.getElementById('name').value, + email: document.getElementById('email').value, + message: document.getElementById('message').value + }; + + // Simulation d'envoi (à remplacer par un vrai backend) + console.log('Formulaire soumis:', formData); + + // Message de confirmation + alert('Merci pour votre message ! Nous vous répondrons dans les plus brefs délais.'); + + // Réinitialisation du formulaire + contactForm.reset(); + }); + } +} + +/** + * Scroll fluide pour les ancres de navigation + */ +function initializeSmoothScroll() { + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const targetId = this.getAttribute('href'); + + if (targetId === '#') return; + + const targetElement = document.querySelector(targetId); + if (targetElement) { + const headerOffset = 80; // Hauteur du header fixe + const elementPosition = targetElement.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + } + }); + }); +} + +/** + * Animation au scroll pour les éléments + */ +function initializeScrollAnimations() { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animate-fade-in'); + observer.unobserve(entry.target); + } + }); + }, { + threshold: 0.12 + }); + + // Observer tous les éléments avec la classe reveal + document.querySelectorAll('.reveal').forEach(el => { + observer.observe(el); + }); +} + +// Initialiser les animations au scroll après le chargement +window.addEventListener('load', initializeScrollAnimations); + +/** + * Insère l'année courante dans le footer + */ +function setCurrentYear() { + var el = document.getElementById('current-year'); + if (el) { + el.textContent = new Date().getFullYear(); + } +} + +/** + * Change l'apparence du header quand on scroll + */ +function initializeHeaderScroll() { + var header = document.querySelector('header'); + if (!header) return; + + function onScroll() { + if (window.scrollY > 18) header.classList.add('header-scrolled'); + else header.classList.remove('header-scrolled'); + } + + window.addEventListener('scroll', onScroll, { passive: true }); + onScroll(); +} + +/** + * Micro-parallax for hero illustration + */ +function initializeHeroParallax() { + var hero = document.getElementById('hero-illustration'); + if (!hero) return; + + hero.addEventListener('mousemove', function (e) { + var rect = hero.getBoundingClientRect(); + var x = (e.clientX - rect.left) / rect.width - 0.5; // -0.5..0.5 + var y = (e.clientY - rect.top) / rect.height - 0.5; + var blobs = hero.querySelectorAll('.bg-blob'); + blobs.forEach(function (b, i) { + var depth = (i === 0) ? 18 : -12; + b.style.transform = 'translate(' + (-x * depth) + 'px,' + (-y * depth) + 'px)'; + }); + }); + + // reset on leave + hero.addEventListener('mouseleave', function () { + var blobs = hero.querySelectorAll('.bg-blob'); + blobs.forEach(function (b) { b.style.transform = ''; }); + }); +} + +/** + * Testimonials slider simple + */ +function initializeTestimonials() { + var slides = Array.from(document.querySelectorAll('#testimonial-slides .testimonial-slide')); + if (!slides.length) return; + var dotsContainer = document.getElementById('testimonial-dots'); + var current = 0; + var autoplay = true; + var timer = null; + + function goTo(idx) { + slides.forEach((s, i) => s.classList.toggle('active', i === idx)); + Array.from(dotsContainer.children).forEach((d, i) => d.classList.toggle('active', i === idx)); + current = idx; + } + + function next() { goTo((current + 1) % slides.length); } + function prev() { goTo((current - 1 + slides.length) % slides.length); } + + // dots + slides.forEach((s, i) => { + var dot = document.createElement('div'); + dot.className = 'dot' + (i === 0 ? ' active' : ''); + dot.addEventListener('click', function () { goTo(i); resetTimer(); }); + dotsContainer.appendChild(dot); + }); + + document.getElementById('next-testimonial').addEventListener('click', function () { next(); resetTimer(); }); + document.getElementById('prev-testimonial').addEventListener('click', function () { prev(); resetTimer(); }); + + function startTimer() { if (timer) clearInterval(timer); timer = setInterval(next, 5000); } + function resetTimer() { if (autoplay) startTimer(); } + + // pause on hover + var container = document.querySelector('.testimonials'); + container.addEventListener('mouseenter', function () { autoplay = false; if (timer) clearInterval(timer); }); + container.addEventListener('mouseleave', function () { autoplay = true; startTimer(); }); + + // start + goTo(0); + startTimer(); +} + +/** + * Initialisation du canvas d'arrière-plan interactif + */ +function initializeBackgroundCanvas() { + if (typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; + var canvas = document.getElementById('bg-canvas'); + if (!canvas) return; + var ctx = canvas.getContext('2d'); + var DPR = window.devicePixelRatio || 1; + var width, height; + var mouse = { x: -9999, y: -9999 }; + + function resize() { + width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + canvas.width = Math.floor(width * DPR); + canvas.height = Math.floor(height * DPR); + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + ctx.setTransform(DPR, 0, 0, DPR, 0, 0); + } + + window.addEventListener('resize', resize); + resize(); + + canvas.addEventListener('mousemove', function (e) { + mouse.x = e.clientX; + mouse.y = e.clientY; + }); + canvas.addEventListener('mouseleave', function () { mouse.x = -9999; mouse.y = -9999; }); + + // particles/blobs + var blobs = []; + var count = Math.max(6, Math.min(28, Math.floor((width * height) / 200000))); + for (var i = 0; i < count; i++) { + blobs.push({ + x: Math.random() * width, + y: Math.random() * height, + r: 80 + Math.random() * 260, + vx: (Math.random() - 0.5) * 0.2, + vy: (Math.random() - 0.5) * 0.2, + hue: 210 + Math.random() * 60 + }); + } + + function drawBlob(b) { + var grd = ctx.createRadialGradient(b.x, b.y, b.r * 0.15, b.x, b.y, b.r); + var h = b.hue; + grd.addColorStop(0, 'hsla(' + h + ',90%,70%,0.20)'); + grd.addColorStop(0.4, 'hsla(' + (h + 30) + ',80%,60%,0.12)'); + grd.addColorStop(1, 'hsla(' + (h + 60) + ',70%,50%,0.02)'); + ctx.fillStyle = grd; + ctx.beginPath(); + ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2); + ctx.fill(); + } + + function step() { + ctx.clearRect(0, 0, width, height); + // subtle background overlay + ctx.globalCompositeOperation = 'source-over'; + for (var i = 0; i < blobs.length; i++) { + var b = blobs[i]; + // move + b.x += b.vx; + b.y += b.vy; + // slight attraction to mouse + if (mouse.x > -9998) { + var dx = mouse.x - b.x; + var dy = mouse.y - b.y; + var dist = Math.sqrt(dx * dx + dy * dy) + 1; + var force = Math.min(80 / dist, 0.9); + b.vx += dx * 0.0008 * force; + b.vy += dy * 0.0008 * force; + } + // slow down velocity + b.vx *= 0.995; + b.vy *= 0.995; + // wrap + if (b.x < -b.r) b.x = width + b.r; + if (b.x > width + b.r) b.x = -b.r; + if (b.y < -b.r) b.y = height + b.r; + if (b.y > height + b.r) b.y = -b.r; + + drawBlob(b); + } + + // small radial highlight near mouse for interactivity + if (mouse.x > -9998) { + var radial = ctx.createRadialGradient(mouse.x, mouse.y, 0, mouse.x, mouse.y, 180); + radial.addColorStop(0, 'rgba(255,255,255,0.06)'); + radial.addColorStop(1, 'rgba(255,255,255,0)'); + ctx.fillStyle = radial; + ctx.fillRect(mouse.x - 180, mouse.y - 180, 360, 360); + } + + requestAnimationFrame(step); + } + + step(); +} + +// lancer le background après DOM ready +window.addEventListener('load', function () { initializeBackgroundCanvas(); }); diff --git a/assets/js/config.js b/assets/js/config.js new file mode 100644 index 0000000..d2d9909 --- /dev/null +++ b/assets/js/config.js @@ -0,0 +1,62 @@ +// Configuration du site MRM & Co +const config = { + // Couleurs principales du site + colors: { + primary: '#2563eb', // Bleu moderne + secondary: '#0f172a', // Bleu foncé + accent: '#3b82f6' // Bleu clair + }, + + // Informations de l'entreprise + company: { + name: 'MRM & Co', + tagline: 'Votre partenaire technologique de confiance', + description: 'Solutions informatiques innovantes pour propulser votre entreprise vers le futur', + email: 'contact@mrm.fr', + address: 'Nîmes, France' + }, + + // Services proposés + services: [ + { + title: 'Développement web', + description: 'Création de sites web modernes, performants et responsives, optimisés pour la conversion et la performance.', + icon: 'web' + }, + { + title: 'Développement logiciel', + description: 'Conception et réalisation de logiciels sur-mesure (desktop, backend, APIs) adaptés à vos processus métier.', + icon: 'software' + }, + { + title: 'Développement mobile', + description: 'Applications mobiles natives et cross-platform performantes, avec expérience utilisateur soignée.', + icon: 'mobile' + } + ], + + // Membres de l'équipe + team: [ + { + name: 'Maxence MINARRO', + role: 'CEO (Directeur Général) & Co-fondateur', + description: '', + image: 'assets/avatars/maxence-minarro.jpeg', + linkedin: '#' + }, + { + name: 'Titouan ROGER', + role: 'CTO (Directeur Technique) & Co-fondateur', + description: 'Développeur FullStack passionné par les nouvelles technologies et l\'innovation.', + image: 'assets/avatars/titouan-roger.jpeg', + linkedin: 'https://www.linkedin.com/in/titouan-roger/' + }, + { + name: 'Florian MOUTOUVIRIN', + role: 'CMO (Directeur Marketing) & Co-fondateur', + description: '', + image: 'assets/avatars/florian-moutouvirin.jpeg', + linkedin: 'https://www.linkedin.com/in/florian-moutouvirin/' + } + ] +}; diff --git a/index.html b/index.html new file mode 100644 index 0000000..135b8ee --- /dev/null +++ b/index.html @@ -0,0 +1,349 @@ + + + + + + + MRM & Co - Solutions Informatiques Innovantes + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+
+ +
+

+ Transformez votre vision digitale + en réalité +

+

+ Solutions informatiques innovantes pour propulser votre entreprise vers le futur +

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

+ Nos Services +

+

+ Des solutions complètes et sur-mesure pour répondre à tous vos besoins informatiques +

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

Témoignages

+

Ce que disent nos clients

+
+ +
+
+
+
“MRM & Co nous a aidés à moderniser notre plateforme — + résultats visibles dès le premier mois.”
+

— Client A, CTO

+
+
+
“Une équipe réactive et très professionnelle, + recommandée pour toute transformation digitale.”
+

— Client B, CEO

+
+
+
“Livraison dans les délais et performances au + rendez-vous — excellentes pratiques de dev.”
+

— Client C, Product Lead

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

+ Notre Équipe +

+

+ Des experts passionnés dédiés à votre succès +

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

+ Contactez-nous +

+

+ Parlons de votre projet et de vos ambitions digitales +

+
+ +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+ + +
+
+ + +
+
+
+
📧
+
+

Email

+

contact@mrmco.fr

+
+
+
+ +
+
+
📍
+
+

Adresse

+

Paris, France

+
+
+
+
+
+
+
+
+ + + + + + + + + \ No newline at end of file