@miragon/slidev-toolkit 1.0.0

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.
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * closing — animierte Abschlussseite (Gegenstück zu `cover`).
4
+ *
5
+ * Schließt das Deck mit demselben Brand-Moment, mit dem `cover` es öffnet:
6
+ * Mesh-Shader als Vollbild-Hintergrund (z0), Content darüber (z2). Trägt die
7
+ * Schlussaussage („Thank you", Call-to-Action) und Kontaktangaben.
8
+ *
9
+ * Slots: default = Schlussaussage (h1) + optionaler Satz (p) aus Markdown.
10
+ * Frontmatter-Props:
11
+ * eyebrow — kleine Überzeile (z. B. "Let's talk")
12
+ * contact — Kontaktzeile rechts im Footer (z. B. "name@miragon.io")
13
+ * footer — Footer-Text links neben dem Logo (optional)
14
+ */
15
+ defineProps<{
16
+ eyebrow?: string
17
+ contact?: string
18
+ footer?: string
19
+ }>()
20
+
21
+ // Brand-Asset reist mit dem Theme: statischer Import, von Vite gebundlet und
22
+ // base-aware. Das Deck erbt das Logo aus dem Theme, ohne es selbst zu duplizieren.
23
+ import logoSrc from '../assets/logo.svg'
24
+ function onLogoError(e: Event) {
25
+ ;(e.target as HTMLImageElement).style.display = 'none'
26
+ }
27
+ </script>
28
+
29
+ <template>
30
+ <div class="closing-layout">
31
+ <BrandMeshBackground />
32
+
33
+ <div class="closing-content">
34
+ <div v-if="eyebrow" class="closing-eyebrow">{{ eyebrow }}</div>
35
+ <div class="closing-body">
36
+ <slot />
37
+ </div>
38
+ </div>
39
+
40
+ <div class="closing-footer">
41
+ <img :src="logoSrc" alt="Miragon" class="closing-logo" @error="onLogoError" />
42
+ <span v-if="contact" class="closing-contact">{{ contact }}</span>
43
+ <span v-else-if="footer" class="closing-footer-text">{{ footer }}</span>
44
+ </div>
45
+ </div>
46
+ </template>
47
+
48
+ <style scoped>
49
+ .closing-layout {
50
+ position: relative;
51
+ width: 100%;
52
+ height: 100%;
53
+ overflow: hidden;
54
+ color: var(--miragon-white);
55
+ /* Brand-Gradient als unterste Ebene: greift, falls BrandMeshBackground (WebGL) gar nicht
56
+ mountet (PDF/headless ohne GPU) — so nie Weiß, immer der Miragon-Verlauf.
57
+ BrandMeshBackground liegt mit position:absolute darüber und verdeckt ihn live. */
58
+ background: linear-gradient(125deg, #335DE5 0%, #1a1a4e 35%, #0d0d2b 55%, #00C853 100%);
59
+ }
60
+
61
+ .closing-content {
62
+ position: relative;
63
+ z-index: 2;
64
+ height: 100%;
65
+ display: flex;
66
+ flex-direction: column;
67
+ justify-content: center;
68
+ padding: 0 4rem;
69
+ max-width: 56rem;
70
+ }
71
+
72
+ .closing-eyebrow {
73
+ font-size: 0.95rem;
74
+ font-weight: 600;
75
+ letter-spacing: 0.18em;
76
+ text-transform: uppercase;
77
+ color: var(--miragon-green);
78
+ margin-bottom: 1.25rem;
79
+ }
80
+
81
+ /* Schlussaussage: weiß, kursiv, sehr fett — spiegelt cover. */
82
+ .closing-body :deep(h1) {
83
+ font-size: 3.6rem;
84
+ line-height: 1.05;
85
+ font-weight: 900;
86
+ font-style: italic;
87
+ color: var(--miragon-white);
88
+ margin: 0;
89
+ text-shadow: 0 2px 24px rgba(13, 13, 43, 0.35);
90
+ }
91
+
92
+ .closing-body :deep(p) {
93
+ font-size: 1.4rem;
94
+ font-weight: 400;
95
+ color: rgba(255, 255, 255, 0.88);
96
+ margin-top: 1.25rem;
97
+ max-width: 42rem;
98
+ }
99
+
100
+ .closing-body :deep(a) {
101
+ color: var(--miragon-green);
102
+ text-decoration: none;
103
+ }
104
+
105
+ .closing-footer {
106
+ position: absolute;
107
+ z-index: 2;
108
+ bottom: 2rem;
109
+ left: 4rem;
110
+ right: 4rem;
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: space-between;
114
+ }
115
+
116
+ .closing-logo { height: 1.75rem; opacity: 0.95; }
117
+
118
+ .closing-contact {
119
+ font-size: 0.95rem;
120
+ font-weight: 600;
121
+ color: var(--miragon-green);
122
+ }
123
+ .closing-footer-text {
124
+ font-size: 0.85rem;
125
+ color: rgba(255, 255, 255, 0.7);
126
+ }
127
+ </style>
@@ -0,0 +1,214 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * compare — Gegenüberstellung zweier Seiten (STATISCHE Welt, kein Mesh-Shader).
4
+ *
5
+ * Zwei gleichwertige Panels nebeneinander: vorher/nachher, manuell/automatisiert,
6
+ * Option A/B. Die Panels sind WEISS (white-card rule, siehe slides Skill) —
7
+ * die semantische Unterscheidung (links = Problem, rechts = Lösung) trägt die
8
+ * Panel-Titel-Farbe (blue-deep vs. green-deep) sowie der Bullet-Marker (blauer
9
+ * vs. grüner Gradient). Folgt der hero/person-Bildsprache: heller Grund, klarer
10
+ * Akzent-Balken, große Typografie.
11
+ *
12
+ * Named Slots (in Markdown mit `::left::` / `::right::` befüllen):
13
+ * left — Inhalt der linken Spalte (Bullets/Text)
14
+ * right — Inhalt der rechten Spalte
15
+ * default — optionaler Einleitungssatz unter dem Titel
16
+ *
17
+ * Frontmatter-Props:
18
+ * title — Slide-Titel (optional)
19
+ * eyebrow — kleine Überzeile (optional)
20
+ * leftTitle — Kopf des linken Panels (default "Before")
21
+ * rightTitle — Kopf des rechten Panels (default "After")
22
+ * accent — "blue" | "green" | "mixed" — Balken-/Eyebrow-Akzent (default blue)
23
+ */
24
+ import { computed } from 'vue'
25
+
26
+ const props = withDefaults(
27
+ defineProps<{
28
+ eyebrow?: string
29
+ leftTitle?: string
30
+ rightTitle?: string
31
+ accent?: 'blue' | 'green' | 'mixed'
32
+ // Slidev reicht das volle Frontmatter-Objekt als `frontmatter`-Prop durch.
33
+ frontmatter?: Record<string, unknown>
34
+ }>(),
35
+ { accent: 'blue', leftTitle: 'Before', rightTitle: 'After' },
36
+ )
37
+
38
+ // `title` ist ein RESERVIERTES Slidev-Frontmatter-Feld (TOC/Nav) und wird NICHT
39
+ // als eigener Prop durchgereicht (FRONTMATTER_FIELDS). Daher aus dem
40
+ // mitgelieferten `frontmatter`-Objekt lesen, damit `title:` in slides.md wie
41
+ // dokumentiert funktioniert.
42
+ const title = computed(() => props.frontmatter?.title as string | undefined)
43
+
44
+ // Token reaktiv (siehe hero.vue) — alle Werte aus theme.css, keine Hex.
45
+ const gradientVar = computed(() => `var(--miragon-gradient-${props.accent})`)
46
+ const accentVar = computed(() =>
47
+ props.accent === 'green' ? 'var(--miragon-green-deep)' : 'var(--miragon-blue)',
48
+ )
49
+
50
+ // Panel-Töne folgen dem accent: "mixed" trägt die Problem/Lösung-Semantik
51
+ // (links blau, rechts grün); "blue"/"green" macht beide Seiten einfarbig.
52
+ // Default bleibt mixed-artig (blau/grün), damit bestehende Decks unverändert sind.
53
+ const twoTone = computed(() => props.accent !== 'blue' && props.accent !== 'green')
54
+ const mono = computed(() => (props.accent === 'green' ? 'green-deep' : 'blue-deep'))
55
+ const leftTone = computed(() => (twoTone.value ? 'var(--miragon-blue-deep)' : `var(--miragon-${mono.value})`))
56
+ const rightTone = computed(() => (twoTone.value ? 'var(--miragon-green-deep)' : `var(--miragon-${mono.value})`))
57
+ const leftMark = computed(() => (twoTone.value || props.accent === 'blue' ? 'var(--miragon-gradient-blue)' : 'var(--miragon-gradient-green)'))
58
+ const rightMark = computed(() => (twoTone.value || props.accent === 'green' ? 'var(--miragon-gradient-green)' : 'var(--miragon-gradient-blue)'))
59
+ </script>
60
+
61
+ <template>
62
+ <div
63
+ class="compare-layout"
64
+ :style="{ '--cmp-grad': gradientVar, '--cmp-accent': accentVar, '--cmp-left': leftTone, '--cmp-right': rightTone, '--cmp-left-mark': leftMark, '--cmp-right-mark': rightMark }"
65
+ >
66
+ <div class="compare-inner">
67
+ <header v-if="title || eyebrow" class="compare-head">
68
+ <span class="compare-bar" aria-hidden="true"></span>
69
+ <div v-if="eyebrow" class="compare-eyebrow">{{ eyebrow }}</div>
70
+ <h2 v-if="title" class="compare-title">{{ title }}</h2>
71
+ <div class="compare-lead"><slot /></div>
72
+ </header>
73
+
74
+ <div class="compare-grid">
75
+ <section class="compare-panel panel-left">
76
+ <h3 class="panel-title">{{ leftTitle }}</h3>
77
+ <div class="panel-body"><slot name="left" /></div>
78
+ </section>
79
+
80
+ <section class="compare-panel panel-right">
81
+ <h3 class="panel-title">{{ rightTitle }}</h3>
82
+ <div class="panel-body"><slot name="right" /></div>
83
+ </section>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </template>
88
+
89
+ <style scoped>
90
+ .compare-layout {
91
+ position: relative;
92
+ width: 100%;
93
+ height: 100%;
94
+ overflow: hidden;
95
+ background: var(--miragon-gray-bg);
96
+ color: var(--miragon-text-primary);
97
+ display: flex;
98
+ align-items: stretch;
99
+ }
100
+
101
+ .compare-inner {
102
+ position: relative;
103
+ z-index: 1;
104
+ width: 100%;
105
+ max-width: 70rem;
106
+ margin: 0 auto;
107
+ padding: 3.5rem 5rem;
108
+ display: flex;
109
+ flex-direction: column;
110
+ }
111
+
112
+ /* --- Kopf ----------------------------------------------------------------- */
113
+ .compare-head {
114
+ flex: 0 0 auto;
115
+ margin-bottom: 2rem;
116
+ }
117
+ .compare-bar {
118
+ display: block;
119
+ width: 3.5rem;
120
+ height: 0.35rem;
121
+ border-radius: 999px;
122
+ background: var(--cmp-grad);
123
+ margin-bottom: 1.25rem;
124
+ }
125
+ .compare-eyebrow {
126
+ font-size: 0.9rem;
127
+ font-weight: 600;
128
+ letter-spacing: 0.18em;
129
+ text-transform: uppercase;
130
+ color: var(--cmp-accent);
131
+ margin-bottom: 0.75rem;
132
+ }
133
+ .compare-title {
134
+ font-size: clamp(1.9rem, 3.2vw, 2.7rem);
135
+ line-height: 1.1;
136
+ font-weight: 800;
137
+ letter-spacing: -0.02em;
138
+ margin: 0;
139
+ }
140
+ .compare-lead :deep(p) {
141
+ font-size: 1.15rem;
142
+ line-height: 1.55;
143
+ color: var(--miragon-text-secondary);
144
+ margin: 1rem 0 0;
145
+ max-width: 46rem;
146
+ }
147
+
148
+ /* --- Zwei-Panel-Raster ---------------------------------------------------- */
149
+ .compare-grid {
150
+ flex: 1 1 auto;
151
+ min-height: 0;
152
+ display: grid;
153
+ grid-template-columns: 1fr 1fr;
154
+ gap: 2rem;
155
+ }
156
+ /* Weisse Panels (white-card rule §6) — die Semantik trägt der Titel + Marker. */
157
+ .compare-panel {
158
+ background: var(--miragon-white);
159
+ border: 1px solid #E5E7EB;
160
+ border-radius: 1.1rem;
161
+ padding: 2rem 2rem 1.5rem;
162
+ box-shadow: 0 8px 20px rgba(51, 93, 229, 0.08);
163
+ }
164
+ .panel-title {
165
+ font-size: 1.35rem;
166
+ font-weight: 800;
167
+ letter-spacing: -0.01em;
168
+ margin: 0 0 1.25rem;
169
+ }
170
+ /* Titel-Farbe trägt die semantische Unterscheidung links=Problem / rechts=Lösung
171
+ (bei accent "mixed"); bei accent "blue"/"green" sind beide Seiten einfarbig. */
172
+ .panel-left .panel-title { color: var(--cmp-left); }
173
+ .panel-right .panel-title { color: var(--cmp-right); }
174
+
175
+ /* Panel-Body: Bullets mit Akzent-Marker passend zur Panel-Seite. */
176
+ .panel-body :deep(ul) {
177
+ list-style: none;
178
+ padding: 0;
179
+ margin: 0;
180
+ }
181
+ .panel-body :deep(li) {
182
+ position: relative;
183
+ font-size: 1.1rem;
184
+ line-height: 1.45;
185
+ color: var(--miragon-text-secondary);
186
+ padding-left: 1.7rem;
187
+ margin: 0 0 0.95rem;
188
+ }
189
+ .panel-body :deep(li)::before {
190
+ content: '';
191
+ position: absolute;
192
+ left: 0;
193
+ top: 0.55em;
194
+ width: 0.6rem;
195
+ height: 0.6rem;
196
+ border-radius: 0.18rem;
197
+ }
198
+ .panel-left .panel-body :deep(li)::before {
199
+ background: var(--cmp-left-mark);
200
+ }
201
+ .panel-right .panel-body :deep(li)::before {
202
+ background: var(--cmp-right-mark);
203
+ }
204
+ .panel-body :deep(p) {
205
+ font-size: 1.1rem;
206
+ line-height: 1.5;
207
+ color: var(--miragon-text-secondary);
208
+ margin: 0 0 0.9rem;
209
+ }
210
+ .panel-body :deep(strong) {
211
+ font-weight: 700;
212
+ color: var(--miragon-text-primary);
213
+ }
214
+ </style>
@@ -0,0 +1,182 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * content-image — content slide with a side image (STATIC world, no shader).
4
+ *
5
+ * Two-column layout: an image on one side, the content (heading + body) on
6
+ * the other. The same pattern as a typical "We are X, but who are you?"
7
+ * slide: a visual anchor + a short narrative or list. Use whenever you want
8
+ * a visual to accompany prose.
9
+ *
10
+ * Frontmatter props:
11
+ * title — slide title (h2-level)
12
+ * eyebrow — uppercase kicker
13
+ * accent — "blue" | "green" | "mixed" (default blue)
14
+ * image — served URL path to the image (e.g. /resources/02-content/team.jpg)
15
+ * imageAlt — alt text for accessibility (default "")
16
+ * side — "left" | "right" (default "left") — which side the image is on
17
+ * Slot:
18
+ * default — free Markdown body (lead, bullets, numbered steps, …)
19
+ */
20
+ import { computed } from 'vue'
21
+
22
+ const props = withDefaults(
23
+ defineProps<{
24
+ eyebrow?: string
25
+ accent?: 'blue' | 'green' | 'mixed'
26
+ image?: string
27
+ imageAlt?: string
28
+ side?: 'left' | 'right'
29
+ frontmatter?: Record<string, unknown>
30
+ }>(),
31
+ { accent: 'blue', side: 'left', imageAlt: '' },
32
+ )
33
+
34
+ const title = computed(() => props.frontmatter?.title as string | undefined)
35
+ const gradientVar = computed(() => `var(--miragon-gradient-${props.accent})`)
36
+ const accentVar = computed(() =>
37
+ props.accent === 'green' ? 'var(--miragon-green-deep)' : 'var(--miragon-blue)',
38
+ )
39
+
40
+ // Base-Pfad respektieren (GitHub Pages baut unter /<repo>/). Runtime-Strings
41
+ // werden von Vite NICHT umgeschrieben — daher manuell mit BASE_URL auflösen.
42
+ // Gleiche Logik wie in person.vue.
43
+ function withBase(path?: string) {
44
+ if (!path) return path
45
+ if (/^https?:\/\//.test(path)) return path
46
+ return import.meta.env.BASE_URL.replace(/\/$/, '') + '/' + path.replace(/^\//, '')
47
+ }
48
+ const imageSrc = computed(() => withBase(props.image))
49
+ </script>
50
+
51
+ <template>
52
+ <div
53
+ class="ci-layout"
54
+ :class="[`image-${side}`]"
55
+ :style="{ '--ci-grad': gradientVar, '--ci-accent': accentVar }"
56
+ >
57
+ <div class="ci-inner">
58
+ <div class="ci-image">
59
+ <img v-if="image" :src="imageSrc" :alt="imageAlt" />
60
+ </div>
61
+
62
+ <div class="ci-content">
63
+ <span class="ci-bar" aria-hidden="true"></span>
64
+ <div v-if="eyebrow" class="ci-eyebrow">{{ eyebrow }}</div>
65
+ <h2 v-if="title" class="ci-title">{{ title }}</h2>
66
+ <div class="ci-body"><slot /></div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </template>
71
+
72
+ <style scoped>
73
+ .ci-layout {
74
+ position: relative;
75
+ width: 100%;
76
+ height: 100%;
77
+ overflow: hidden;
78
+ background: var(--miragon-gray-bg);
79
+ color: var(--miragon-text-primary);
80
+ display: flex;
81
+ align-items: stretch;
82
+ }
83
+
84
+ .ci-inner {
85
+ position: relative;
86
+ z-index: 1;
87
+ width: 100%;
88
+ max-width: 74rem;
89
+ margin: 0 auto;
90
+ padding: 3rem 4rem;
91
+ display: grid;
92
+ grid-template-columns: 1fr 1.1fr;
93
+ gap: 3rem;
94
+ align-items: center;
95
+ }
96
+ .image-right .ci-inner {
97
+ grid-template-columns: 1.1fr 1fr;
98
+ }
99
+ .image-right .ci-image {
100
+ order: 2;
101
+ }
102
+ .image-right .ci-content {
103
+ order: 1;
104
+ }
105
+
106
+ .ci-image {
107
+ display: flex;
108
+ justify-content: center;
109
+ align-items: center;
110
+ }
111
+ .ci-image img {
112
+ width: 100%;
113
+ max-height: 26rem;
114
+ object-fit: cover;
115
+ border-radius: 1.1rem;
116
+ box-shadow: 0 12px 30px rgba(51, 93, 229, 0.18);
117
+ background: var(--miragon-white);
118
+ }
119
+
120
+ .ci-content {
121
+ display: flex;
122
+ flex-direction: column;
123
+ }
124
+ .ci-bar {
125
+ display: block;
126
+ width: 3.5rem;
127
+ height: 0.35rem;
128
+ border-radius: 999px;
129
+ background: var(--ci-grad);
130
+ margin-bottom: 1rem;
131
+ }
132
+ .ci-eyebrow {
133
+ font-size: 0.9rem;
134
+ font-weight: 600;
135
+ letter-spacing: 0.18em;
136
+ text-transform: uppercase;
137
+ color: var(--ci-accent);
138
+ margin-bottom: 0.6rem;
139
+ }
140
+ .ci-title {
141
+ font-size: clamp(1.75rem, 2.6vw, 2rem);
142
+ line-height: 1.2;
143
+ font-weight: 800;
144
+ letter-spacing: -0.02em;
145
+ margin: 0 0 1.25rem;
146
+ }
147
+ .ci-body :deep(p) {
148
+ font-size: 1.05rem;
149
+ line-height: 1.55;
150
+ color: var(--miragon-text-secondary);
151
+ margin: 0 0 0.85rem;
152
+ }
153
+ .ci-body :deep(strong) {
154
+ font-weight: 700;
155
+ color: var(--miragon-text-primary);
156
+ }
157
+
158
+ /* Bullets with accent marker (same pattern as the `content` layout). */
159
+ .ci-body :deep(ul) {
160
+ list-style: none;
161
+ padding: 0;
162
+ margin: 0;
163
+ }
164
+ .ci-body :deep(li) {
165
+ position: relative;
166
+ font-size: 1.05rem;
167
+ line-height: 1.5;
168
+ color: var(--miragon-text-secondary);
169
+ padding-left: 1.6rem;
170
+ margin: 0 0 0.6rem;
171
+ }
172
+ .ci-body :deep(li)::before {
173
+ content: '';
174
+ position: absolute;
175
+ left: 0;
176
+ top: 0.55em;
177
+ width: 0.55rem;
178
+ height: 0.55rem;
179
+ border-radius: 0.18rem;
180
+ background: var(--ci-grad);
181
+ }
182
+ </style>
@@ -0,0 +1,186 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * content — Workhorse-Inhaltsslide (STATISCHE Welt, KEIN Mesh-Shader).
4
+ *
5
+ * Der Alltags-Archetyp: ein Titel oben, darunter freier Body (Bullets,
6
+ * <v-clicks>, kleine Karten). Folgt der hero/person-Bildsprache: heller Grund,
7
+ * großzügige Typografie, ein einziger sparsamer Gradient-Akzent (Balken +
8
+ * Bullet-Marker). Strukturelle Visualisierung statt dekoriertem Text —
9
+ * Aufzählungen bekommen einen klaren Akzent-Marker (kein Emoji).
10
+ *
11
+ * Frontmatter-Props:
12
+ * title — Slide-Titel (h2-Ebene; optional, sonst nur Body)
13
+ * eyebrow — kleine Überzeile (optional)
14
+ * accent — "blue" | "green" | "mixed" — Gradient-Akzent (default blue)
15
+ * Slot: default = Body aus Markdown (max. 5 Bullets · max. 1 Verschachtelung).
16
+ */
17
+ import { computed } from 'vue'
18
+
19
+ const props = withDefaults(
20
+ defineProps<{
21
+ eyebrow?: string
22
+ accent?: 'blue' | 'green' | 'mixed'
23
+ // Slidev reicht das volle Frontmatter-Objekt als `frontmatter`-Prop durch.
24
+ frontmatter?: Record<string, unknown>
25
+ }>(),
26
+ { accent: 'blue' },
27
+ )
28
+
29
+ // `title` ist ein RESERVIERTES Slidev-Frontmatter-Feld (TOC/Nav) und wird NICHT
30
+ // als eigener Prop durchgereicht (FRONTMATTER_FIELDS). Daher aus dem
31
+ // mitgelieferten `frontmatter`-Objekt lesen, damit `title:` in slides.md wie
32
+ // dokumentiert funktioniert.
33
+ const title = computed(() => props.frontmatter?.title as string | undefined)
34
+
35
+ // Token reaktiv (siehe hero.vue) — alle Werte aus theme.css, keine Hex.
36
+ const gradientVar = computed(() => `var(--miragon-gradient-${props.accent})`)
37
+ const accentVar = computed(() =>
38
+ props.accent === 'green' ? 'var(--miragon-green-deep)' : 'var(--miragon-blue)',
39
+ )
40
+ </script>
41
+
42
+ <template>
43
+ <div
44
+ class="content-layout"
45
+ :style="{ '--c-grad': gradientVar, '--c-accent': accentVar }"
46
+ >
47
+ <div class="content-inner">
48
+ <header v-if="title || eyebrow" class="content-head">
49
+ <span class="content-bar" aria-hidden="true"></span>
50
+ <div v-if="eyebrow" class="content-eyebrow">{{ eyebrow }}</div>
51
+ <h2 v-if="title" class="content-title">{{ title }}</h2>
52
+ </header>
53
+
54
+ <div class="content-body">
55
+ <slot />
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+ <style scoped>
62
+ .content-layout {
63
+ position: relative;
64
+ width: 100%;
65
+ height: 100%;
66
+ overflow: hidden;
67
+ background: var(--miragon-gray-bg);
68
+ color: var(--miragon-text-primary);
69
+ display: flex;
70
+ align-items: stretch;
71
+ }
72
+
73
+ .content-inner {
74
+ position: relative;
75
+ z-index: 1;
76
+ width: 100%;
77
+ max-width: 68rem;
78
+ margin: 0 auto;
79
+ padding: 1.75rem 5rem 2rem;
80
+ display: flex;
81
+ flex-direction: column;
82
+ }
83
+
84
+ /* --- Kopf: Balken + Eyebrow + Titel (wie hero, kompakter) ----------------- */
85
+ .content-head {
86
+ flex: 0 0 auto;
87
+ margin-bottom: 1.5rem;
88
+ }
89
+ .content-bar {
90
+ display: block;
91
+ width: 3.5rem;
92
+ height: 0.35rem;
93
+ border-radius: 999px;
94
+ background: var(--c-grad);
95
+ margin-bottom: 0.75rem;
96
+ }
97
+ .content-eyebrow {
98
+ font-size: 0.9rem;
99
+ font-weight: 600;
100
+ letter-spacing: 0.18em;
101
+ text-transform: uppercase;
102
+ color: var(--c-accent);
103
+ margin-bottom: 0.75rem;
104
+ }
105
+ .content-title {
106
+ font-size: clamp(2rem, 3.4vw, 2.9rem);
107
+ line-height: 1.1;
108
+ font-weight: 800;
109
+ letter-spacing: -0.02em;
110
+ color: var(--miragon-text-primary);
111
+ margin: 0;
112
+ }
113
+
114
+ /* --- Body: freier Markdown-Inhalt ----------------------------------------- */
115
+ .content-body {
116
+ flex: 1 1 auto;
117
+ min-height: 0;
118
+ }
119
+ .content-body :deep(p) {
120
+ font-size: 1.2rem;
121
+ line-height: 1.6;
122
+ color: var(--miragon-text-secondary);
123
+ margin: 0 0 1rem;
124
+ max-width: 48rem;
125
+ }
126
+
127
+ /* Aufzählungen: klarer Akzent-Marker (CSS-Quadrat, kein Emoji). */
128
+ .content-body :deep(ul) {
129
+ list-style: none;
130
+ padding: 0;
131
+ margin: 0;
132
+ }
133
+ .content-body :deep(li) {
134
+ position: relative;
135
+ font-size: 1.25rem;
136
+ line-height: 1.5;
137
+ color: var(--miragon-text-secondary);
138
+ padding-left: 2rem;
139
+ margin: 0 0 1.1rem;
140
+ max-width: 50rem;
141
+ }
142
+ .content-body :deep(li)::before {
143
+ content: '';
144
+ position: absolute;
145
+ left: 0;
146
+ top: 0.5em;
147
+ width: 0.7rem;
148
+ height: 0.7rem;
149
+ border-radius: 0.2rem;
150
+ background: var(--c-grad);
151
+ }
152
+ /* Eine Verschachtelungsebene: dezenter, kleinerer Marker. */
153
+ .content-body :deep(li ul) {
154
+ margin-top: 0.8rem;
155
+ }
156
+ .content-body :deep(li li) {
157
+ font-size: 1.05rem;
158
+ color: var(--miragon-text-muted);
159
+ margin-bottom: 0.6rem;
160
+ }
161
+ .content-body :deep(li li)::before {
162
+ top: 0.55em;
163
+ width: 0.45rem;
164
+ height: 0.45rem;
165
+ border-radius: 999px;
166
+ background: var(--c-accent);
167
+ }
168
+
169
+ .content-body :deep(strong) {
170
+ font-weight: 700;
171
+ color: var(--miragon-text-primary);
172
+ }
173
+ .content-body :deep(a) {
174
+ color: var(--c-accent);
175
+ text-decoration: none;
176
+ border-bottom: 1px solid currentColor;
177
+ }
178
+ .content-body :deep(code) {
179
+ font-family: var(--miragon-font-mono);
180
+ font-size: 0.9em;
181
+ background: var(--miragon-blue-light);
182
+ color: var(--miragon-blue-darker);
183
+ padding: 0.1em 0.4em;
184
+ border-radius: 0.35rem;
185
+ }
186
+ </style>