cinqcinqdev-seo 0.1.71 → 0.1.73

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 (123) hide show
  1. package/README.md +39 -2
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/assets/admin-tw.css +1 -1
  4. package/dist/runtime/components/admin-cms/DynamicRenderer.vue +16 -2
  5. package/dist/runtime/components/sections/{BlogHero.vue → cinqcinq/BlogHero.vue} +35 -3
  6. package/dist/runtime/components/sections/{CallToAction.vue → cinqcinq/CallToAction.vue} +35 -3
  7. package/dist/runtime/components/sections/{ContactForm.vue → cinqcinq/ContactForm.vue} +34 -2
  8. package/dist/runtime/components/sections/{FAQ.vue → cinqcinq/FAQ.vue} +35 -3
  9. package/dist/runtime/components/sections/{Features.vue → cinqcinq/Features.vue} +35 -3
  10. package/dist/runtime/components/sections/{HeroSection.vue → cinqcinq/HeroSection.vue} +11 -0
  11. package/dist/runtime/components/sections/{Newsletter.vue → cinqcinq/Newsletter.vue} +35 -3
  12. package/dist/runtime/components/sections/{Pricing.vue → cinqcinq/Pricing.vue} +35 -3
  13. package/dist/runtime/components/sections/{Process.vue → cinqcinq/Process.vue} +35 -3
  14. package/dist/runtime/components/sections/{Quote.vue → cinqcinq/Quote.vue} +35 -3
  15. package/dist/runtime/components/sections/{RichText.vue → cinqcinq/RichText.vue} +32 -4
  16. package/dist/runtime/components/sections/{ServiceGrid.vue → cinqcinq/ServiceGrid.vue} +35 -3
  17. package/dist/runtime/components/sections/{Testimonials.vue → cinqcinq/Testimonials.vue} +35 -3
  18. package/dist/runtime/components/sections/{TextVisual.vue → cinqcinq/TextVisual.vue} +39 -3
  19. package/dist/runtime/components/sections/swiss-style/BlogHero.d.vue.ts +3 -0
  20. package/dist/runtime/components/sections/swiss-style/BlogHero.vue +146 -0
  21. package/dist/runtime/components/sections/swiss-style/BlogHero.vue.d.ts +3 -0
  22. package/dist/runtime/components/sections/swiss-style/CallToAction.d.vue.ts +3 -0
  23. package/dist/runtime/components/sections/swiss-style/CallToAction.vue +138 -0
  24. package/dist/runtime/components/sections/swiss-style/CallToAction.vue.d.ts +3 -0
  25. package/dist/runtime/components/sections/swiss-style/Comparison.d.vue.ts +3 -0
  26. package/dist/runtime/components/sections/swiss-style/Comparison.vue +280 -0
  27. package/dist/runtime/components/sections/swiss-style/Comparison.vue.d.ts +3 -0
  28. package/dist/runtime/components/sections/swiss-style/ContactForm.d.vue.ts +3 -0
  29. package/dist/runtime/components/sections/swiss-style/ContactForm.vue +173 -0
  30. package/dist/runtime/components/sections/swiss-style/ContactForm.vue.d.ts +3 -0
  31. package/dist/runtime/components/sections/swiss-style/DataTable.d.vue.ts +3 -0
  32. package/dist/runtime/components/sections/swiss-style/DataTable.vue +245 -0
  33. package/dist/runtime/components/sections/swiss-style/DataTable.vue.d.ts +3 -0
  34. package/dist/runtime/components/sections/swiss-style/FAQ.d.vue.ts +3 -0
  35. package/dist/runtime/components/sections/swiss-style/FAQ.vue +107 -0
  36. package/dist/runtime/components/sections/swiss-style/FAQ.vue.d.ts +3 -0
  37. package/dist/runtime/components/sections/swiss-style/Features.d.vue.ts +3 -0
  38. package/dist/runtime/components/sections/swiss-style/Features.vue +103 -0
  39. package/dist/runtime/components/sections/swiss-style/Features.vue.d.ts +3 -0
  40. package/dist/runtime/components/sections/swiss-style/HeroFan.d.vue.ts +3 -0
  41. package/dist/runtime/components/sections/swiss-style/HeroFan.vue +108 -0
  42. package/dist/runtime/components/sections/swiss-style/HeroFan.vue.d.ts +3 -0
  43. package/dist/runtime/components/sections/swiss-style/HeroSection.d.vue.ts +3 -0
  44. package/dist/runtime/components/sections/swiss-style/HeroSection.vue +190 -0
  45. package/dist/runtime/components/sections/swiss-style/HeroSection.vue.d.ts +3 -0
  46. package/dist/runtime/components/sections/swiss-style/HeroSlider.d.vue.ts +3 -0
  47. package/dist/runtime/components/sections/swiss-style/HeroSlider.vue +138 -0
  48. package/dist/runtime/components/sections/swiss-style/HeroSlider.vue.d.ts +3 -0
  49. package/dist/runtime/components/sections/swiss-style/Newsletter.d.vue.ts +3 -0
  50. package/dist/runtime/components/sections/swiss-style/Newsletter.vue +125 -0
  51. package/dist/runtime/components/sections/swiss-style/Newsletter.vue.d.ts +3 -0
  52. package/dist/runtime/components/sections/swiss-style/Pricing.d.vue.ts +3 -0
  53. package/dist/runtime/components/sections/swiss-style/Pricing.vue +148 -0
  54. package/dist/runtime/components/sections/swiss-style/Pricing.vue.d.ts +3 -0
  55. package/dist/runtime/components/sections/swiss-style/Process.d.vue.ts +3 -0
  56. package/dist/runtime/components/sections/swiss-style/Process.vue +124 -0
  57. package/dist/runtime/components/sections/swiss-style/Process.vue.d.ts +3 -0
  58. package/dist/runtime/components/sections/swiss-style/Quote.d.vue.ts +3 -0
  59. package/dist/runtime/components/sections/swiss-style/Quote.vue +130 -0
  60. package/dist/runtime/components/sections/swiss-style/Quote.vue.d.ts +3 -0
  61. package/dist/runtime/components/sections/swiss-style/RelatedPages.d.vue.ts +3 -0
  62. package/dist/runtime/components/sections/swiss-style/RelatedPages.vue +212 -0
  63. package/dist/runtime/components/sections/swiss-style/RelatedPages.vue.d.ts +3 -0
  64. package/dist/runtime/components/sections/swiss-style/RichText.d.vue.ts +3 -0
  65. package/dist/runtime/components/sections/swiss-style/RichText.vue +100 -0
  66. package/dist/runtime/components/sections/swiss-style/RichText.vue.d.ts +3 -0
  67. package/dist/runtime/components/sections/swiss-style/ServiceGrid.d.vue.ts +3 -0
  68. package/dist/runtime/components/sections/swiss-style/ServiceGrid.vue +121 -0
  69. package/dist/runtime/components/sections/swiss-style/ServiceGrid.vue.d.ts +3 -0
  70. package/dist/runtime/components/sections/swiss-style/Testimonials.d.vue.ts +3 -0
  71. package/dist/runtime/components/sections/swiss-style/Testimonials.vue +126 -0
  72. package/dist/runtime/components/sections/swiss-style/Testimonials.vue.d.ts +3 -0
  73. package/dist/runtime/components/sections/swiss-style/TextVisual.d.vue.ts +3 -0
  74. package/dist/runtime/components/sections/swiss-style/TextVisual.vue +146 -0
  75. package/dist/runtime/components/sections/swiss-style/TextVisual.vue.d.ts +3 -0
  76. package/dist/runtime/composables/useAdminSections.js +75 -37
  77. package/dist/runtime/composables/useAdminSettings.d.ts +1 -0
  78. package/dist/runtime/pages/admin/editor/[id].vue +15 -1
  79. package/dist/runtime/pages/admin/setup.vue +77 -2
  80. package/package.json +1 -1
  81. /package/dist/runtime/components/sections/{BlogHero.d.vue.ts → cinqcinq/BlogHero.d.vue.ts} +0 -0
  82. /package/dist/runtime/components/sections/{BlogHero.vue.d.ts → cinqcinq/BlogHero.vue.d.ts} +0 -0
  83. /package/dist/runtime/components/sections/{CallToAction.d.vue.ts → cinqcinq/CallToAction.d.vue.ts} +0 -0
  84. /package/dist/runtime/components/sections/{CallToAction.vue.d.ts → cinqcinq/CallToAction.vue.d.ts} +0 -0
  85. /package/dist/runtime/components/sections/{Comparison.d.vue.ts → cinqcinq/Comparison.d.vue.ts} +0 -0
  86. /package/dist/runtime/components/sections/{Comparison.vue → cinqcinq/Comparison.vue} +0 -0
  87. /package/dist/runtime/components/sections/{Comparison.vue.d.ts → cinqcinq/Comparison.vue.d.ts} +0 -0
  88. /package/dist/runtime/components/sections/{ContactForm.d.vue.ts → cinqcinq/ContactForm.d.vue.ts} +0 -0
  89. /package/dist/runtime/components/sections/{ContactForm.vue.d.ts → cinqcinq/ContactForm.vue.d.ts} +0 -0
  90. /package/dist/runtime/components/sections/{DataTable.d.vue.ts → cinqcinq/DataTable.d.vue.ts} +0 -0
  91. /package/dist/runtime/components/sections/{DataTable.vue → cinqcinq/DataTable.vue} +0 -0
  92. /package/dist/runtime/components/sections/{DataTable.vue.d.ts → cinqcinq/DataTable.vue.d.ts} +0 -0
  93. /package/dist/runtime/components/sections/{FAQ.d.vue.ts → cinqcinq/FAQ.d.vue.ts} +0 -0
  94. /package/dist/runtime/components/sections/{FAQ.vue.d.ts → cinqcinq/FAQ.vue.d.ts} +0 -0
  95. /package/dist/runtime/components/sections/{Features.d.vue.ts → cinqcinq/Features.d.vue.ts} +0 -0
  96. /package/dist/runtime/components/sections/{Features.vue.d.ts → cinqcinq/Features.vue.d.ts} +0 -0
  97. /package/dist/runtime/components/sections/{HeroFan.d.vue.ts → cinqcinq/HeroFan.d.vue.ts} +0 -0
  98. /package/dist/runtime/components/sections/{HeroFan.vue → cinqcinq/HeroFan.vue} +0 -0
  99. /package/dist/runtime/components/sections/{HeroFan.vue.d.ts → cinqcinq/HeroFan.vue.d.ts} +0 -0
  100. /package/dist/runtime/components/sections/{HeroSection.d.vue.ts → cinqcinq/HeroSection.d.vue.ts} +0 -0
  101. /package/dist/runtime/components/sections/{HeroSection.vue.d.ts → cinqcinq/HeroSection.vue.d.ts} +0 -0
  102. /package/dist/runtime/components/sections/{HeroSlider.d.vue.ts → cinqcinq/HeroSlider.d.vue.ts} +0 -0
  103. /package/dist/runtime/components/sections/{HeroSlider.vue → cinqcinq/HeroSlider.vue} +0 -0
  104. /package/dist/runtime/components/sections/{HeroSlider.vue.d.ts → cinqcinq/HeroSlider.vue.d.ts} +0 -0
  105. /package/dist/runtime/components/sections/{Newsletter.d.vue.ts → cinqcinq/Newsletter.d.vue.ts} +0 -0
  106. /package/dist/runtime/components/sections/{Newsletter.vue.d.ts → cinqcinq/Newsletter.vue.d.ts} +0 -0
  107. /package/dist/runtime/components/sections/{Pricing.d.vue.ts → cinqcinq/Pricing.d.vue.ts} +0 -0
  108. /package/dist/runtime/components/sections/{Pricing.vue.d.ts → cinqcinq/Pricing.vue.d.ts} +0 -0
  109. /package/dist/runtime/components/sections/{Process.d.vue.ts → cinqcinq/Process.d.vue.ts} +0 -0
  110. /package/dist/runtime/components/sections/{Process.vue.d.ts → cinqcinq/Process.vue.d.ts} +0 -0
  111. /package/dist/runtime/components/sections/{Quote.d.vue.ts → cinqcinq/Quote.d.vue.ts} +0 -0
  112. /package/dist/runtime/components/sections/{Quote.vue.d.ts → cinqcinq/Quote.vue.d.ts} +0 -0
  113. /package/dist/runtime/components/sections/{RelatedPages.d.vue.ts → cinqcinq/RelatedPages.d.vue.ts} +0 -0
  114. /package/dist/runtime/components/sections/{RelatedPages.vue → cinqcinq/RelatedPages.vue} +0 -0
  115. /package/dist/runtime/components/sections/{RelatedPages.vue.d.ts → cinqcinq/RelatedPages.vue.d.ts} +0 -0
  116. /package/dist/runtime/components/sections/{RichText.d.vue.ts → cinqcinq/RichText.d.vue.ts} +0 -0
  117. /package/dist/runtime/components/sections/{RichText.vue.d.ts → cinqcinq/RichText.vue.d.ts} +0 -0
  118. /package/dist/runtime/components/sections/{ServiceGrid.d.vue.ts → cinqcinq/ServiceGrid.d.vue.ts} +0 -0
  119. /package/dist/runtime/components/sections/{ServiceGrid.vue.d.ts → cinqcinq/ServiceGrid.vue.d.ts} +0 -0
  120. /package/dist/runtime/components/sections/{Testimonials.d.vue.ts → cinqcinq/Testimonials.d.vue.ts} +0 -0
  121. /package/dist/runtime/components/sections/{Testimonials.vue.d.ts → cinqcinq/Testimonials.vue.d.ts} +0 -0
  122. /package/dist/runtime/components/sections/{TextVisual.d.vue.ts → cinqcinq/TextVisual.d.vue.ts} +0 -0
  123. /package/dist/runtime/components/sections/{TextVisual.vue.d.ts → cinqcinq/TextVisual.vue.d.ts} +0 -0
@@ -0,0 +1,125 @@
1
+ <template>
2
+ <section ref="sectionEl" :class="[sectionPad, isVisible ? 'sv-in' : 'sv-out']" :dir="textDirection" :style="[{ backgroundColor: bgColor, color: textColor }, spacingStyle]">
3
+
4
+ <!-- CENTERED -->
5
+ <div v-if="layout === 'centered'" :class="[containerClass]">
6
+ <div class="border py-12 px-10 md:px-16" :style="{ borderColor: ca(textColor,'20') }">
7
+ <div class="grid md:grid-cols-2 gap-10 items-end">
8
+ <div>
9
+ <span v-if="badge" class="block text-[9px] font-black uppercase tracking-[0.5em] mb-4" :style="{ opacity: 0.35 }">{{ badge }}</span>
10
+ <h2 class="font-black uppercase leading-[0.88]" :style="{ fontSize: titleFontSize, letterSpacing: '-0.03em' }">{{ title }}</h2>
11
+ <p v-if="description" :class="[bodyClass, 'leading-relaxed mt-3']" :style="{ opacity: 0.6 }">{{ description }}</p>
12
+ </div>
13
+ <div>
14
+ <div class="flex border" :style="{ borderColor: ca(textColor,'30') }">
15
+ <input type="email" :placeholder="placeholder || 'your@email.com'" class="flex-1 px-4 py-3 text-sm outline-none bg-transparent" :style="{ color: textColor }" />
16
+ <button class="px-5 py-3 text-[11px] font-black uppercase tracking-wider border-l shrink-0" :style="{ borderColor: ca(textColor,'30'), backgroundColor: textColor, color: bgColor }">{{ buttonText || 'Subscribe' }}</button>
17
+ </div>
18
+ <p v-if="disclaimer" class="text-[11px] mt-2" :style="{ opacity: 0.35 }">{{ disclaimer }}</p>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- SPLIT -->
25
+ <div v-else-if="layout === 'split'" :class="[containerClass, 'grid grid-cols-1 md:grid-cols-2 border divide-x']" :style="{ borderColor: ca(textColor,'15') }">
26
+ <div class="p-10">
27
+ <span v-if="badge" class="block text-[9px] font-black uppercase tracking-[0.5em] mb-4" :style="{ opacity: 0.35 }">{{ badge }}</span>
28
+ <h2 class="font-black uppercase leading-[0.88]" :style="{ fontSize: titleFontSize, letterSpacing: '-0.03em' }">{{ title }}</h2>
29
+ <p v-if="description" :class="[bodyClass, 'leading-relaxed mt-3']" :style="{ opacity: 0.6 }">{{ description }}</p>
30
+ </div>
31
+ <div class="p-10 flex flex-col justify-center">
32
+ <div class="flex border" :style="{ borderColor: ca(textColor,'30') }">
33
+ <input type="email" :placeholder="placeholder || 'your@email.com'" class="flex-1 px-4 py-3 text-sm outline-none bg-transparent" :style="{ color: textColor }" />
34
+ <button class="px-5 py-3 text-[11px] font-black uppercase tracking-wider border-l shrink-0" :style="{ borderColor: ca(textColor,'30'), backgroundColor: textColor, color: bgColor }">{{ buttonText || 'Subscribe' }}</button>
35
+ </div>
36
+ <p v-if="disclaimer" class="text-[11px] mt-2" :style="{ opacity: 0.35 }">{{ disclaimer }}</p>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- MINIMAL (default) -->
41
+ <div v-else :class="[containerClass]">
42
+ <div class="flex flex-col md:flex-row items-start md:items-center gap-8 border-t border-b py-10" :style="{ borderColor: ca(textColor,'15') }">
43
+ <div class="flex-1">
44
+ <h2 class="font-black uppercase leading-[0.9]" :style="{ fontSize: titleFontSize, letterSpacing: '-0.03em' }">{{ title }}</h2>
45
+ <p v-if="description" :class="[bodyClass, 'mt-1']" :style="{ opacity: 0.55 }">{{ description }}</p>
46
+ </div>
47
+ <div class="flex gap-0 w-full md:w-auto border" :style="{ borderColor: ca(textColor,'30') }">
48
+ <input type="email" :placeholder="placeholder || 'your@email.com'" class="flex-1 md:w-48 px-4 py-2.5 text-sm outline-none bg-transparent" :style="{ color: textColor }" />
49
+ <button class="px-5 py-2.5 text-[11px] font-black uppercase tracking-wider border-l shrink-0" :style="{ borderColor: ca(textColor,'30'), backgroundColor: textColor, color: bgColor }">{{ buttonText || 'OK' }}</button>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ </section>
55
+ </template>
56
+
57
+ <script setup>
58
+ const ca = (c, h) => {
59
+ const m = c?.match(/rgba\((var\([^)]+\)),\s*[\d.]+\)/);
60
+ return m ? `rgba(${m[1]}, ${Math.round(parseInt(h, 16) / 255 * 1e3) / 1e3})` : (c || "") + h;
61
+ };
62
+ import { ref, computed, onMounted } from "vue";
63
+ const props = defineProps({
64
+ layout: { type: String, default: "centered" },
65
+ badge: { type: String, default: "" },
66
+ title: { type: String, default: "Stay in the loop" },
67
+ description: { type: String, default: "" },
68
+ placeholder: { type: String, default: "your@email.com" },
69
+ buttonText: { type: String, default: "Subscribe" },
70
+ disclaimer: { type: String, default: "" },
71
+ bgColor: { type: String, default: "rgba(var(--body-bg), 1)" },
72
+ textColor: { type: String, default: "rgba(var(--text-color), 1)" },
73
+ spacing: { type: String, default: "normal" },
74
+ radius: { type: String, default: "sharp" },
75
+ textDirection: { type: String, default: "ltr" },
76
+ contentWidth: { type: String, default: "normal" },
77
+ textSize: { type: String, default: "md" },
78
+ paddingTop: { type: String, default: "" },
79
+ paddingBottom: { type: String, default: "" },
80
+ marginTop: { type: String, default: "" },
81
+ marginBottom: { type: String, default: "" },
82
+ paddingX: { type: String, default: "" }
83
+ });
84
+ const sectionEl = ref(null);
85
+ const isVisible = ref(false);
86
+ onMounted(() => {
87
+ const obs = new IntersectionObserver(([e]) => {
88
+ if (e.isIntersecting) {
89
+ isVisible.value = true;
90
+ obs.disconnect();
91
+ }
92
+ }, { threshold: 0.06 });
93
+ if (sectionEl.value) obs.observe(sectionEl.value);
94
+ setTimeout(() => {
95
+ if (!isVisible.value) isVisible.value = true;
96
+ }, 300);
97
+ });
98
+ const sectionPad = computed(() => ({ compact: "py-8 px-4 md:px-8", normal: "py-14 px-8 md:px-16", spacious: "py-24 px-8 md:px-16" })[props.spacing] || "py-14 px-8 md:px-16");
99
+ const containerClass = computed(() => ({ narrow: "max-w-3xl mx-auto", normal: "max-w-6xl mx-auto", wide: "max-w-7xl mx-auto", full: "max-w-full" })[props.contentWidth] || "max-w-6xl mx-auto");
100
+ const titleFontSize = computed(() => ({ sm: "clamp(1rem,1.5vw,1.3rem)", md: "clamp(1.5rem,2.5vw,2rem)", lg: "clamp(2.5rem,6vw,5rem)" })[props.textSize] || "clamp(1.5rem,2.5vw,2rem)");
101
+ const bodyClass = computed(() => ({ sm: "text-xs", md: "text-sm", lg: "text-base" })[props.textSize] || "text-sm");
102
+ const SM = { none: "0", xs: "8px", sm: "24px", md: "48px", lg: "80px", xl: "128px", "-xs": "-8px", "-sm": "-24px", "-md": "-48px", "-lg": "-80px", "-xl": "-128px" };
103
+ const spacingStyle = computed(() => {
104
+ const s = {};
105
+ if (props.paddingTop && SM[props.paddingTop]) s.paddingTop = SM[props.paddingTop];
106
+ if (props.paddingBottom && SM[props.paddingBottom]) s.paddingBottom = SM[props.paddingBottom];
107
+ if (props.marginTop && SM[props.marginTop]) s.marginTop = SM[props.marginTop];
108
+ if (props.marginBottom && SM[props.marginBottom]) s.marginBottom = SM[props.marginBottom];
109
+ if (props.paddingX && SM[props.paddingX]) {
110
+ const v = SM[props.paddingX];
111
+ if (v.startsWith("-")) {
112
+ s.marginLeft = v;
113
+ s.marginRight = v;
114
+ } else {
115
+ s.paddingLeft = v;
116
+ s.paddingRight = v;
117
+ }
118
+ }
119
+ return s;
120
+ });
121
+ </script>
122
+
123
+ <style scoped>
124
+ .sv-out{opacity:0;transform:translateY(16px)}.sv-in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s ease}
125
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,148 @@
1
+ <template>
2
+ <section ref="sectionEl" :class="[sectionPad, isVisible ? 'sv-in' : 'sv-out']" :dir="textDirection" :style="[{ backgroundColor: bgColor, color: textColor }, spacingStyle]">
3
+ <div :class="[containerClass]">
4
+
5
+ <!-- Header -->
6
+ <div v-if="title || description" class="border-b pb-6 mb-0" :style="{ borderColor: ca(textColor,'15') }">
7
+ <span v-if="badge" class="block text-[9px] font-black uppercase tracking-[0.5em] mb-3" :style="{ opacity: 0.35 }">{{ badge }}</span>
8
+ <component :is="titleTag" class="font-black uppercase leading-[0.88]" :style="{ fontSize: titleFontSize, letterSpacing: '-0.03em' }">{{ title }}</component>
9
+ <p v-if="description" :class="[bodyClass, 'leading-relaxed mt-3 max-w-xl']" :style="{ opacity: 0.55 }">{{ description }}</p>
10
+ </div>
11
+
12
+ <!-- CARDS -->
13
+ <div v-if="layout === 'cards'" class="grid grid-cols-1 md:grid-cols-3 border divide-x" :style="{ borderColor: ca(textColor,'15') }">
14
+ <div v-for="(plan, i) in items" :key="i" class="p-8 flex flex-col relative" :style="plan.highlighted ? { backgroundColor: textColor, color: bgColor } : {}">
15
+ <div v-if="plan.highlighted" class="absolute top-0 right-0 px-3 py-1 text-[9px] font-black uppercase tracking-widest" :style="{ backgroundColor: bgColor, color: textColor }">Popular</div>
16
+ <p class="text-[10px] font-black uppercase tracking-[0.4em] mb-4" :style="{ opacity: 0.5 }">{{ plan.name }}</p>
17
+ <div class="mb-4 border-b pb-6" :style="{ borderColor: plan.highlighted ? ca(bgColor,'25') : ca(textColor,'12') }">
18
+ <span class="text-4xl font-black tracking-tight">{{ plan.price }}</span>
19
+ <span v-if="plan.period" class="text-sm ml-1" :style="{ opacity: 0.5 }">{{ plan.period }}</span>
20
+ </div>
21
+ <p v-if="plan.description" class="text-xs leading-relaxed mb-6" :style="{ opacity: 0.55 }">{{ plan.description }}</p>
22
+ <ul v-if="plan.features && plan.features.length" class="space-y-2.5 mb-8 flex-1">
23
+ <li v-for="(feat, fi) in plan.features" :key="fi" class="flex items-start gap-3 text-sm">
24
+ <span class="text-[10px] mt-0.5 shrink-0 font-black">—</span>
25
+ <span :style="{ opacity: 0.75 }">{{ typeof feat === 'object' ? (feat.fr || feat) : feat }}</span>
26
+ </li>
27
+ </ul>
28
+ <div class="flex-1"></div>
29
+ <NuxtLink v-if="plan.ctaText || plan.ctaLink" :to="plan.ctaLink || '#'"
30
+ class="block text-center py-3 px-6 text-[11px] font-black uppercase tracking-widest transition-all hover:opacity-80 mt-6 border"
31
+ :style="plan.highlighted ? { backgroundColor: bgColor, color: textColor, borderColor: bgColor } : { borderColor: textColor, color: textColor }">
32
+ {{ plan.ctaText || 'Get started' }}
33
+ </NuxtLink>
34
+ </div>
35
+ </div>
36
+
37
+ <!-- MINIMAL (list) -->
38
+ <div v-else-if="layout === 'minimal'" class="divide-y max-w-2xl" :style="{ borderColor: ca(textColor,'12') }">
39
+ <div v-for="(plan, i) in items" :key="i" class="flex items-center gap-6 py-7" :style="{ borderColor: ca(textColor,'12') }">
40
+ <div class="flex-1">
41
+ <p class="font-black uppercase tracking-tight text-lg leading-none mb-1">{{ plan.name }}</p>
42
+ <p v-if="plan.description" class="text-xs" :style="{ opacity: 0.5 }">{{ plan.description }}</p>
43
+ </div>
44
+ <div class="text-right shrink-0">
45
+ <p class="text-2xl font-black tracking-tighter">{{ plan.price }}</p>
46
+ <p v-if="plan.period" class="text-[10px]" :style="{ opacity: 0.4 }">{{ plan.period }}</p>
47
+ </div>
48
+ <NuxtLink v-if="plan.ctaText || plan.ctaLink" :to="plan.ctaLink || '#'" class="inline-flex items-center gap-2 text-[10px] font-black uppercase tracking-widest transition-all hover:gap-4 shrink-0 border px-4 py-2" :style="{ borderColor: ca(textColor,'30'), color: textColor }">{{ plan.ctaText || '→' }}</NuxtLink>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- TABLE (default) -->
53
+ <div v-else class="overflow-x-auto">
54
+ <table class="w-full min-w-[540px] border-collapse">
55
+ <thead>
56
+ <tr class="border-b" :style="{ borderColor: ca(textColor,'15') }">
57
+ <th class="text-left py-6 pr-6 align-bottom"><span class="text-[10px] font-black uppercase tracking-wider" :style="{ opacity: 0.35 }">Plan</span></th>
58
+ <th v-for="(plan, i) in items" :key="i" class="py-6 px-4 align-bottom border-l" :style="{ borderColor: ca(textColor,'10') }">
59
+ <div :style="plan.highlighted ? { color: textColor } : {}">
60
+ <div class="text-[10px] font-black uppercase tracking-[0.35em] mb-2" :style="{ opacity: 0.4 }">{{ plan.name }}</div>
61
+ <div class="font-black text-2xl tracking-tight">{{ plan.price }}</div>
62
+ </div>
63
+ </th>
64
+ </tr>
65
+ </thead>
66
+ <tbody>
67
+ <tr v-for="(feat, fi) in (items[0]?.features || [])" :key="fi" class="border-b" :style="{ borderColor: ca(textColor,'08') }">
68
+ <td class="py-4 pr-6 text-sm" :style="{ opacity: 0.6 }">{{ typeof feat === 'object' ? (feat.fr || feat) : feat }}</td>
69
+ <td v-for="(plan, pi) in items" :key="pi" class="py-4 px-4 text-center border-l" :style="{ borderColor: ca(textColor,'08') }">
70
+ <span class="text-[11px] font-black">✓</span>
71
+ </td>
72
+ </tr>
73
+ </tbody>
74
+ </table>
75
+ </div>
76
+
77
+ </div>
78
+ </section>
79
+ </template>
80
+
81
+ <script setup>
82
+ const ca = (c, h) => {
83
+ const m = c?.match(/rgba\((var\([^)]+\)),\s*[\d.]+\)/);
84
+ return m ? `rgba(${m[1]}, ${Math.round(parseInt(h, 16) / 255 * 1e3) / 1e3})` : (c || "") + h;
85
+ };
86
+ import { ref, computed, onMounted } from "vue";
87
+ const props = defineProps({
88
+ layout: { type: String, default: "cards" },
89
+ badge: { type: String, default: "" },
90
+ title: { type: String, default: "" },
91
+ titleTag: { type: String, default: "h2" },
92
+ description: { type: String, default: "" },
93
+ items: { type: Array, default: () => [] },
94
+ bgColor: { type: String, default: "rgba(var(--body-bg), 1)" },
95
+ textColor: { type: String, default: "rgba(var(--text-color), 1)" },
96
+ spacing: { type: String, default: "normal" },
97
+ radius: { type: String, default: "sharp" },
98
+ textDirection: { type: String, default: "ltr" },
99
+ contentWidth: { type: String, default: "normal" },
100
+ textSize: { type: String, default: "md" },
101
+ paddingTop: { type: String, default: "" },
102
+ paddingBottom: { type: String, default: "" },
103
+ marginTop: { type: String, default: "" },
104
+ marginBottom: { type: String, default: "" },
105
+ paddingX: { type: String, default: "" }
106
+ });
107
+ const sectionEl = ref(null);
108
+ const isVisible = ref(false);
109
+ onMounted(() => {
110
+ const obs = new IntersectionObserver(([e]) => {
111
+ if (e.isIntersecting) {
112
+ isVisible.value = true;
113
+ obs.disconnect();
114
+ }
115
+ }, { threshold: 0.06 });
116
+ if (sectionEl.value) obs.observe(sectionEl.value);
117
+ setTimeout(() => {
118
+ if (!isVisible.value) isVisible.value = true;
119
+ }, 300);
120
+ });
121
+ const sectionPad = computed(() => ({ compact: "py-8 px-4 md:px-8", normal: "py-14 px-8 md:px-16", spacious: "py-24 px-8 md:px-16" })[props.spacing] || "py-14 px-8 md:px-16");
122
+ const containerClass = computed(() => ({ narrow: "max-w-3xl mx-auto", normal: "max-w-6xl mx-auto", wide: "max-w-7xl mx-auto", full: "max-w-full" })[props.contentWidth] || "max-w-6xl mx-auto");
123
+ const titleFontSize = computed(() => ({ sm: "clamp(1rem,1.5vw,1.3rem)", md: "clamp(1.5rem,2.5vw,2rem)", lg: "clamp(2.5rem,6vw,5rem)" })[props.textSize] || "clamp(1.5rem,2.5vw,2rem)");
124
+ const bodyClass = computed(() => ({ sm: "text-xs", md: "text-sm", lg: "text-base" })[props.textSize] || "text-sm");
125
+ const SM = { none: "0", xs: "8px", sm: "24px", md: "48px", lg: "80px", xl: "128px", "-xs": "-8px", "-sm": "-24px", "-md": "-48px", "-lg": "-80px", "-xl": "-128px" };
126
+ const spacingStyle = computed(() => {
127
+ const s = {};
128
+ if (props.paddingTop && SM[props.paddingTop]) s.paddingTop = SM[props.paddingTop];
129
+ if (props.paddingBottom && SM[props.paddingBottom]) s.paddingBottom = SM[props.paddingBottom];
130
+ if (props.marginTop && SM[props.marginTop]) s.marginTop = SM[props.marginTop];
131
+ if (props.marginBottom && SM[props.marginBottom]) s.marginBottom = SM[props.marginBottom];
132
+ if (props.paddingX && SM[props.paddingX]) {
133
+ const v = SM[props.paddingX];
134
+ if (v.startsWith("-")) {
135
+ s.marginLeft = v;
136
+ s.marginRight = v;
137
+ } else {
138
+ s.paddingLeft = v;
139
+ s.paddingRight = v;
140
+ }
141
+ }
142
+ return s;
143
+ });
144
+ </script>
145
+
146
+ <style scoped>
147
+ .sv-out{opacity:0;transform:translateY(16px)}.sv-in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s ease}
148
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,124 @@
1
+ <template>
2
+ <section ref="sectionEl" :class="[sectionPad, isVisible ? 'sv-in' : 'sv-out']" :dir="textDirection" :style="[{ backgroundColor: bgColor, color: textColor }, spacingStyle]">
3
+ <div :class="[containerClass]">
4
+
5
+ <!-- Header -->
6
+ <div v-if="title || description" class="border-b pb-6 mb-0" :style="{ borderColor: ca(textColor,'15') }">
7
+ <span v-if="badge" class="block text-[9px] font-black uppercase tracking-[0.5em] mb-3" :style="{ opacity: 0.35 }">{{ badge }}</span>
8
+ <component :is="titleTag" class="font-black uppercase leading-[0.88]" :style="{ fontSize: titleFontSize, letterSpacing: '-0.03em' }">{{ title }}</component>
9
+ <p v-if="description" :class="[bodyClass, 'leading-relaxed mt-3 max-w-xl']" :style="{ opacity: 0.55 }">{{ description }}</p>
10
+ </div>
11
+
12
+ <!-- STEPS (vertical) -->
13
+ <div v-if="layout === 'steps'" class="divide-y" :style="{ borderColor: ca(textColor,'10') }">
14
+ <div v-for="(step, i) in items" :key="i" class="grid grid-cols-[3rem_1fr] gap-8 py-8 items-start" :style="{ borderColor: ca(textColor,'10') }">
15
+ <div class="font-black tabular-nums leading-none pt-1" :style="{ fontSize: '2.5rem', opacity: 0.1, letterSpacing: '-0.05em' }">{{ String(i+1).padStart(2,'0') }}</div>
16
+ <div>
17
+ <component :is="itemTag" class="font-black uppercase tracking-tight mb-2 leading-tight">{{ step.title }}</component>
18
+ <p class="text-sm leading-relaxed" :style="{ opacity: 0.55 }">{{ step.description }}</p>
19
+ </div>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- HORIZONTAL -->
24
+ <div v-else-if="layout === 'horizontal'" class="grid grid-cols-1 md:grid-cols-4 divide-y md:divide-y-0 md:divide-x border-t border-b" :style="{ borderColor: ca(textColor,'12') }">
25
+ <div v-for="(step, i) in items" :key="i" class="p-8" :style="{ borderColor: ca(textColor,'12') }">
26
+ <div class="font-black tabular-nums mb-6" :style="{ fontSize: '2.5rem', opacity: 0.1, letterSpacing: '-0.05em' }">{{ String(i+1).padStart(2,'0') }}</div>
27
+ <component :is="itemTag" class="text-sm font-black uppercase tracking-tight mb-2 leading-tight">{{ step.title }}</component>
28
+ <p class="text-xs leading-relaxed" :style="{ opacity: 0.5 }">{{ step.description }}</p>
29
+ </div>
30
+ </div>
31
+
32
+ <!-- CARDS -->
33
+ <div v-else-if="layout === 'cards'" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 border divide-x divide-y" :style="{ borderColor: ca(textColor,'12') }">
34
+ <div v-for="(step, i) in items" :key="i" class="p-8" :style="{ borderColor: ca(textColor,'12') }">
35
+ <div class="font-black tabular-nums mb-6" :style="{ fontSize: '2rem', opacity: 0.1, letterSpacing: '-0.05em' }">{{ String(i+1).padStart(2,'0') }}</div>
36
+ <component :is="itemTag" class="font-black uppercase tracking-tight mb-3 leading-tight">{{ step.title }}</component>
37
+ <p class="text-sm leading-relaxed" :style="{ opacity: 0.55 }">{{ step.description }}</p>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- TIMELINE (default) -->
42
+ <div v-else class="divide-y" :style="{ borderColor: ca(textColor,'10') }">
43
+ <div v-for="(step, i) in items" :key="i" class="grid md:grid-cols-[8rem_1fr] gap-8 py-8 items-start" :style="{ borderColor: ca(textColor,'10') }">
44
+ <div class="font-black tabular-nums text-right hidden md:block" :style="{ fontSize: '3rem', opacity: 0.08, letterSpacing: '-0.05em' }">{{ String(i+1).padStart(2,'0') }}</div>
45
+ <div class="border-l pl-8" :style="{ borderColor: ca(textColor,'15') }">
46
+ <component :is="itemTag" class="font-black uppercase tracking-tight mb-2 leading-tight">{{ step.title }}</component>
47
+ <p class="text-sm leading-relaxed" :style="{ opacity: 0.55 }">{{ step.description }}</p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ </div>
53
+ </section>
54
+ </template>
55
+
56
+ <script setup>
57
+ const ca = (c, h) => {
58
+ const m = c?.match(/rgba\((var\([^)]+\)),\s*[\d.]+\)/);
59
+ return m ? `rgba(${m[1]}, ${Math.round(parseInt(h, 16) / 255 * 1e3) / 1e3})` : (c || "") + h;
60
+ };
61
+ import { ref, computed, onMounted } from "vue";
62
+ const props = defineProps({
63
+ layout: { type: String, default: "steps" },
64
+ badge: { type: String, default: "" },
65
+ title: { type: String, default: "" },
66
+ titleTag: { type: String, default: "h2" },
67
+ itemTag: { type: String, default: "h3" },
68
+ description: { type: String, default: "" },
69
+ items: { type: Array, default: () => [] },
70
+ bgColor: { type: String, default: "rgba(var(--body-bg), 1)" },
71
+ textColor: { type: String, default: "rgba(var(--text-color), 1)" },
72
+ spacing: { type: String, default: "normal" },
73
+ radius: { type: String, default: "sharp" },
74
+ textDirection: { type: String, default: "ltr" },
75
+ contentWidth: { type: String, default: "normal" },
76
+ textSize: { type: String, default: "md" },
77
+ paddingTop: { type: String, default: "" },
78
+ paddingBottom: { type: String, default: "" },
79
+ marginTop: { type: String, default: "" },
80
+ marginBottom: { type: String, default: "" },
81
+ paddingX: { type: String, default: "" }
82
+ });
83
+ const sectionEl = ref(null);
84
+ const isVisible = ref(false);
85
+ onMounted(() => {
86
+ const obs = new IntersectionObserver(([e]) => {
87
+ if (e.isIntersecting) {
88
+ isVisible.value = true;
89
+ obs.disconnect();
90
+ }
91
+ }, { threshold: 0.06 });
92
+ if (sectionEl.value) obs.observe(sectionEl.value);
93
+ setTimeout(() => {
94
+ if (!isVisible.value) isVisible.value = true;
95
+ }, 300);
96
+ });
97
+ const sectionPad = computed(() => ({ compact: "py-8 px-4 md:px-8", normal: "py-14 px-8 md:px-16", spacious: "py-24 px-8 md:px-16" })[props.spacing] || "py-14 px-8 md:px-16");
98
+ const containerClass = computed(() => ({ narrow: "max-w-3xl mx-auto", normal: "max-w-6xl mx-auto", wide: "max-w-7xl mx-auto", full: "max-w-full" })[props.contentWidth] || "max-w-6xl mx-auto");
99
+ const titleFontSize = computed(() => ({ sm: "clamp(1rem,1.5vw,1.3rem)", md: "clamp(1.5rem,2.5vw,2rem)", lg: "clamp(2.5rem,6vw,5rem)" })[props.textSize] || "clamp(1.5rem,2.5vw,2rem)");
100
+ const bodyClass = computed(() => ({ sm: "text-xs", md: "text-sm", lg: "text-base" })[props.textSize] || "text-sm");
101
+ const SM = { none: "0", xs: "8px", sm: "24px", md: "48px", lg: "80px", xl: "128px", "-xs": "-8px", "-sm": "-24px", "-md": "-48px", "-lg": "-80px", "-xl": "-128px" };
102
+ const spacingStyle = computed(() => {
103
+ const s = {};
104
+ if (props.paddingTop && SM[props.paddingTop]) s.paddingTop = SM[props.paddingTop];
105
+ if (props.paddingBottom && SM[props.paddingBottom]) s.paddingBottom = SM[props.paddingBottom];
106
+ if (props.marginTop && SM[props.marginTop]) s.marginTop = SM[props.marginTop];
107
+ if (props.marginBottom && SM[props.marginBottom]) s.marginBottom = SM[props.marginBottom];
108
+ if (props.paddingX && SM[props.paddingX]) {
109
+ const v = SM[props.paddingX];
110
+ if (v.startsWith("-")) {
111
+ s.marginLeft = v;
112
+ s.marginRight = v;
113
+ } else {
114
+ s.paddingLeft = v;
115
+ s.paddingRight = v;
116
+ }
117
+ }
118
+ return s;
119
+ });
120
+ </script>
121
+
122
+ <style scoped>
123
+ .sv-out{opacity:0;transform:translateY(16px)}.sv-in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s ease}
124
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <section ref="sectionEl" :class="[sectionPad, isVisible ? 'sv-in' : 'sv-out']" :dir="textDirection" :style="[{ backgroundColor: bgColor, color: textColor }, spacingStyle]">
3
+
4
+ <!-- CENTERED -->
5
+ <div v-if="layout === 'centered'" :class="[containerClass]">
6
+ <div class="border-t border-b py-14" :style="{ borderColor: ca(textColor,'20') }">
7
+ <div class="grid md:grid-cols-[2rem_1fr] gap-8 items-start">
8
+ <div class="font-black leading-none hidden md:block" style="font-size:4rem; opacity:0.12">"</div>
9
+ <div>
10
+ <blockquote class="font-black uppercase leading-[0.88] mb-8" :style="{ fontSize: quoteFontSize, letterSpacing: '-0.03em' }">{{ quote }}</blockquote>
11
+ <div class="flex items-center gap-4">
12
+ <div class="w-8 h-px" :style="{ backgroundColor: textColor }"></div>
13
+ <img v-if="avatar" :src="avatar" class="w-8 h-8 object-cover" style="border-radius:0" />
14
+ <div>
15
+ <div v-if="author" class="text-xs font-black uppercase tracking-wider">{{ author }}</div>
16
+ <div v-if="role" class="text-[11px]" :style="{ opacity: 0.45 }">{{ role }}</div>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- LEFT -->
25
+ <div v-else-if="layout === 'left'" :class="[containerClass, 'grid md:grid-cols-[1px_1fr] gap-8']">
26
+ <div class="hidden md:block" :style="{ backgroundColor: textColor, opacity: 0.15 }"></div>
27
+ <div class="pl-0 md:pl-8">
28
+ <blockquote class="font-black uppercase leading-[0.88] mb-8" :style="{ fontSize: quoteFontSize, letterSpacing: '-0.03em' }">{{ quote }}</blockquote>
29
+ <div class="flex items-center gap-4">
30
+ <img v-if="avatar" :src="avatar" class="w-8 h-8 object-cover" style="border-radius:0" />
31
+ <div v-else-if="author" class="w-8 h-8 flex items-center justify-center text-xs font-black" :style="{ backgroundColor: ca(textColor,'15') }">{{ author[0] }}</div>
32
+ <div>
33
+ <div v-if="author" class="text-xs font-black uppercase tracking-wider">{{ author }}</div>
34
+ <div v-if="role" class="text-[11px]" :style="{ opacity: 0.45 }">{{ role }}</div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- LARGE (default) -->
41
+ <div v-else :class="[containerClass]">
42
+ <div class="border-t border-b py-12 md:py-16" :style="{ borderColor: ca(textColor,'20') }">
43
+ <blockquote class="font-black uppercase leading-[0.88] mb-10" :style="{ fontSize: quoteFontSize, letterSpacing: '-0.03em' }">{{ quote }}</blockquote>
44
+ <div class="flex items-center gap-4">
45
+ <div class="w-10 h-px" :style="{ backgroundColor: textColor }"></div>
46
+ <img v-if="avatar" :src="avatar" class="w-8 h-8 object-cover" style="border-radius:0" />
47
+ <div>
48
+ <span v-if="author" class="text-xs font-black uppercase tracking-wider">{{ author }}</span>
49
+ <span v-if="role" class="text-[11px] ml-3" :style="{ opacity: 0.45 }">{{ role }}</span>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </div>
54
+
55
+ <!-- Placeholder -->
56
+ <div v-if="!quote" :class="[containerClass, 'border-t border-b py-10']" :style="{ borderColor: ca(textColor,'12') }">
57
+ <div class="h-6 mb-4" :style="{ backgroundColor: ca(textColor,'08'), width: '80%' }"></div>
58
+ <div class="h-6 mb-8" :style="{ backgroundColor: ca(textColor,'08'), width: '60%' }"></div>
59
+ <div class="h-3" :style="{ backgroundColor: ca(textColor,'12'), width: '20%' }"></div>
60
+ </div>
61
+
62
+ </section>
63
+ </template>
64
+
65
+ <script setup>
66
+ const ca = (c, h) => {
67
+ const m = c?.match(/rgba\((var\([^)]+\)),\s*[\d.]+\)/);
68
+ return m ? `rgba(${m[1]}, ${Math.round(parseInt(h, 16) / 255 * 1e3) / 1e3})` : (c || "") + h;
69
+ };
70
+ import { ref, computed, onMounted } from "vue";
71
+ const props = defineProps({
72
+ layout: { type: String, default: "centered" },
73
+ quote: { type: String, default: "" },
74
+ author: { type: String, default: "" },
75
+ role: { type: String, default: "" },
76
+ avatar: { type: String, default: "" },
77
+ bgColor: { type: String, default: "rgba(var(--body-bg), 1)" },
78
+ textColor: { type: String, default: "rgba(var(--text-color), 1)" },
79
+ spacing: { type: String, default: "normal" },
80
+ radius: { type: String, default: "sharp" },
81
+ textDirection: { type: String, default: "ltr" },
82
+ contentWidth: { type: String, default: "narrow" },
83
+ textSize: { type: String, default: "md" },
84
+ paddingTop: { type: String, default: "" },
85
+ paddingBottom: { type: String, default: "" },
86
+ marginTop: { type: String, default: "" },
87
+ marginBottom: { type: String, default: "" },
88
+ paddingX: { type: String, default: "" }
89
+ });
90
+ const sectionEl = ref(null);
91
+ const isVisible = ref(false);
92
+ onMounted(() => {
93
+ const obs = new IntersectionObserver(([e]) => {
94
+ if (e.isIntersecting) {
95
+ isVisible.value = true;
96
+ obs.disconnect();
97
+ }
98
+ }, { threshold: 0.06 });
99
+ if (sectionEl.value) obs.observe(sectionEl.value);
100
+ setTimeout(() => {
101
+ if (!isVisible.value) isVisible.value = true;
102
+ }, 300);
103
+ });
104
+ const sectionPad = computed(() => ({ compact: "py-8 px-4 md:px-8", normal: "py-14 px-8 md:px-16", spacious: "py-24 px-8 md:px-16" })[props.spacing] || "py-14 px-8 md:px-16");
105
+ const containerClass = computed(() => ({ narrow: "max-w-3xl mx-auto", normal: "max-w-6xl mx-auto", wide: "max-w-7xl mx-auto", full: "max-w-full" })[props.contentWidth] || "max-w-3xl mx-auto");
106
+ const quoteFontSize = computed(() => ({ sm: "clamp(1.2rem,2vw,1.8rem)", md: "clamp(1.5rem,3vw,2.5rem)", lg: "clamp(2rem,5vw,4rem)" })[props.textSize] || "clamp(1.5rem,3vw,2.5rem)");
107
+ const SM = { none: "0", xs: "8px", sm: "24px", md: "48px", lg: "80px", xl: "128px", "-xs": "-8px", "-sm": "-24px", "-md": "-48px", "-lg": "-80px", "-xl": "-128px" };
108
+ const spacingStyle = computed(() => {
109
+ const s = {};
110
+ if (props.paddingTop && SM[props.paddingTop]) s.paddingTop = SM[props.paddingTop];
111
+ if (props.paddingBottom && SM[props.paddingBottom]) s.paddingBottom = SM[props.paddingBottom];
112
+ if (props.marginTop && SM[props.marginTop]) s.marginTop = SM[props.marginTop];
113
+ if (props.marginBottom && SM[props.marginBottom]) s.marginBottom = SM[props.marginBottom];
114
+ if (props.paddingX && SM[props.paddingX]) {
115
+ const v = SM[props.paddingX];
116
+ if (v.startsWith("-")) {
117
+ s.marginLeft = v;
118
+ s.marginRight = v;
119
+ } else {
120
+ s.paddingLeft = v;
121
+ s.paddingRight = v;
122
+ }
123
+ }
124
+ return s;
125
+ });
126
+ </script>
127
+
128
+ <style scoped>
129
+ .sv-out{opacity:0;transform:translateY(16px)}.sv-in{opacity:1;transform:none;transition:opacity .6s ease,transform .6s ease}
130
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof __VLS_export;
2
+ export default _default;
3
+ declare const __VLS_export: any;