cinqcinqdev-seo 0.1.31 → 0.1.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/assets/admin-tw.css +1 -1
  3. package/dist/runtime/components/admin-cms/ContentCalendar.vue +7 -7
  4. package/dist/runtime/components/admin-cms/DynamicRenderer.vue +2 -2
  5. package/dist/runtime/components/admin-cms/Navbar.vue +5 -5
  6. package/dist/runtime/components/admin-cms/Sidebar.vue +8 -8
  7. package/dist/runtime/components/sections/ContactForm.vue +15 -11
  8. package/dist/runtime/components/sections/FAQ.vue +9 -5
  9. package/dist/runtime/components/sections/Features.vue +10 -6
  10. package/dist/runtime/components/sections/HeroSection.vue +19 -15
  11. package/dist/runtime/components/sections/Pricing.vue +12 -8
  12. package/dist/runtime/components/sections/Process.vue +13 -9
  13. package/dist/runtime/components/sections/ServiceGrid.vue +14 -10
  14. package/dist/runtime/components/sections/Testimonials.vue +16 -12
  15. package/dist/runtime/components/sections/TextVisual.vue +15 -11
  16. package/dist/runtime/pages/admin/account.vue +31 -31
  17. package/dist/runtime/pages/admin/branding.vue +14 -14
  18. package/dist/runtime/pages/admin/calendar.vue +1 -1
  19. package/dist/runtime/pages/admin/content-library.vue +16 -16
  20. package/dist/runtime/pages/admin/editor/[id].vue +114 -114
  21. package/dist/runtime/pages/admin/index.vue +20 -20
  22. package/dist/runtime/pages/admin/pages/[type].vue +54 -54
  23. package/dist/runtime/pages/admin/personas/[id].vue +9 -9
  24. package/dist/runtime/pages/admin/personas/index.vue +6 -6
  25. package/dist/runtime/pages/admin/products.vue +7 -7
  26. package/dist/runtime/pages/admin/seo.vue +36 -36
  27. package/dist/runtime/pages/admin/setup.vue +6 -6
  28. package/dist/runtime/pages/admin/strategies/[id].vue +7 -7
  29. package/dist/runtime/pages/admin/strategies/index.vue +7 -7
  30. package/dist/runtime/pages/admin/todos.vue +8 -8
  31. package/package.json +1 -1
@@ -405,14 +405,14 @@ const savePage = async () => {
405
405
  <input type="file" ref="fileInput" class="hidden" accept="image/*" @change="handleFileUpload" />
406
406
 
407
407
  <!-- ── Top Bar ── -->
408
- <header class="h-12 bg-background/80 backdrop-blur-md border-b px-6 flex items-center justify-between z-[60] shrink-0">
408
+ <header class="h-12 bg-white/80 backdrop-blur-md border-b px-6 flex items-center justify-between z-[60] shrink-0">
409
409
  <div class="flex items-center gap-4">
410
- <NuxtLink :to="basePath" class="text-mgray-400 hover:text-black transition-colors">←</NuxtLink>
411
- <div class="flex gap-1 bg-mgray-100 p-1 rounded-md">
412
- <button @click="isLeftSidebarOpen = !isLeftSidebarOpen" :class="['p-1.5 rounded transition-all', isLeftSidebarOpen ? 'bg-background shadow-sm text-primary' : 'text-mgray-400']" title="Structure">
410
+ <NuxtLink :to="basePath" class="text-gray-400 hover:text-black transition-colors">←</NuxtLink>
411
+ <div class="flex gap-1 bg-gray-100 p-1 rounded-md">
412
+ <button @click="isLeftSidebarOpen = !isLeftSidebarOpen" :class="['p-1.5 rounded transition-all', isLeftSidebarOpen ? 'bg-white shadow-sm text-[#3d35ff]' : 'text-gray-400']" title="Structure">
413
413
  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M4 6h16M4 12h10M4 18h16"/></svg>
414
414
  </button>
415
- <button @click="isRightSidebarOpen = !isRightSidebarOpen" :class="['p-1.5 rounded transition-all', isRightSidebarOpen ? 'bg-background shadow-sm text-primary' : 'text-mgray-400']" title="Propriétés">
415
+ <button @click="isRightSidebarOpen = !isRightSidebarOpen" :class="['p-1.5 rounded transition-all', isRightSidebarOpen ? 'bg-white shadow-sm text-[#3d35ff]' : 'text-gray-400']" title="Propriétés">
416
416
  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 6h8M12 12h8M12 18h8M4 6h.01M4 12h.01M4 18h.01"/></svg>
417
417
  </button>
418
418
  </div>
@@ -420,23 +420,23 @@ const savePage = async () => {
420
420
  </div>
421
421
 
422
422
  <div class="flex items-center gap-4">
423
- <div class="flex bg-mgray-100 p-1 rounded-full scale-90">
424
- <button @click="viewMode = 'structure'" :class="['px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest', viewMode === 'structure' ? 'bg-background text-black' : 'text-mgray-400']">Structure</button>
425
- <button @click="viewMode = 'seo'" :class="['px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest', viewMode === 'seo' ? 'bg-background text-black' : 'text-mgray-400']">Audit SEO</button>
423
+ <div class="flex bg-gray-100 p-1 rounded-full scale-90">
424
+ <button @click="viewMode = 'structure'" :class="['px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest', viewMode === 'structure' ? 'bg-white text-black' : 'text-gray-400']">Structure</button>
425
+ <button @click="viewMode = 'seo'" :class="['px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest', viewMode === 'seo' ? 'bg-white text-black' : 'text-gray-400']">Audit SEO</button>
426
426
  </div>
427
- <NuxtLink v-if="page?.slug" :to="pageViewUrl" target="_blank" class="flex items-center gap-1.5 px-4 py-2 rounded-full text-[9px] font-black uppercase tracking-widest border border-mgray-200 text-mgray-500 hover:border-primary hover:text-primary transition-all">
427
+ <NuxtLink v-if="page?.slug" :to="pageViewUrl" target="_blank" class="flex items-center gap-1.5 px-4 py-2 rounded-full text-[9px] font-black uppercase tracking-widest border border-gray-200 text-gray-500 hover:border-[#3d35ff] hover:text-[#3d35ff] transition-all">
428
428
  <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
429
429
  Voir la page
430
430
  </NuxtLink>
431
431
  <button @click="togglePublished"
432
432
  :class="[
433
433
  'flex items-center gap-1.5 px-4 py-2 rounded-full text-[9px] font-black uppercase tracking-widest border transition-all active:scale-95',
434
- page?.status === 'published' ? 'bg-green-50 border-green-200 text-green-600 hover:bg-green-100' : 'bg-mgray-50 border-mgray-200 text-mgray-400 hover:border-mgray-300'
434
+ page?.status === 'published' ? 'bg-green-50 border-green-200 text-green-600 hover:bg-green-100' : 'bg-gray-50 border-gray-200 text-gray-400 hover:border-gray-300'
435
435
  ]">
436
- <span :class="['w-1.5 h-1.5 rounded-full', page?.status === 'published' ? 'bg-green-500' : 'bg-mgray-300']"></span>
436
+ <span :class="['w-1.5 h-1.5 rounded-full', page?.status === 'published' ? 'bg-green-500' : 'bg-gray-300']"></span>
437
437
  {{ page?.status === "published" ? "Publi\xE9" : "Brouillon" }}
438
438
  </button>
439
- <button @click="savePage" class="relative bg-primary text-white px-5 py-2 rounded-full text-[9px] font-black uppercase tracking-widest shadow-lg active:scale-95 transition-all">
439
+ <button @click="savePage" class="relative bg-[#3d35ff] text-white px-5 py-2 rounded-full text-[9px] font-black uppercase tracking-widest shadow-lg active:scale-95 transition-all">
440
440
  {{ isSaving ? "Enregistrement..." : "Mettre \xE0 jour" }}
441
441
  <span v-if="!isSaving && dirtyBlocks.size > 0"
442
442
  class="absolute -top-1.5 -right-1.5 w-4 h-4 bg-amber-400 text-white rounded-full text-[7px] font-black flex items-center justify-center">
@@ -449,10 +449,10 @@ const savePage = async () => {
449
449
  <div class="flex flex-1 overflow-hidden relative">
450
450
 
451
451
  <!-- ── Left Sidebar: Structure / SEO Tree ── -->
452
- <aside :class="['bg-background border-r z-50 transition-all duration-300 overflow-hidden flex flex-col', isLeftSidebarOpen ? 'w-64' : 'w-0']">
452
+ <aside :class="['bg-white border-r z-50 transition-all duration-300 overflow-hidden flex flex-col', isLeftSidebarOpen ? 'w-64' : 'w-0']">
453
453
  <div class="p-6 w-64 h-full flex flex-col">
454
454
  <div v-if="viewMode === 'structure'" class="flex-1 space-y-2 overflow-y-auto">
455
- <h3 class="text-[9px] font-black text-mgray-300 uppercase mb-4 italic tracking-widest">Arborescence</h3>
455
+ <h3 class="text-[9px] font-black text-gray-300 uppercase mb-4 italic tracking-widest">Arborescence</h3>
456
456
  <div v-if="page?.content_locked" class="p-4 rounded-xl bg-amber-50 border border-amber-100 text-amber-600 text-[8px] font-black uppercase tracking-widest text-center">
457
457
  Page verrouillée<br/><span class="font-normal normal-case opacity-60 text-[7px]">Seul le SEO est éditable</span>
458
458
  </div>
@@ -460,7 +460,7 @@ const savePage = async () => {
460
460
  <div v-for="(block, index) in page.content" :key="index" @click="selectedBlockIndex = index"
461
461
  :class="[
462
462
  'p-3 rounded-xl border-2 transition-all cursor-pointer group flex items-center justify-between gap-2',
463
- selectedBlockIndex === index ? 'border-primary bg-blue-50/50 text-primary' : dirtyBlocks.has(index) ? 'border-amber-300 bg-amber-50 text-amber-700' : 'border-mgray-50 bg-mgray-50 text-mgray-500'
463
+ selectedBlockIndex === index ? 'border-[#3d35ff] bg-blue-50/50 text-[#3d35ff]' : dirtyBlocks.has(index) ? 'border-amber-300 bg-amber-50 text-amber-700' : 'border-gray-50 bg-gray-50 text-gray-500'
464
464
  ]">
465
465
  <input
466
466
  v-if="editingLabelIndex === index"
@@ -485,14 +485,14 @@ const savePage = async () => {
485
485
  </div>
486
486
  </div>
487
487
  </div>
488
- <button @click="showAddModal = true" class="w-full py-3 border-2 border-dashed border-mgray-100 rounded-xl text-[9px] font-black text-mgray-400 hover:border-primary transition-all">+ SECTION</button>
488
+ <button @click="showAddModal = true" class="w-full py-3 border-2 border-dashed border-gray-100 rounded-xl text-[9px] font-black text-gray-400 hover:border-[#3d35ff] transition-all">+ SECTION</button>
489
489
  </template>
490
490
  </div>
491
491
 
492
492
  <div v-else class="flex-1 overflow-y-auto">
493
- <h3 class="text-[9px] font-black text-primary uppercase mb-6 italic tracking-widest">Hiérarchie Hn</h3>
493
+ <h3 class="text-[9px] font-black text-[#3d35ff] uppercase mb-6 italic tracking-widest">Hiérarchie Hn</h3>
494
494
  <div v-for="(node, nIdx) in seoTree" :key="nIdx"
495
- class="mb-2 p-2 rounded border-l-2 bg-mgray-50/50 cursor-pointer hover:bg-blue-50/50"
495
+ class="mb-2 p-2 rounded border-l-2 bg-gray-50/50 cursor-pointer hover:bg-blue-50/50"
496
496
  :style="{ marginLeft: `${tagDepth(node.tag) * 14}px`, borderColor: tagColor(node.tag) }"
497
497
  @click="selectedBlockIndex = node.blockIndex">
498
498
  <span class="text-[7px] font-black uppercase block" :style="{ color: tagColor(node.tag) }">{{ node.tag }}</span>
@@ -504,26 +504,26 @@ const savePage = async () => {
504
504
 
505
505
  <!-- ── Preview ── -->
506
506
  <main class="flex-1 overflow-y-auto p-4 md:p-10 bg-[#F5F5F7]">
507
- <div class="max-w-4xl mx-auto bg-background shadow-2xl rounded-[40px] overflow-hidden min-h-screen border border-black/5">
507
+ <div class="max-w-4xl mx-auto bg-white shadow-2xl rounded-[40px] overflow-hidden min-h-screen border border-black/5">
508
508
  <div v-for="(block, index) in page.content" :key="index" @click="selectedBlockIndex = index"
509
- class="relative cursor-pointer" :class="{ 'ring-4 ring-primary ring-inset z-10': selectedBlockIndex === index }">
509
+ class="relative cursor-pointer" :class="{ 'ring-4 ring-[#3d35ff] ring-inset z-10': selectedBlockIndex === index }">
510
510
  <component :is="resolveSection(block.type)" v-bind="resolvePropsForPreview(block.props)" />
511
511
  </div>
512
512
  </div>
513
513
  </main>
514
514
 
515
515
  <!-- ── Right Panel: Props / SEO ── -->
516
- <aside :class="['bg-background border-l z-50 transition-all duration-300 overflow-y-auto', isRightSidebarOpen ? 'w-80' : 'w-0']">
516
+ <aside :class="['bg-white border-l z-50 transition-all duration-300 overflow-y-auto', isRightSidebarOpen ? 'w-80' : 'w-0']">
517
517
  <div class="p-6 w-80">
518
518
 
519
519
  <!-- Mode tabs + lang switcher -->
520
520
  <div class="flex items-center justify-between mb-5 border-b pb-4">
521
- <div class="flex gap-0.5 bg-mgray-100 p-0.5 rounded-lg">
521
+ <div class="flex gap-0.5 bg-gray-100 p-0.5 rounded-lg">
522
522
  <button @click="rightPanelMode = 'block'"
523
523
  :disabled="selectedBlockIndex === null || page?.content_locked"
524
524
  :class="[
525
525
  'px-2.5 py-1 text-[7px] font-black uppercase rounded-md transition-all',
526
- rightPanelMode === 'block' ? 'bg-background shadow text-primary' : 'text-mgray-400',
526
+ rightPanelMode === 'block' ? 'bg-white shadow text-[#3d35ff]' : 'text-gray-400',
527
527
  selectedBlockIndex === null || page?.content_locked ? 'opacity-30 cursor-not-allowed' : ''
528
528
  ]">
529
529
  Bloc
@@ -531,16 +531,16 @@ const savePage = async () => {
531
531
  <button @click="rightPanelMode = 'seo'"
532
532
  :class="[
533
533
  'px-2.5 py-1 text-[7px] font-black uppercase rounded-md transition-all',
534
- rightPanelMode === 'seo' ? 'bg-background shadow text-primary' : 'text-mgray-400'
534
+ rightPanelMode === 'seo' ? 'bg-white shadow text-[#3d35ff]' : 'text-gray-400'
535
535
  ]">
536
536
  SEO
537
537
  </button>
538
538
  </div>
539
- <div class="flex gap-0.5 bg-mgray-100 p-0.5 rounded-lg">
539
+ <div class="flex gap-0.5 bg-gray-100 p-0.5 rounded-lg">
540
540
  <button v-for="lang in LANGS" :key="lang" @click="editingLang = lang"
541
541
  :class="[
542
542
  'px-2 py-1 text-[7px] font-black uppercase rounded-md transition-all',
543
- editingLang === lang ? 'bg-background shadow text-primary' : 'text-mgray-400'
543
+ editingLang === lang ? 'bg-white shadow text-[#3d35ff]' : 'text-gray-400'
544
544
  ]">
545
545
  {{ langLabel(lang) }}
546
546
  </button>
@@ -551,7 +551,7 @@ const savePage = async () => {
551
551
  <div v-if="rightPanelMode === 'block' && selectedBlockIndex !== null">
552
552
 
553
553
  <!-- Diff -->
554
- <div v-if="blockDiffs.has(selectedBlockIndex)" class="mb-5 rounded-2xl overflow-hidden border border-amber-200 bg-background">
554
+ <div v-if="blockDiffs.has(selectedBlockIndex)" class="mb-5 rounded-2xl overflow-hidden border border-amber-200 bg-white">
555
555
  <button @click="showDiff = !showDiff"
556
556
  class="w-full flex items-center justify-between px-4 py-3 bg-amber-50 hover:bg-amber-100 transition-colors">
557
557
  <span class="flex items-center gap-2 text-[10px] font-black text-amber-700 uppercase tracking-wider">
@@ -562,7 +562,7 @@ const savePage = async () => {
562
562
  </button>
563
563
  <div v-if="showDiff" class="divide-y divide-gray-50">
564
564
  <div v-for="change in blockDiffs.get(selectedBlockIndex)" :key="change.field" class="px-4 py-3">
565
- <p class="text-[9px] font-black uppercase tracking-widest text-mgray-400 mb-2">{{ change.field }}</p>
565
+ <p class="text-[9px] font-black uppercase tracking-widest text-gray-400 mb-2">{{ change.field }}</p>
566
566
  <div class="flex items-start gap-2 mb-1.5">
567
567
  <span class="w-1 self-stretch rounded-full bg-red-300 shrink-0"></span>
568
568
  <p class="text-[11px] text-red-500 line-through leading-snug break-words min-w-0">{{ change.old }}</p>
@@ -578,40 +578,40 @@ const savePage = async () => {
578
578
  <!-- AI Generate -->
579
579
  <div v-if="aiEnabled" class="mb-5">
580
580
  <div v-if="!showAiPrompt && !isGenerating">
581
- <button @click="showAiPrompt = true" class="w-full py-2.5 rounded-xl border-2 border-dashed border-primary-300 text-primary text-[8px] font-black uppercase tracking-widest hover:border-primary hover:bg-primary-50 transition-all">
581
+ <button @click="showAiPrompt = true" class="w-full py-2.5 rounded-xl border-2 border-dashed border-[#3d35ff]/30 text-[#3d35ff] text-[8px] font-black uppercase tracking-widest hover:border-[#3d35ff] hover:bg-[#3d35ff]/5 transition-all">
582
582
  ✦ Générer avec l'IA
583
583
  </button>
584
584
  </div>
585
- <div v-else-if="isGenerating" class="py-2.5 rounded-xl bg-primary-50 border border-primary-200 text-center text-[8px] font-black uppercase tracking-widest text-primary animate-pulse">
585
+ <div v-else-if="isGenerating" class="py-2.5 rounded-xl bg-[#3d35ff]/5 border border-[#3d35ff]/20 text-center text-[8px] font-black uppercase tracking-widest text-[#3d35ff] animate-pulse">
586
586
  Génération en cours...
587
587
  </div>
588
- <div v-else class="p-3 bg-primary-50 rounded-2xl border border-primary-200 space-y-2">
589
- <label class="text-[8px] font-black uppercase text-primary-600 tracking-widest block">Décrivez votre contenu</label>
590
- <textarea v-model="aiPromptText" rows="2" placeholder="Ex: service de création de sites web pour PME..." class="w-full bg-background rounded-xl p-2.5 text-[10px] outline-none ring-1 ring-primary-200 focus:ring-primary resize-none"></textarea>
588
+ <div v-else class="p-3 bg-[#3d35ff]/5 rounded-2xl border border-[#3d35ff]/20 space-y-2">
589
+ <label class="text-[8px] font-black uppercase text-[#3d35ff]/60 tracking-widest block">Décrivez votre contenu</label>
590
+ <textarea v-model="aiPromptText" rows="2" placeholder="Ex: service de création de sites web pour PME..." class="w-full bg-white rounded-xl p-2.5 text-[10px] outline-none ring-1 ring-[#3d35ff]/20 focus:ring-[#3d35ff] resize-none"></textarea>
591
591
  <div class="flex gap-2">
592
592
  <button @click="showAiPrompt = false;
593
- aiPromptText = ''" class="flex-1 py-2 rounded-xl border border-mgray-200 text-[8px] font-black uppercase text-mgray-400 hover:border-mgray-300 transition-all">Annuler</button>
594
- <button @click="generateWithAI" :disabled="!aiPromptText.trim()" class="flex-1 py-2 rounded-xl bg-primary text-white text-[8px] font-black uppercase tracking-widest disabled:opacity-40 transition-all active:scale-95">✦ Générer</button>
593
+ aiPromptText = ''" class="flex-1 py-2 rounded-xl border border-gray-200 text-[8px] font-black uppercase text-gray-400 hover:border-gray-300 transition-all">Annuler</button>
594
+ <button @click="generateWithAI" :disabled="!aiPromptText.trim()" class="flex-1 py-2 rounded-xl bg-[#3d35ff] text-white text-[8px] font-black uppercase tracking-widest disabled:opacity-40 transition-all active:scale-95">✦ Générer</button>
595
595
  </div>
596
596
  </div>
597
597
  </div>
598
598
 
599
599
  <!-- Anchor ID -->
600
- <div class="mb-6 pb-6 border-b border-mgray-50">
601
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">ID / Ancre</label>
602
- <div class="flex items-center bg-mgray-50 rounded-xl ring-1 ring-gray-100 focus-within:ring-primary overflow-hidden">
603
- <span class="text-[9px] font-mono text-mgray-300 pl-3 pr-1 shrink-0">#</span>
600
+ <div class="mb-6 pb-6 border-b border-gray-50">
601
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">ID / Ancre</label>
602
+ <div class="flex items-center bg-gray-50 rounded-xl ring-1 ring-gray-100 focus-within:ring-[#3d35ff] overflow-hidden">
603
+ <span class="text-[9px] font-mono text-gray-300 pl-3 pr-1 shrink-0">#</span>
604
604
  <input v-model="page.content[selectedBlockIndex].id" class="flex-1 bg-transparent p-3 text-[10px] font-mono outline-none" placeholder="mon-ancre" />
605
605
  </div>
606
606
  </div>
607
607
 
608
608
  <!-- Group tabs -->
609
- <div v-if="selectedCMSConfig?.groups" class="flex gap-1 bg-mgray-100 p-1 rounded-xl mb-6">
609
+ <div v-if="selectedCMSConfig?.groups" class="flex gap-1 bg-gray-100 p-1 rounded-xl mb-6">
610
610
  <button v-for="group in selectedCMSConfig.groups" :key="group.key"
611
611
  @click="selectedFieldGroup = group.key"
612
612
  :class="[
613
613
  'flex-1 py-2 text-[8px] font-black uppercase tracking-widest rounded-lg transition-all',
614
- selectedFieldGroup === group.key ? 'bg-background shadow text-primary' : 'text-mgray-400 hover:text-mgray-600'
614
+ selectedFieldGroup === group.key ? 'bg-white shadow text-[#3d35ff]' : 'text-gray-400 hover:text-gray-600'
615
615
  ]">
616
616
  {{ group.label }}
617
617
  </button>
@@ -619,7 +619,7 @@ const savePage = async () => {
619
619
 
620
620
  <!-- Fields -->
621
621
  <div v-for="(field, name) in visibleFields" :key="name" class="mb-6">
622
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">{{ field.label }}</label>
622
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">{{ field.label }}</label>
623
623
 
624
624
  <!-- Image -->
625
625
  <div v-if="field.type === 'image'" class="space-y-2">
@@ -628,10 +628,10 @@ const savePage = async () => {
628
628
  <button @click="page.content[selectedBlockIndex].props[name] = ''" class="absolute inset-0 bg-black/60 text-white text-[8px] font-black opacity-0 group-hover:opacity-100 transition-opacity">SUPPRIMER</button>
629
629
  </div>
630
630
  <div class="flex gap-2">
631
- <button @click="triggerUpload(page.content[selectedBlockIndex].props, name)" class="flex-1 py-2.5 bg-mgray-50 border-2 border-dashed rounded-xl text-[9px] font-black hover:bg-mgray-100 transition-all" :disabled="isUploading">
631
+ <button @click="triggerUpload(page.content[selectedBlockIndex].props, name)" class="flex-1 py-2.5 bg-gray-50 border-2 border-dashed rounded-xl text-[9px] font-black hover:bg-gray-100 transition-all" :disabled="isUploading">
632
632
  {{ isUploading ? "ENVOI..." : "\u2191 UPLOAD" }}
633
633
  </button>
634
- <button @click="openGallery(page.content[selectedBlockIndex].props, name)" class="flex-1 py-2.5 bg-mgray-50 border-2 border-dashed rounded-xl text-[9px] font-black hover:bg-mgray-100 transition-all">
634
+ <button @click="openGallery(page.content[selectedBlockIndex].props, name)" class="flex-1 py-2.5 bg-gray-50 border-2 border-dashed rounded-xl text-[9px] font-black hover:bg-gray-100 transition-all">
635
635
  ⊞ GALERIE
636
636
  </button>
637
637
  </div>
@@ -640,26 +640,26 @@ const savePage = async () => {
640
640
  <!-- Text i18n -->
641
641
  <template v-else-if="field.type === 'text'">
642
642
  <div v-if="field.i18n" class="relative">
643
- <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-primary-400">{{ langLabel(editingLang) }}</span>
644
- <input :value="getI18nProp(name, editingLang)" @input="setI18nProp(name, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
643
+ <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-[#3d35ff]/40">{{ langLabel(editingLang) }}</span>
644
+ <input :value="getI18nProp(name, editingLang)" @input="setI18nProp(name, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
645
645
  </div>
646
646
  <input v-else :value="page.content[selectedBlockIndex].props[name]" @input="page.content[selectedBlockIndex].props[name] = $event.target.value;
647
- trigger()" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
647
+ trigger()" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
648
648
  </template>
649
649
 
650
650
  <!-- Textarea i18n -->
651
651
  <template v-else-if="field.type === 'textarea'">
652
652
  <div v-if="field.i18n" class="relative">
653
- <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-primary-400">{{ langLabel(editingLang) }}</span>
654
- <textarea :value="getI18nProp(name, editingLang)" @input="setI18nProp(name, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" rows="3" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-primary"></textarea>
653
+ <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-[#3d35ff]/40">{{ langLabel(editingLang) }}</span>
654
+ <textarea :value="getI18nProp(name, editingLang)" @input="setI18nProp(name, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" rows="3" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]"></textarea>
655
655
  </div>
656
656
  <textarea v-else :value="page.content[selectedBlockIndex].props[name]" @input="page.content[selectedBlockIndex].props[name] = $event.target.value;
657
- trigger()" rows="3" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100"></textarea>
657
+ trigger()" rows="3" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100"></textarea>
658
658
  </template>
659
659
 
660
660
  <!-- Select -->
661
661
  <select v-else-if="field.type === 'select'" :value="page.content[selectedBlockIndex].props[name]" @change="page.content[selectedBlockIndex].props[name] = $event.target.value;
662
- trigger()" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold ring-1 ring-gray-100 outline-none">
662
+ trigger()" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold ring-1 ring-gray-100 outline-none">
663
663
  <option v-for="opt in field.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
664
664
  </select>
665
665
 
@@ -670,7 +670,7 @@ const savePage = async () => {
670
670
  trigger()"
671
671
  :class="[
672
672
  'p-2.5 rounded-xl border-2 text-center transition-all',
673
- page.content[selectedBlockIndex].props[name] === opt.value ? 'border-primary bg-blue-50/50 text-primary' : 'border-mgray-100 text-mgray-400 hover:border-mgray-200'
673
+ page.content[selectedBlockIndex].props[name] === opt.value ? 'border-[#3d35ff] bg-blue-50/50 text-[#3d35ff]' : 'border-gray-100 text-gray-400 hover:border-gray-200'
674
674
  ]">
675
675
  <span class="text-base block mb-1">{{ opt.icon }}</span>
676
676
  <span class="text-[7px] font-black uppercase tracking-wide block leading-tight">{{ opt.label }}</span>
@@ -678,7 +678,7 @@ const savePage = async () => {
678
678
  </div>
679
679
 
680
680
  <!-- Color -->
681
- <div v-else-if="field.type === 'color'" class="flex gap-2 bg-mgray-50 p-2 rounded-xl ring-1 ring-gray-100">
681
+ <div v-else-if="field.type === 'color'" class="flex gap-2 bg-gray-50 p-2 rounded-xl ring-1 ring-gray-100">
682
682
  <button v-for="c in ['#3d35ff', '#ffffff', '#000000', '#F9F9FB']" :key="c" @click="page.content[selectedBlockIndex].props[name] = c;
683
683
  trigger()" class="w-5 h-5 rounded-full border border-black/5" :style="{ background: c }"></button>
684
684
  <input :value="page.content[selectedBlockIndex].props[name]" @input="page.content[selectedBlockIndex].props[name] = $event.target.value;
@@ -687,7 +687,7 @@ const savePage = async () => {
687
687
 
688
688
  <!-- List -->
689
689
  <div v-else-if="field.type === 'list'" class="space-y-4">
690
- <div v-for="(item, idx) in page.content[selectedBlockIndex].props[name] || []" :key="idx" class="p-4 bg-mgray-50 rounded-2xl relative border">
690
+ <div v-for="(item, idx) in page.content[selectedBlockIndex].props[name] || []" :key="idx" class="p-4 bg-gray-50 rounded-2xl relative border">
691
691
  <button @click="page.content[selectedBlockIndex].props[name].splice(idx, 1);
692
692
  trigger()" class="absolute top-2 right-2 text-red-400 text-[8px]">✕</button>
693
693
  <div v-for="(subField, subName) in field.itemFields" :key="subName" class="mb-2">
@@ -695,16 +695,16 @@ const savePage = async () => {
695
695
  <div v-if="subField.type === 'image'" class="mt-1 space-y-1.5">
696
696
  <img v-if="item[subName]" :src="item[subName]" class="w-full h-16 rounded-lg border object-cover" />
697
697
  <div class="flex gap-1.5">
698
- <button @click="triggerUpload(item, subName)" class="flex-1 text-[8px] font-black uppercase bg-background px-2 py-1.5 rounded-lg shadow-sm border">↑ Upload</button>
699
- <button @click="openGallery(item, subName)" class="flex-1 text-[8px] font-black uppercase bg-background px-2 py-1.5 rounded-lg shadow-sm border">⊞ Galerie</button>
698
+ <button @click="triggerUpload(item, subName)" class="flex-1 text-[8px] font-black uppercase bg-white px-2 py-1.5 rounded-lg shadow-sm border">↑ Upload</button>
699
+ <button @click="openGallery(item, subName)" class="flex-1 text-[8px] font-black uppercase bg-white px-2 py-1.5 rounded-lg shadow-sm border">⊞ Galerie</button>
700
700
  </div>
701
701
  </div>
702
702
  <select v-else-if="subField.type === 'select'" :value="item[subName]" @change="item[subName] = $event.target.value;
703
- trigger()" class="w-full bg-background rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm border">
703
+ trigger()" class="w-full bg-white rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm border">
704
704
  <option v-for="opt in subField.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
705
705
  </select>
706
706
  <template v-else-if="subField.i18n">
707
- <input :value="getI18nVal(item[subName], editingLang)" @input="setI18nVal(item, subName, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" :placeholder="langLabel(editingLang)" class="w-full bg-background rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
707
+ <input :value="getI18nVal(item[subName], editingLang)" @input="setI18nVal(item, subName, editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" :placeholder="langLabel(editingLang)" class="w-full bg-white rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
708
708
  </template>
709
709
  <div v-else-if="subField.type === 'stringlist'" class="space-y-1 mt-1">
710
710
  <div v-for="(feat, fi) in item[subName] || []" :key="fi" class="flex gap-1 items-center">
@@ -713,33 +713,33 @@ const savePage = async () => {
713
713
  @input="setI18nVal(item[subName], fi, editingLang, $event.target.value)"
714
714
  :dir="editingLang === 'ar' ? 'rtl' : 'ltr'"
715
715
  :placeholder="langLabel(editingLang)"
716
- class="flex-1 bg-background rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
716
+ class="flex-1 bg-white rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
717
717
  <button @click="item[subName].splice(fi, 1);
718
718
  trigger()" class="text-red-400 text-[8px] px-1 shrink-0">✕</button>
719
719
  </div>
720
720
  <button @click="if (!item[subName]) item[subName] = [];
721
721
  item[subName].push({ fr: '', ar: '', en: '' });
722
- trigger()" class="w-full py-1.5 border border-dashed rounded-lg text-[8px] font-black text-mgray-400 hover:border-mgray-400 transition">+ Fonctionnalité</button>
722
+ trigger()" class="w-full py-1.5 border border-dashed rounded-lg text-[8px] font-black text-gray-400 hover:border-gray-400 transition">+ Fonctionnalité</button>
723
723
  </div>
724
724
  <input v-else :value="item[subName]" @input="item[subName] = $event.target.value;
725
- trigger()" class="w-full bg-background rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
725
+ trigger()" class="w-full bg-white rounded-lg p-2 text-[10px] font-bold outline-none shadow-sm" />
726
726
  </div>
727
727
  </div>
728
728
  <button @click="if (!page.content[selectedBlockIndex].props[name]) page.content[selectedBlockIndex].props[name] = [];
729
729
  page.content[selectedBlockIndex].props[name].push({});
730
- trigger()" class="w-full py-2 border-2 border-dashed rounded-xl text-[8px] font-black text-mgray-400">+ AJOUTER</button>
730
+ trigger()" class="w-full py-2 border-2 border-dashed rounded-xl text-[8px] font-black text-gray-400">+ AJOUTER</button>
731
731
  </div>
732
732
  </div>
733
733
  </div>
734
734
 
735
735
  <!-- ── SEO CONFIG PANEL ── -->
736
736
  <div v-else-if="rightPanelMode === 'seo'">
737
- <h3 class="text-[9px] font-black text-mgray-300 uppercase mb-4 italic tracking-widest">Config SEO</h3>
737
+ <h3 class="text-[9px] font-black text-gray-300 uppercase mb-4 italic tracking-widest">Config SEO</h3>
738
738
 
739
739
  <!-- AI Audit -->
740
740
  <div v-if="aiEnabled" class="mb-6">
741
741
  <button @click="runSeoAudit" :disabled="isAuditing"
742
- class="w-full flex items-center justify-center gap-2 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest bg-primary-100 text-primary hover:bg-primary hover:text-white transition-all disabled:opacity-50">
742
+ class="w-full flex items-center justify-center gap-2 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest bg-[#3d35ff]/10 text-[#3d35ff] hover:bg-[#3d35ff] hover:text-white transition-all disabled:opacity-50">
743
743
  <svg v-if="isAuditing" class="w-3.5 h-3.5 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"/></svg>
744
744
  <svg v-else class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/></svg>
745
745
  {{ isAuditing ? "Analyse..." : "Audit IA" }}
@@ -748,7 +748,7 @@ const savePage = async () => {
748
748
 
749
749
  <!-- Audit results -->
750
750
  <div v-if="aiEnabled && seoAudit" class="mb-6 space-y-3">
751
- <div class="flex items-center gap-3 p-3 bg-mgray-50 rounded-xl">
751
+ <div class="flex items-center gap-3 p-3 bg-gray-50 rounded-xl">
752
752
  <div :class="[
753
753
  'w-11 h-11 rounded-xl flex items-center justify-center text-sm font-black shrink-0',
754
754
  seoAudit.score >= 75 ? 'bg-green-100 text-green-700' : seoAudit.score >= 50 ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-600'
@@ -756,15 +756,15 @@ const savePage = async () => {
756
756
  {{ seoAudit.score }}
757
757
  </div>
758
758
  <div>
759
- <p class="text-[9px] font-black uppercase tracking-widest text-mgray-400">Score SEO</p>
759
+ <p class="text-[9px] font-black uppercase tracking-widest text-gray-400">Score SEO</p>
760
760
  <p :class="['text-[11px] font-black', seoAudit.score >= 75 ? 'text-green-600' : seoAudit.score >= 50 ? 'text-amber-600' : 'text-red-500']">
761
761
  {{ seoAudit.score >= 75 ? "Bon" : seoAudit.score >= 50 ? "\xC0 am\xE9liorer" : "Insuffisant" }}
762
762
  </p>
763
763
  </div>
764
- <button @click="applySeoAudit" class="ml-auto px-3 py-1.5 bg-primary text-white rounded-lg text-[9px] font-black uppercase tracking-wide hover:brightness-90 transition shrink-0">Appliquer</button>
764
+ <button @click="applySeoAudit" class="ml-auto px-3 py-1.5 bg-[#3d35ff] text-white rounded-lg text-[9px] font-black uppercase tracking-wide hover:bg-[#2f28cc] transition shrink-0">Appliquer</button>
765
765
  </div>
766
766
  <div v-if="seoAudit.issues?.length" class="space-y-1.5">
767
- <p class="text-[8px] font-black uppercase tracking-widest text-mgray-400">Problèmes</p>
767
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-400">Problèmes</p>
768
768
  <div v-for="(issue, i) in seoAudit.issues" :key="i"
769
769
  :class="[
770
770
  'flex items-start gap-2 p-2.5 rounded-lg text-[10px]',
@@ -775,89 +775,89 @@ const savePage = async () => {
775
775
  </div>
776
776
  </div>
777
777
  <div v-if="seoAudit.suggestions?.length" class="space-y-1.5">
778
- <p class="text-[8px] font-black uppercase tracking-widest text-mgray-400">Recommandations</p>
779
- <div v-for="(tip, i) in seoAudit.suggestions" :key="i" class="flex items-start gap-2 p-2.5 bg-primary-50 rounded-lg text-[10px] text-primary">
778
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-400">Recommandations</p>
779
+ <div v-for="(tip, i) in seoAudit.suggestions" :key="i" class="flex items-start gap-2 p-2.5 bg-[#3d35ff]/5 rounded-lg text-[10px] text-[#3d35ff]">
780
780
  <span class="shrink-0 font-black mt-0.5">→</span>{{ tip }}
781
781
  </div>
782
782
  </div>
783
783
  <div v-if="seoAudit.keywords?.length">
784
- <p class="text-[8px] font-black uppercase tracking-widest text-mgray-400 mb-1.5">Mots-clés suggérés</p>
784
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-400 mb-1.5">Mots-clés suggérés</p>
785
785
  <div class="flex flex-wrap gap-1">
786
- <span v-for="kw in seoAudit.keywords" :key="kw" class="px-2 py-1 bg-mgray-100 text-mgray-600 rounded-lg text-[9px] font-semibold">{{ kw }}</span>
786
+ <span v-for="kw in seoAudit.keywords" :key="kw" class="px-2 py-1 bg-gray-100 text-gray-600 rounded-lg text-[9px] font-semibold">{{ kw }}</span>
787
787
  </div>
788
788
  </div>
789
- <div class="border-t border-mgray-100 pt-3">
790
- <p class="text-[8px] font-black uppercase tracking-widest text-mgray-400 mb-1">Meta title suggéré</p>
791
- <p class="text-[10px] text-mgray-700 bg-mgray-50 rounded-lg p-2">{{ seoAudit.meta_title?.fr }}</p>
792
- <p class="text-[8px] font-black uppercase tracking-widest text-mgray-400 mb-1 mt-2">Meta description suggérée</p>
793
- <p class="text-[10px] text-mgray-500 bg-mgray-50 rounded-lg p-2">{{ seoAudit.meta_description?.fr }}</p>
789
+ <div class="border-t border-gray-100 pt-3">
790
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-400 mb-1">Meta title suggéré</p>
791
+ <p class="text-[10px] text-gray-700 bg-gray-50 rounded-lg p-2">{{ seoAudit.meta_title?.fr }}</p>
792
+ <p class="text-[8px] font-black uppercase tracking-widest text-gray-400 mb-1 mt-2">Meta description suggérée</p>
793
+ <p class="text-[10px] text-gray-500 bg-gray-50 rounded-lg p-2">{{ seoAudit.meta_description?.fr }}</p>
794
794
  </div>
795
795
  </div>
796
796
 
797
- <div class="border-t border-mgray-100 pt-4 mb-5"></div>
797
+ <div class="border-t border-gray-100 pt-4 mb-5"></div>
798
798
 
799
799
  <div class="mb-5">
800
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">Type de page</label>
801
- <select v-model="page.type" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary">
800
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">Type de page</label>
801
+ <select v-model="page.type" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]">
802
802
  <option v-for="opt in pageTypeOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
803
803
  </select>
804
804
  </div>
805
805
 
806
806
  <div class="mb-5">
807
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">Titre de la page</label>
808
- <input v-model="page.title" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" placeholder="Nom interne de la page" />
807
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">Titre de la page</label>
808
+ <input v-model="page.title" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" placeholder="Nom interne de la page" />
809
809
  </div>
810
810
 
811
811
  <div class="mb-5">
812
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">URL (Slug)</label>
813
- <div class="flex items-center bg-mgray-50 rounded-xl ring-1 ring-gray-100 focus-within:ring-primary overflow-hidden">
814
- <span class="text-[9px] font-mono text-mgray-300 pl-3 pr-1 shrink-0">/</span>
812
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">URL (Slug)</label>
813
+ <div class="flex items-center bg-gray-50 rounded-xl ring-1 ring-gray-100 focus-within:ring-[#3d35ff] overflow-hidden">
814
+ <span class="text-[9px] font-mono text-gray-300 pl-3 pr-1 shrink-0">/</span>
815
815
  <input v-model="page.slug" class="flex-1 bg-transparent p-3 text-[10px] font-mono outline-none" placeholder="mon-url" />
816
816
  </div>
817
817
  </div>
818
818
 
819
819
  <div class="mb-5">
820
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">Meta Title</label>
820
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">Meta Title</label>
821
821
  <div class="relative">
822
- <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-primary-400">{{ langLabel(editingLang) }}</span>
823
- <input :value="getSeoI18n('meta_title', editingLang)" @input="setSeoI18n('meta_title', editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" placeholder="Title de la page..." class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
822
+ <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-[#3d35ff]/40">{{ langLabel(editingLang) }}</span>
823
+ <input :value="getSeoI18n('meta_title', editingLang)" @input="setSeoI18n('meta_title', editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" placeholder="Title de la page..." class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
824
824
  </div>
825
825
  </div>
826
826
 
827
827
  <div class="mb-5">
828
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">Meta Description</label>
828
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">Meta Description</label>
829
829
  <div class="relative">
830
- <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-primary-400">{{ langLabel(editingLang) }}</span>
831
- <textarea :value="getSeoI18n('meta_description', editingLang)" @input="setSeoI18n('meta_description', editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" rows="4" placeholder="Description courte de la page..." class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-primary"></textarea>
830
+ <span class="absolute -top-1 right-0 text-[6px] font-black uppercase text-[#3d35ff]/40">{{ langLabel(editingLang) }}</span>
831
+ <textarea :value="getSeoI18n('meta_description', editingLang)" @input="setSeoI18n('meta_description', editingLang, $event.target.value)" :dir="editingLang === 'ar' ? 'rtl' : 'ltr'" rows="4" placeholder="Description courte de la page..." class="w-full bg-gray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]"></textarea>
832
832
  </div>
833
833
  </div>
834
834
 
835
835
  <div class="mb-5">
836
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">OG Title <span class="text-mgray-300 normal-case font-medium">(défaut: meta title)</span></label>
837
- <input :value="page?.seo_config?.og_title ?? ''" @input="updateSeoField('og_title', $event.target.value)" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
836
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">OG Title <span class="text-gray-300 normal-case font-medium">(défaut: meta title)</span></label>
837
+ <input :value="page?.seo_config?.og_title ?? ''" @input="updateSeoField('og_title', $event.target.value)" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
838
838
  </div>
839
839
 
840
840
  <div class="mb-5">
841
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">OG Description</label>
842
- <textarea :value="page?.seo_config?.og_description ?? ''" @input="updateSeoField('og_description', $event.target.value)" rows="3" class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-primary"></textarea>
841
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">OG Description</label>
842
+ <textarea :value="page?.seo_config?.og_description ?? ''" @input="updateSeoField('og_description', $event.target.value)" rows="3" class="w-full bg-gray-50 rounded-xl p-3 text-[10px] outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]"></textarea>
843
843
  </div>
844
844
 
845
845
  <div class="mb-5">
846
- <label class="text-[8px] font-black uppercase text-mgray-400 mb-2 block tracking-widest">OG Image (URL)</label>
847
- <input :value="page?.seo_config?.og_image ?? ''" @input="updateSeoField('og_image', $event.target.value)" placeholder="https://..." class="w-full bg-mgray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
846
+ <label class="text-[8px] font-black uppercase text-gray-400 mb-2 block tracking-widest">OG Image (URL)</label>
847
+ <input :value="page?.seo_config?.og_image ?? ''" @input="updateSeoField('og_image', $event.target.value)" placeholder="https://..." class="w-full bg-gray-50 rounded-xl p-3 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
848
848
  </div>
849
849
 
850
- <div class="flex items-center justify-between p-3 bg-mgray-50 rounded-xl ring-1 ring-gray-100">
851
- <label class="text-[8px] font-black uppercase text-mgray-400 tracking-widest">No Index</label>
850
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-xl ring-1 ring-gray-100">
851
+ <label class="text-[8px] font-black uppercase text-gray-400 tracking-widest">No Index</label>
852
852
  <button @click="updateSeoField('no_index', !page?.seo_config?.no_index)"
853
- :class="['w-9 h-5 rounded-full transition-colors relative', page?.seo_config?.no_index ? 'bg-primary' : 'bg-mgray-200']">
854
- <span :class="['absolute top-0.5 w-4 h-4 bg-background rounded-full shadow transition-transform', page?.seo_config?.no_index ? 'translate-x-4' : 'translate-x-0.5']"></span>
853
+ :class="['w-9 h-5 rounded-full transition-colors relative', page?.seo_config?.no_index ? 'bg-[#3d35ff]' : 'bg-gray-200']">
854
+ <span :class="['absolute top-0.5 w-4 h-4 bg-white rounded-full shadow transition-transform', page?.seo_config?.no_index ? 'translate-x-4' : 'translate-x-0.5']"></span>
855
855
  </button>
856
856
  </div>
857
857
  </div>
858
858
 
859
859
  <!-- Empty state -->
860
- <div v-else class="text-center py-12 text-mgray-300">
860
+ <div v-else class="text-center py-12 text-gray-300">
861
861
  <p class="text-[9px] font-black uppercase tracking-widest">Sélectionnez un bloc</p>
862
862
  <p class="text-[8px] mt-2 opacity-60">ou passez en mode SEO →</p>
863
863
  </div>
@@ -868,21 +868,21 @@ const savePage = async () => {
868
868
 
869
869
  <!-- ── Gallery Modal ── -->
870
870
  <div v-if="showGallery" class="fixed inset-0 bg-black/90 backdrop-blur-xl z-[110] flex items-center justify-center p-6" @click.self="showGallery = false">
871
- <div class="bg-background max-w-4xl w-full rounded-[32px] flex flex-col overflow-hidden shadow-2xl" style="max-height:85vh" @click.stop>
871
+ <div class="bg-white max-w-4xl w-full rounded-[32px] flex flex-col overflow-hidden shadow-2xl" style="max-height:85vh" @click.stop>
872
872
  <div class="flex items-center gap-4 px-8 py-5 border-b shrink-0">
873
- <h2 class="text-[11px] font-black uppercase tracking-[0.4em] text-primary shrink-0">Galerie</h2>
873
+ <h2 class="text-[11px] font-black uppercase tracking-[0.4em] text-[#3d35ff] shrink-0">Galerie</h2>
874
874
  <div class="flex-1 relative">
875
- <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-mgray-300 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M21 21l-4.35-4.35M17 11A6 6 0 115 11a6 6 0 0112 0z"/></svg>
876
- <input v-model="gallerySearch" type="text" placeholder="Rechercher une image..." class="w-full bg-mgray-50 rounded-xl pl-9 pr-4 py-2 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-primary" />
875
+ <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-300 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M21 21l-4.35-4.35M17 11A6 6 0 115 11a6 6 0 0112 0z"/></svg>
876
+ <input v-model="gallerySearch" type="text" placeholder="Rechercher une image..." class="w-full bg-gray-50 rounded-xl pl-9 pr-4 py-2 text-[10px] font-bold outline-none ring-1 ring-gray-100 focus:ring-[#3d35ff]" />
877
877
  </div>
878
- <button @click="showGallery = false" class="text-mgray-400 hover:text-black transition-colors text-lg leading-none shrink-0">✕</button>
878
+ <button @click="showGallery = false" class="text-gray-400 hover:text-black transition-colors text-lg leading-none shrink-0">✕</button>
879
879
  </div>
880
880
  <div class="flex-1 overflow-y-auto p-6">
881
- <div v-if="isLoadingGallery" class="flex items-center justify-center py-20 text-mgray-300 text-[9px] font-black uppercase tracking-widest">Chargement...</div>
882
- <div v-else-if="filteredGallery.length === 0" class="flex items-center justify-center py-20 text-mgray-300 text-[9px] font-black uppercase tracking-widest">Aucune image trouvée</div>
881
+ <div v-if="isLoadingGallery" class="flex items-center justify-center py-20 text-gray-300 text-[9px] font-black uppercase tracking-widest">Chargement...</div>
882
+ <div v-else-if="filteredGallery.length === 0" class="flex items-center justify-center py-20 text-gray-300 text-[9px] font-black uppercase tracking-widest">Aucune image trouvée</div>
883
883
  <div v-else class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 gap-3">
884
884
  <button v-for="img in filteredGallery" :key="img.url" @click="selectGalleryImage(img.url)"
885
- class="group relative aspect-square rounded-xl overflow-hidden border-2 border-transparent hover:border-primary transition-all focus:outline-none">
885
+ class="group relative aspect-square rounded-xl overflow-hidden border-2 border-transparent hover:border-[#3d35ff] transition-all focus:outline-none">
886
886
  <img :src="img.url" :alt="img.name" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" />
887
887
  <div class="absolute bottom-0 inset-x-0 bg-black/60 px-2 py-1 translate-y-full group-hover:translate-y-0 transition-transform duration-200">
888
888
  <p class="text-white text-[7px] font-bold truncate">{{ img.name }}</p>
@@ -895,16 +895,16 @@ const savePage = async () => {
895
895
 
896
896
  <!-- ── Add Section Modal ── -->
897
897
  <div v-if="showAddModal" class="fixed inset-0 bg-black/95 backdrop-blur-xl z-[100] flex items-center justify-center p-6" @click.self="showAddModal = false">
898
- <div class="bg-background max-w-4xl w-full rounded-[40px] p-12 shadow-2xl overflow-y-auto max-h-[85vh]">
899
- <h2 class="text-[12px] font-black uppercase text-center mb-12 tracking-[0.4em] italic text-primary">Nouvelle Section</h2>
898
+ <div class="bg-white max-w-4xl w-full rounded-[40px] p-12 shadow-2xl overflow-y-auto max-h-[85vh]">
899
+ <h2 class="text-[12px] font-black uppercase text-center mb-12 tracking-[0.4em] italic text-[#3d35ff]">Nouvelle Section</h2>
900
900
  <div class="grid grid-cols-2 md:grid-cols-4 gap-8">
901
901
  <button v-for="(conf, type) in CMS_COMPONENTS" :key="type"
902
902
  @click="page.content.push({ type, props: JSON.parse(JSON.stringify(adminSettings.applyPresets(type, conf.defaultProps))) });
903
903
  showAddModal = false;
904
904
  selectedBlockIndex = page.content.length - 1"
905
- class="flex flex-col items-center gap-6 p-8 border-2 border-mgray-50 rounded-[40px] hover:border-primary transition-all group active:scale-95">
905
+ class="flex flex-col items-center gap-6 p-8 border-2 border-gray-50 rounded-[40px] hover:border-[#3d35ff] transition-all group active:scale-95">
906
906
  <span class="text-6xl group-hover:scale-110 transition-transform">{{ conf.icon }}</span>
907
- <span class="text-[10px] font-black uppercase tracking-widest text-mgray">{{ conf.label }}</span>
907
+ <span class="text-[10px] font-black uppercase tracking-widest text-gray-900">{{ conf.label }}</span>
908
908
  </button>
909
909
  </div>
910
910
  </div>