/* Shared constants + small helpers for all directions */ const BRAND = { whatsapp: '+212615105100', whatsappLink: 'https://wa.me/212615105100', instagram: '@soutenancema', instagramLink: 'https://instagram.com/soutenancema', tagline: 'Capturez vos moments de soutenance', domain: 'soutenance.ma', }; const UNIVERSITIES = [ { name: 'UM5 Rabat', short: 'UM5R' }, { name: 'UM5 Souissi', short: 'UM5S' }, { name: 'ENCG Rabat', short: 'ENCG' }, { name: 'UM6P', short: 'UM6P' }, { name: 'UIR', short: 'UIR' }, { name: 'EMSI', short: 'EMSI' }, ]; /* Expanded list — used by the sliding marquee under the hero. */ const ALL_UNIVERSITIES = [ { name: 'Université Mohammed V — Rabat', short: 'UM5R' }, { name: 'Université Mohammed VI Polytechnique', short: 'UM6P' }, { name: 'Université Internationale de Rabat', short: 'UIR' }, { name: 'Université Hassan II — Casablanca', short: 'UH2C' }, { name: 'Université Cadi Ayyad — Marrakech', short: 'UCAM' }, { name: 'Université Sidi Mohamed Ben Abdellah', short: 'USMBA' }, { name: 'Al Akhawayn University — Ifrane', short: 'AUI' }, { name: 'Université Abdelmalek Essaâdi', short: 'UAE' }, { name: 'Université Moulay Ismail — Meknès', short: 'UMI' }, { name: 'Université Ibn Zohr — Agadir', short: 'UIZ' }, { name: 'Université Ibn Tofail — Kénitra', short: 'UIT' }, { name: 'Université Mohammed Premier — Oujda', short: 'UMP' }, { name: 'Université Hassan 1er — Settat', short: 'UH1' }, { name: 'Université Sultan Moulay Slimane', short: 'USMS' }, { name: 'ENCG Rabat', short: 'ENCG' }, { name: 'ENSA', short: 'ENSA' }, { name: 'EMSI', short: 'EMSI' }, { name: 'HEM Business School', short: 'HEM' }, { name: 'ESCA École de Management', short: 'ESCA' }, { name: 'Université Privée de Marrakech', short: 'UPM' }, { name: 'Université Internationale de Casablanca', short: 'UIC' }, { name: 'INSEA Rabat', short: 'INSEA' }, ]; const PACKS = [ { name: 'Essentiel', tagline: 'L\u2019essentiel pour garder une trace impeccable.', price: '1 400', priceNum: 1400, duration: 'Formule de base', features: [ 'Vid\u00e9o de pr\u00e9sentation', '50* photos retouch\u00e9es', 'Galerie en ligne priv\u00e9e', 'Livraison m\u00eame jour \u2014 Photos', ], }, { name: 'Prestige', tagline: 'L\u2019exp\u00e9rience compl\u00e8te en qualit\u00e9 4K.', price: '2 400', priceNum: 2400, duration: 'Formule recommand\u00e9e', features: [ 'Vid\u00e9o de pr\u00e9sentation', 'Film souvenir \u2014 4K', 'Vid\u00e9o Reel', '180* photos retouch\u00e9es', 'Galerie en ligne priv\u00e9e', 'Livraison m\u00eame jour \u2014 Photos', ], featured: true, }, { name: 'Signature', tagline: 'Le plus choisi. Photos + film souvenir.', price: '1 700', priceNum: 1700, duration: 'Formule premium', features: [ 'Vid\u00e9o de pr\u00e9sentation', 'Film souvenir', '120* photos retouch\u00e9es', 'Galerie en ligne priv\u00e9e', 'Livraison m\u00eame jour \u2014 Photos', ], }, ]; const PACK_NOTE = '*Le nombre de photos peut varier en fonction du contenu.'; /* Build-your-own options. Each group is either a radio (pick one) or a * checkbox set (pick any). Prices in MAD. */ const CUSTOM_OPTIONS = { photos: { label: 'Photos retouch\u00e9es', mode: 'radio', required: true, items: [ { id: 'photos-0', label: 'Aucune photo', price: 0 }, { id: 'photos-50', label: '50* photos retouch\u00e9es', price: 800 }, { id: 'photos-120', label: '120* photos retouch\u00e9es', price: 1100, recommended: true }, { id: 'photos-180', label: '180* photos retouch\u00e9es', price: 1400 }, ], }, video: { label: 'Vid\u00e9o', mode: 'check', items: [ { id: 'vid-pres', label: 'Vid\u00e9o de pr\u00e9sentation', price: 400 }, { id: 'vid-souv', label: 'Film souvenir', price: 500 }, { id: 'vid-souv-4k', label: 'Film souvenir \u2014 4K', price: 800, excludes: ['vid-souv'] }, { id: 'vid-reel', label: 'Vid\u00e9o Reel', price: 300 }, ], }, delivery: { label: 'Livraison', mode: 'radio', required: true, items: [ { id: 'deliv-std', label: 'Livraison standard (3 jours)', price: 0 }, { id: 'deliv-same', label: 'Livraison m\u00eame jour \u2014 Photos', price: 200 }, ], }, }; /* Defaults: start with a sensible recommended selection */ const CUSTOM_DEFAULT_SELECTION = { photos: 'photos-120', video: ['vid-pres'], delivery: 'deliv-same', }; function computeCustomTotal(selection) { let total = 0; const items = []; // photos (radio) const pId = selection.photos; if (pId) { const p = CUSTOM_OPTIONS.photos.items.find((x) => x.id === pId); if (p && p.price > 0) { total += p.price; items.push(p); } else if (p) items.push(p); } // video (check) (selection.video || []).forEach((vid) => { const v = CUSTOM_OPTIONS.video.items.find((x) => x.id === vid); if (v) { total += v.price; items.push(v); } }); // delivery (radio) const dId = selection.delivery; if (dId) { const d = CUSTOM_OPTIONS.delivery.items.find((x) => x.id === dId); if (d) { total += d.price; items.push(d); } } return { total, items }; } const SERVICES = [ { title: 'Photographie', body: 'Portraits, photos de groupe avec la famille, ambiance et d\u00e9tails.' }, { title: 'Vid\u00e9o', body: 'Films souvenirs cin\u00e9matiques pour revivre votre soutenance.' }, { title: 'Haute Qualit\u00e9', body: 'Vid\u00e9os 4K haute qualit\u00e9 et photos haute d\u00e9finition.' }, ]; /* SVG placeholder \u2014 striped block with caption. * Used wherever a real photo should land. */ function PhotoSlot({ caption = 'Photo', tone = 'warm', style = {}, rounded = 16, ratio = '3 / 4' }) { const palettes = { warm: ['#f3e6df', '#e9d5cb', '#1c1c1c'], cool: ['#e6e7ef', '#d3d5e3', '#1c1c1c'], dark: ['#1f1d1d', '#2a2828', '#f4f3ee'], pink: ['#ffd9e0', '#ffb8c6', '#7a0e2f'], }; const [a, b, fg] = palettes[tone] || palettes.warm; return (
{caption}
); } /* Small inline icons (sized by font-size via currentColor) */ const Icon = { Whatsapp: (p) => ( ), Instagram: (p) => ( ), Camera: (p) => ( ), Video: (p) => ( ), Drone: (p) => ( ), Album: (p) => ( ), Check: (p) => ( ), Arrow: (p) => ( ), Sparkle: (p) => ( ), }; /* Service icon mapping */ const SERVICE_ICONS = { 'Photographie': Icon.Camera, 'Vid\u00e9o': Icon.Video, 'Haute Qualit\u00e9': Icon.Camera, }; /* Tiny university logo mark (we don't have real logos, so use lockups with school short codes) */ function UniMark({ name, short, theme = 'light' }) { const fg = theme === 'dark' ? '#f4f3ee' : '#3a3a3a'; const bg = theme === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)'; const border = theme === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.08)'; return (
{short}
{name}
); } /* Calendar grid for booking date picker */ function CalendarPreview({ accent = '#ff2e63', textColor = '#1a1a1a', mutedColor = '#9a9a9a', highlightDay = 18, secondaryDay = 22 }) { const days = ['L','M','M','J','V','S','D']; const firstOffset = 3; // Juin 2026 starts Monday-ish for demo const cells = Array.from({ length: 35 }, (_, i) => { const day = i - firstOffset + 1; return day >= 1 && day <= 30 ? day : null; }); return (
Juin 2026
{days.map((d, i) =>
{d}
)}
{cells.map((d, i) => { const isSelected = d === highlightDay; const isAvailable = d && d >= 12 && [12, 14, 15, 18, 19, 22, 25, 26, 28].includes(d); const isSecondary = d === secondaryDay; return (
{d} {isAvailable && !isSelected && !isSecondary && ( )}
); })}
); } Object.assign(window, { BRAND, UNIVERSITIES, ALL_UNIVERSITIES, PACKS, PACK_NOTE, CUSTOM_OPTIONS, CUSTOM_DEFAULT_SELECTION, computeCustomTotal, SERVICES, SERVICE_ICONS, PhotoSlot, Icon, UniMark, CalendarPreview });