@cyber-dash-tech/revela 0.1.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +239 -0
  3. package/README.zh-CN.md +270 -0
  4. package/designs/default/DESIGN.md +1100 -0
  5. package/designs/editorial-ribbon/DESIGN.md +1092 -0
  6. package/designs/minimal/DESIGN.md +1079 -0
  7. package/domains/consulting/INDUSTRY.md +230 -0
  8. package/domains/deeptech-investment/INDUSTRY.md +160 -0
  9. package/domains/general/INDUSTRY.md +6 -0
  10. package/index.ts +1 -0
  11. package/lib/agents/research-prompt.ts +129 -0
  12. package/lib/commands/designs.ts +59 -0
  13. package/lib/commands/disable.ts +14 -0
  14. package/lib/commands/domains.ts +59 -0
  15. package/lib/commands/enable.ts +48 -0
  16. package/lib/commands/help.ts +35 -0
  17. package/lib/config.ts +65 -0
  18. package/lib/ctx.ts +27 -0
  19. package/lib/design/designs.ts +389 -0
  20. package/lib/domain/domains.ts +258 -0
  21. package/lib/frontmatter.ts +63 -0
  22. package/lib/log.ts +35 -0
  23. package/lib/prompt-builder.ts +194 -0
  24. package/lib/qa/checks.ts +594 -0
  25. package/lib/qa/index.ts +38 -0
  26. package/lib/qa/measure.ts +287 -0
  27. package/lib/read-hooks/extractors/docx.ts +16 -0
  28. package/lib/read-hooks/extractors/pdf.ts +19 -0
  29. package/lib/read-hooks/extractors/pptx.ts +53 -0
  30. package/lib/read-hooks/extractors/xlsx.ts +81 -0
  31. package/lib/read-hooks/image/compress.ts +36 -0
  32. package/lib/read-hooks/index.ts +12 -0
  33. package/lib/read-hooks/post-read.ts +74 -0
  34. package/lib/read-hooks/pre-read.ts +51 -0
  35. package/package.json +65 -0
  36. package/plugin.ts +365 -0
  37. package/skill/SKILL.md +676 -0
  38. package/tools/designs.ts +126 -0
  39. package/tools/domains.ts +73 -0
  40. package/tools/qa.ts +61 -0
  41. package/tools/research-save.ts +96 -0
  42. package/tools/workspace-scan.ts +154 -0
@@ -0,0 +1,1092 @@
1
+ ---
2
+ name: editorial-ribbon
3
+ description: High-contrast editorial light theme — condensed typography, vivid orange/mint ribbons, crisp print-like layouts
4
+ author: OpenAI
5
+ version: 1.0.0
6
+ preview: bundled
7
+ ---
8
+
9
+ ## Visual Style — Editorial Ribbon Theme
10
+
11
+ <!-- @section:global:start -->
12
+
13
+ Apply this visual style when generating all slides in this session.
14
+
15
+ This theme keeps the existing component system and layout logic intact, but changes the art direction to an editorial annual-report look inspired by the reference: bold condensed headlines, clean off-white content pages, sharp black separators, and large diagonal ribbon shapes in saturated orange, mint, blush, and soft aqua. The result should feel like a design-forward impact report rather than a software deck.
16
+
17
+ ### Core Principle
18
+
19
+ Do **not** change component layout, HTML structure, slide architecture, spacing system, or JS behavior. Only restyle surfaces, typography, color, decoration, chart treatment, and visual hierarchy.
20
+
21
+ ### Color Palette
22
+
23
+ ```css
24
+ :root {
25
+ --bg-primary: #F3F1ED; /* soft editorial paper */
26
+ --bg-secondary: #E8E5DF; /* light warm grey */
27
+ --bg-card: rgba(255, 255, 255, 0.72); /* translucent paper card */
28
+ --bg-card-solid: #F7F5F1; /* solid card fill when translucency is not desired */
29
+
30
+ --text-primary: #111111; /* near-black ink */
31
+ --text-secondary: #434343; /* dark neutral grey */
32
+ --text-muted: #7A7872; /* muted editorial caption */
33
+
34
+ --accent: #F45A2A; /* ustwo-like orange */
35
+ --accent-2: #87D8B8; /* mint ribbon */
36
+ --accent-3: #CFE8DB; /* pale mint */
37
+ --accent-4: #F3B7D9; /* pink/lilac accent */
38
+ --accent-5: #D9E6F6; /* pale blue support */
39
+
40
+ --border: rgba(17, 17, 17, 0.12);
41
+ --border-strong: rgba(17, 17, 17, 0.32);
42
+ --shadow-soft: 0 8px 30px rgba(0, 0, 0, 0.06);
43
+ --shadow-strong: 0 18px 50px rgba(0, 0, 0, 0.10);
44
+ }
45
+ ```
46
+
47
+ ### Typography
48
+
49
+ - **Display font**: `Oswald` — for all major headings, section titles, stat labels that need the compressed editorial feel.
50
+ - **Body font**: `Inter` — for paragraph text, labels, captions, lists, and UI.
51
+ - Font link tag:
52
+ ```html
53
+ <link rel="preconnect" href="https://fonts.googleapis.com">
54
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Oswald:wght@300;400;500;600&display=swap" rel="stylesheet">
55
+ ```
56
+ - Title size (h1): `92px`, weight 500, uppercase by default
57
+ - Section heading (h2): `58px`, weight 500, uppercase by default
58
+ - Subtitle size: `22px`, weight 400
59
+ - Body size: `18px`
60
+ - Label: `13px`, letter-spacing `0.12em`, uppercase
61
+ - Card title: `28px` (`Oswald`)
62
+ - Card body: `17px`
63
+ - Stat number: `72px`, weight 500
64
+ - Stat label: `15px`, uppercase
65
+ - Line height: `1.6` for body, `0.96` for h1, `1.0` for h2
66
+ - Letter spacing: `-0.02em` for large headings
67
+
68
+ All sizes are fixed `px` for the 1920×1080 canvas. Do not use `clamp()` or viewport-relative units.
69
+
70
+ ### Background Layers
71
+
72
+ This theme uses clean paper backgrounds on most slides, and stronger graphic treatment on cover or section-divider slides.
73
+
74
+ **Layer 1 — Editorial paper base:**
75
+ ```css
76
+ body::before {
77
+ content: '';
78
+ position: fixed;
79
+ inset: 0;
80
+ background:
81
+ linear-gradient(180deg, #F4F1EC 0%, #F0ECE6 100%);
82
+ z-index: -4;
83
+ }
84
+ ```
85
+
86
+ **Layer 2 — Faint print grain:**
87
+ ```css
88
+ body::after {
89
+ content: '';
90
+ position: fixed;
91
+ inset: 0;
92
+ background-image:
93
+ radial-gradient(rgba(0,0,0,0.025) 0.6px, transparent 0.6px);
94
+ background-size: 10px 10px;
95
+ opacity: 0.20;
96
+ z-index: -3;
97
+ pointer-events: none;
98
+ }
99
+ ```
100
+
101
+ **Layer 3 — Ribbon / panel shapes:**
102
+ ```css
103
+ .geo-shape-1, .geo-shape-2, .geo-shape-3 {
104
+ position: fixed;
105
+ pointer-events: none;
106
+ z-index: -2;
107
+ opacity: 0.95;
108
+ }
109
+ .geo-shape-1 {
110
+ width: 1200px;
111
+ height: 1200px;
112
+ right: -320px;
113
+ top: -220px;
114
+ background: linear-gradient(135deg, var(--accent-2) 0%, var(--accent-3) 100%);
115
+ clip-path: polygon(32% 0, 100% 0, 100% 100%, 60% 100%, 16% 48%);
116
+ }
117
+ .geo-shape-2 {
118
+ width: 960px;
119
+ height: 960px;
120
+ left: -200px;
121
+ top: -120px;
122
+ background: linear-gradient(135deg, #D93C09 0%, var(--accent) 55%, #F86D3E 100%);
123
+ clip-path: polygon(0 0, 78% 0, 42% 100%, 0 100%);
124
+ }
125
+ .geo-shape-3 {
126
+ width: 760px;
127
+ height: 760px;
128
+ right: 280px;
129
+ top: 160px;
130
+ background: radial-gradient(circle at 30% 60%, rgba(32,180,126,0.95) 0%, rgba(135,216,184,0.82) 35%, rgba(135,216,184,0) 72%);
131
+ filter: blur(24px);
132
+ border-radius: 40% 60% 52% 48% / 58% 38% 62% 42%;
133
+ opacity: 0.75;
134
+ }
135
+ ```
136
+
137
+ ### Slide Layout
138
+
139
+ - **1920×1080 fixed canvas** — each slide uses a `.slide-canvas` (1920×1080px),
140
+ scaled to fit the viewport via JS `transform: scale()`.
141
+ - Content width tiers (inside canvas, via `max-width` + `margin: 0 auto`):
142
+ - Default (`1200px`): Cover, Quote, Closing
143
+ - Wide (`1600px`): content-heavy slides (grids, multi-column, process flows)
144
+ - Canvas padding: `60px 80px`
145
+ - **No `clamp()` — use fixed `px` for all sizes.** The canvas is always
146
+ 1920×1080 and JS handles scaling, so all typography and spacing must be fixed `px`.
147
+ - **Canvas utilization** — Elements should fill approximately 70–80% of the
148
+ 1920×1080 canvas area. The editorial feel comes from bold typography and
149
+ controlled whitespace, not from leaving the canvas half-empty.
150
+ - **Content slides**: feel like clean report pages on soft paper. Grid/flex
151
+ containers should stretch toward the canvas edges. Cards should have ample
152
+ internal padding and body text so they feel substantial.
153
+ - **Cover and section-divider slides**: feel graphic and poster-like. Use
154
+ oversized condensed headlines, vivid ribbon shapes, and strong asymmetrical
155
+ compositions. Add decorative fills — diagonal ribbon blocks, gradient rules,
156
+ or large typographic ornaments.
157
+ - **Never** leave more than ~20% of the canvas visually empty on any slide.
158
+ - Title slide: large condensed uppercase heading + graphic ribbon decoration
159
+ - Content slides: heading top with rule separator, content below in clean editorial blocks
160
+
161
+ ### HTML Structure
162
+
163
+ Every generated presentation must use this exact HTML skeleton:
164
+
165
+ ```html
166
+ <!DOCTYPE html>
167
+ <html lang="{language}">
168
+ <head>
169
+ <meta charset="UTF-8">
170
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
171
+ <title>{Presentation Title}</title>
172
+ <link rel="preconnect" href="https://fonts.googleapis.com">
173
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Oswald:wght@300;400;500;600&display=swap" rel="stylesheet">
174
+ <script src="https://cdn.jsdelivr.net/npm/lucide@latest/dist/umd/lucide.js"></script>
175
+ <!-- <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> ← only if charts needed -->
176
+ <style>/* all CSS here */</style>
177
+ </head>
178
+ <body>
179
+ <!-- Geometric ribbon shapes -->
180
+ <div class="geo-shape-1"></div>
181
+ <div class="geo-shape-2"></div>
182
+ <div class="geo-shape-3"></div>
183
+
184
+ <!-- Navigation -->
185
+ <div class="progress-bar" id="progressBar"></div>
186
+ <nav class="nav-dots" id="navDots" aria-label="Slide navigation"></nav>
187
+
188
+ <!-- Slides -->
189
+ <section class="slide title-slide" data-slide-type="cover" data-index="0">
190
+ <div class="slide-canvas"> ... </div>
191
+ </section>
192
+ <section class="slide" data-slide-type="content" data-index="1">
193
+ <div class="slide-canvas"> ... </div>
194
+ </section>
195
+ <!-- every <section class="slide"> must have data-slide-type — see SKILL.md for valid values -->
196
+
197
+ <script>/* all JS here */</script>
198
+ </body>
199
+ </html>
200
+ ```
201
+
202
+ ### Core CSS
203
+
204
+ ```css
205
+ * { box-sizing: border-box; margin: 0; padding: 0; }
206
+
207
+ html {
208
+ scroll-snap-type: y mandatory;
209
+ overflow-y: scroll;
210
+ height: 100%;
211
+ }
212
+
213
+ body {
214
+ background: var(--bg-primary);
215
+ color: var(--text-primary);
216
+ font-family: 'Inter', ui-sans-serif, sans-serif;
217
+ -webkit-font-smoothing: antialiased;
218
+ height: 100%;
219
+ }
220
+
221
+ .slide {
222
+ height: 100dvh;
223
+ scroll-snap-align: start;
224
+ overflow: hidden;
225
+ display: flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+ background: transparent;
229
+ }
230
+
231
+ .slide-canvas {
232
+ width: 1920px;
233
+ height: 1080px;
234
+ flex-shrink: 0;
235
+ transform-origin: center center;
236
+ overflow: hidden;
237
+ position: relative;
238
+ display: flex;
239
+ flex-direction: column;
240
+ justify-content: center;
241
+ padding: 60px 80px;
242
+ }
243
+
244
+ /* Default content-slide treatment: editorial page */
245
+ .slide:not(.title-slide) .slide-canvas {
246
+ background: linear-gradient(180deg, rgba(255,255,255,0.34) 0%, rgba(255,255,255,0.14) 100%);
247
+ }
248
+
249
+ /* Cover slide gets graphic treatment */
250
+ .title-slide .slide-canvas {
251
+ background: #0D0D0D;
252
+ }
253
+ ```
254
+
255
+ ### Navigation & Progress
256
+
257
+ ```css
258
+ .progress-bar {
259
+ position: fixed;
260
+ top: 0;
261
+ left: 0;
262
+ height: 3px;
263
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-2) 100%);
264
+ width: 0%;
265
+ z-index: 100;
266
+ transition: width 0.2s ease;
267
+ }
268
+
269
+ .nav-dots {
270
+ position: fixed;
271
+ right: 20px;
272
+ top: 50%;
273
+ transform: translateY(-50%);
274
+ display: flex;
275
+ flex-direction: column;
276
+ gap: 9px;
277
+ z-index: 100;
278
+ }
279
+ .nav-dots button {
280
+ width: 8px;
281
+ height: 8px;
282
+ border-radius: 50%;
283
+ border: 1px solid rgba(17,17,17,0.35);
284
+ background: rgba(255,255,255,0.45);
285
+ cursor: pointer;
286
+ padding: 0;
287
+ transition: transform 0.2s ease, background 0.2s ease;
288
+ }
289
+ .nav-dots button.active {
290
+ background: var(--accent);
291
+ border-color: var(--accent);
292
+ transform: scale(1.1);
293
+ }
294
+ ```
295
+
296
+ ### Reduced Motion
297
+
298
+ ```css
299
+ @media (prefers-reduced-motion: reduce) {
300
+ .reveal {
301
+ transition: opacity 0.3s ease;
302
+ opacity: 1;
303
+ }
304
+ }
305
+ ```
306
+
307
+ ### SlidePresentation Class (Complete JavaScript)
308
+
309
+ Keep the original `SlidePresentation` class unchanged.
310
+
311
+ ```javascript
312
+ class SlidePresentation {
313
+ constructor() {
314
+ this.slides = document.querySelectorAll('.slide');
315
+ this.currentSlide = 0;
316
+ this.setupScaling();
317
+ this.setupProgressBar();
318
+ this.setupNavDots();
319
+ this.setupIntersectionObserver();
320
+ this.setupKeyboardNav();
321
+ this.setupTouchNav();
322
+ this.setupMouseWheel();
323
+ }
324
+
325
+ setupScaling() {
326
+ const canvases = document.querySelectorAll('.slide-canvas');
327
+ const BASE_W = 1920, BASE_H = 1080;
328
+ const update = () => {
329
+ const vw = window.innerWidth;
330
+ const vh = window.innerHeight;
331
+ const scale = Math.min(vw / BASE_W, vh / BASE_H);
332
+ canvases.forEach(c => { c.style.transform = `scale(${scale})`; });
333
+ };
334
+ window.addEventListener('resize', update);
335
+ update();
336
+ }
337
+
338
+ setupProgressBar() {
339
+ const bar = document.getElementById('progressBar');
340
+ const update = () => {
341
+ const scrolled = window.scrollY;
342
+ const total = document.body.scrollHeight - window.innerHeight;
343
+ bar.style.width = total > 0 ? (scrolled / total * 100) + '%' : '0%';
344
+ };
345
+ window.addEventListener('scroll', update, { passive: true });
346
+ update();
347
+ }
348
+
349
+ setupNavDots() {
350
+ const nav = document.getElementById('navDots');
351
+ this.slides.forEach((_, i) => {
352
+ const btn = document.createElement('button');
353
+ btn.setAttribute('aria-label', `Go to slide ${i + 1}`);
354
+ if (i === 0) btn.classList.add('active');
355
+ btn.addEventListener('click', () => this.goTo(i));
356
+ nav.appendChild(btn);
357
+ });
358
+ this.dots = nav.querySelectorAll('button');
359
+
360
+ const obs = new IntersectionObserver((entries) => {
361
+ entries.forEach(e => {
362
+ if (e.isIntersecting) {
363
+ const idx = parseInt(e.target.dataset.index);
364
+ this.dots.forEach((d, i) => d.classList.toggle('active', i === idx));
365
+ this.currentSlide = idx;
366
+ }
367
+ });
368
+ }, { threshold: 0.5 });
369
+ this.slides.forEach(s => obs.observe(s));
370
+ }
371
+
372
+ setupIntersectionObserver() {
373
+ const obs = new IntersectionObserver((entries) => {
374
+ entries.forEach(e => {
375
+ if (e.isIntersecting) {
376
+ e.target.querySelectorAll('.reveal').forEach(el => el.classList.add('visible'));
377
+ }
378
+ });
379
+ }, { threshold: 0.15 });
380
+ this.slides.forEach(s => obs.observe(s));
381
+ }
382
+
383
+ setupKeyboardNav() {
384
+ document.addEventListener('keydown', e => {
385
+ if (['ArrowDown', 'ArrowRight', ' ', 'PageDown'].includes(e.key)) {
386
+ e.preventDefault();
387
+ this.goTo(this.currentSlide + 1);
388
+ } else if (['ArrowUp', 'ArrowLeft', 'PageUp'].includes(e.key)) {
389
+ e.preventDefault();
390
+ this.goTo(this.currentSlide - 1);
391
+ }
392
+ });
393
+ }
394
+
395
+ setupTouchNav() {
396
+ let startY = 0;
397
+ document.addEventListener('touchstart', e => { startY = e.touches[0].clientY; }, { passive: true });
398
+ document.addEventListener('touchend', e => {
399
+ const dy = startY - e.changedTouches[0].clientY;
400
+ if (Math.abs(dy) > 40) this.goTo(this.currentSlide + (dy > 0 ? 1 : -1));
401
+ }, { passive: true });
402
+ }
403
+
404
+ setupMouseWheel() {
405
+ let last = 0;
406
+ document.addEventListener('wheel', e => {
407
+ const now = Date.now();
408
+ if (now - last < 800) return;
409
+ last = now;
410
+ this.goTo(this.currentSlide + (e.deltaY > 0 ? 1 : -1));
411
+ }, { passive: true });
412
+ }
413
+
414
+ goTo(idx) {
415
+ const clamped = Math.max(0, Math.min(this.slides.length - 1, idx));
416
+ this.slides[clamped].scrollIntoView({ behavior: 'smooth' });
417
+ this.currentSlide = clamped;
418
+ }
419
+ }
420
+
421
+ new SlidePresentation();
422
+ ```
423
+
424
+ After the class, still include:
425
+ 1. `lucide.createIcons();` if icons are used
426
+ 2. ECharts initialization if charts are present
427
+ 3. Inline editing code
428
+
429
+ <!-- @section:global:end -->
430
+
431
+ ### Component Library
432
+
433
+ All component layout and HTML structure remain unchanged. Only art direction changes.
434
+
435
+ <!-- @section:components:start -->
436
+
437
+ <!-- @component:reveal:start -->
438
+ #### Reveal Animation (.reveal)
439
+
440
+ Keep the same behavior, but make the fade slightly snappier.
441
+
442
+ ```css
443
+ .reveal {
444
+ opacity: 0;
445
+ transition: opacity 0.32s ease;
446
+ }
447
+ .reveal.visible {
448
+ opacity: 1;
449
+ }
450
+ .reveal:nth-child(1) { transition-delay: 0s; }
451
+ .reveal:nth-child(2) { transition-delay: 0.05s; }
452
+ .reveal:nth-child(3) { transition-delay: 0.10s; }
453
+ .reveal:nth-child(4) { transition-delay: 0.15s; }
454
+ .reveal:nth-child(5) { transition-delay: 0.20s; }
455
+ .reveal:nth-child(6) { transition-delay: 0.25s; }
456
+ .reveal:nth-child(7) { transition-delay: 0.30s; }
457
+ .reveal:nth-child(8) { transition-delay: 0.35s; }
458
+ ```
459
+
460
+ <!-- @component:reveal:end -->
461
+
462
+ <!-- @component:showcase:start -->
463
+ #### Showcase (.showcase)
464
+
465
+ Semi-transparent container that frames a component or image with padding and
466
+ visual depth. Use inside `.two-col-aside`, `.two-col-main`, or as a standalone
467
+ wrapper when a component needs a "stage" rather than sitting directly on the
468
+ slide background. Vertically and horizontally centers its content.
469
+
470
+ ```css
471
+ .showcase {
472
+ padding: 28px;
473
+ border: 1px solid rgba(17, 17, 17, 0.10);
474
+ background: rgba(255, 255, 255, 0.42);
475
+ display: flex;
476
+ align-items: center;
477
+ justify-content: center;
478
+ flex: 1;
479
+ }
480
+ .title-slide .showcase {
481
+ background: rgba(255, 255, 255, 0.05);
482
+ border-color: rgba(255, 255, 255, 0.12);
483
+ }
484
+ ```
485
+
486
+ <!-- @component:showcase:end -->
487
+
488
+ <!-- @component:card:start -->
489
+ #### Card (.card)
490
+
491
+ Cards should feel like editorial blocks placed on paper, not glassmorphism widgets.
492
+
493
+ ```html
494
+ <div class="card">
495
+ <i data-lucide="circle-dashed" class="card-icon"></i>
496
+ <p class="card-label">01</p>
497
+ <p class="card-title">Title</p>
498
+ <p class="card-body">Body text describing the item.</p>
499
+ </div>
500
+ ```
501
+
502
+ ```css
503
+ .card {
504
+ background: linear-gradient(180deg, rgba(255,255,255,0.78) 0%, rgba(250,248,244,0.92) 100%);
505
+ backdrop-filter: blur(8px);
506
+ -webkit-backdrop-filter: blur(8px);
507
+ border: 1px solid rgba(17,17,17,0.10);
508
+ border-radius: 0;
509
+ padding: 30px;
510
+ box-shadow: var(--shadow-soft);
511
+ display: flex;
512
+ flex-direction: column;
513
+ justify-content: flex-start;
514
+ transition: transform 0.22s ease, box-shadow 0.22s ease;
515
+ position: relative;
516
+ overflow: hidden;
517
+ }
518
+ .card::before {
519
+ content: '';
520
+ position: absolute;
521
+ left: 0;
522
+ top: 0;
523
+ width: 8px;
524
+ height: 100%;
525
+ background: linear-gradient(180deg, var(--accent) 0%, var(--accent-2) 100%);
526
+ opacity: 0.92;
527
+ }
528
+ .card:hover {
529
+ transform: translateY(-2px);
530
+ box-shadow: var(--shadow-strong);
531
+ }
532
+ .card-icon {
533
+ width: 26px;
534
+ height: 26px;
535
+ color: var(--accent);
536
+ margin-bottom: 14px;
537
+ transition: transform 0.2s ease;
538
+ }
539
+ .card:hover .card-icon { transform: scale(1.05); }
540
+ .card-label {
541
+ font-size: 13px;
542
+ letter-spacing: 0.12em;
543
+ text-transform: uppercase;
544
+ color: var(--text-muted);
545
+ margin-bottom: 10px;
546
+ padding-left: 0;
547
+ }
548
+ .card-title {
549
+ font-family: 'Oswald', sans-serif;
550
+ font-size: 28px;
551
+ line-height: 1.0;
552
+ text-transform: uppercase;
553
+ letter-spacing: -0.01em;
554
+ margin-bottom: 14px;
555
+ }
556
+ .card-body {
557
+ font-size: 17px;
558
+ color: var(--text-secondary);
559
+ line-height: 1.65;
560
+ flex: 1;
561
+ }
562
+ ```
563
+
564
+ <!-- @component:card:end -->
565
+
566
+ <!-- @component:image-card:start -->
567
+ #### Image Card (.image-card)
568
+
569
+ ```css
570
+ .image-card {
571
+ border-radius: 0;
572
+ overflow: hidden;
573
+ border: 1px solid rgba(17,17,17,0.10);
574
+ box-shadow: var(--shadow-soft);
575
+ background: #FFFFFF;
576
+ display: flex;
577
+ flex-direction: column;
578
+ }
579
+ .image-card img {
580
+ width: 100%;
581
+ height: 100%;
582
+ object-fit: cover;
583
+ display: block;
584
+ }
585
+ .image-card-caption {
586
+ padding: 14px 18px;
587
+ font-size: 14px;
588
+ color: var(--text-muted);
589
+ background: #FBF9F5;
590
+ border-top: 1px solid rgba(17,17,17,0.08);
591
+ font-family: 'Inter', sans-serif;
592
+ }
593
+ ```
594
+
595
+ <!-- @component:image-card:end -->
596
+
597
+ <!-- @component:card-img:start -->
598
+ #### Card with Image Header (.card-img)
599
+
600
+ ```css
601
+ .card-img {
602
+ padding: 0;
603
+ overflow: hidden;
604
+ }
605
+ .card-img-top {
606
+ width: 100%;
607
+ aspect-ratio: 16 / 10;
608
+ overflow: hidden;
609
+ position: relative;
610
+ }
611
+ .card-img-top::after {
612
+ content: '';
613
+ position: absolute;
614
+ inset: auto 0 0 0;
615
+ height: 8px;
616
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-2) 100%);
617
+ }
618
+ .card-img-top img {
619
+ width: 100%;
620
+ height: 100%;
621
+ object-fit: cover;
622
+ display: block;
623
+ }
624
+ .card-img .card-title,
625
+ .card-img .card-body,
626
+ .card-img .card-label {
627
+ padding-left: 30px;
628
+ padding-right: 30px;
629
+ }
630
+ .card-img .card-title { padding-top: 22px; }
631
+ .card-img .card-body { padding-bottom: 30px; }
632
+ ```
633
+
634
+ <!-- @component:card-img:end -->
635
+
636
+ <!-- @component:avatar:start -->
637
+ #### Avatar (.avatar)
638
+
639
+ ```css
640
+ .avatar {
641
+ width: 64px;
642
+ height: 64px;
643
+ border-radius: 50%;
644
+ object-fit: cover;
645
+ border: 3px solid #FFFFFF;
646
+ box-shadow: 0 0 0 1px rgba(17,17,17,0.12);
647
+ flex-shrink: 0;
648
+ }
649
+ .avatar-sm { width: 48px; height: 48px; }
650
+ .avatar-lg { width: 96px; height: 96px; }
651
+ ```
652
+
653
+ <!-- @component:avatar:end -->
654
+
655
+ <!-- @component:stat-card:start -->
656
+ #### Stat Card (.stat-card)
657
+
658
+ Stat cards should feel graphic and report-like.
659
+
660
+ ```css
661
+ .stat-card {
662
+ background: linear-gradient(180deg, rgba(255,255,255,0.86) 0%, rgba(249,246,241,0.96) 100%);
663
+ border: 1px solid rgba(17,17,17,0.10);
664
+ border-radius: 0;
665
+ padding: 48px 40px;
666
+ text-align: center;
667
+ box-shadow: var(--shadow-soft);
668
+ position: relative;
669
+ overflow: hidden;
670
+ display: flex;
671
+ flex-direction: column;
672
+ align-items: center;
673
+ justify-content: center;
674
+ }
675
+ .stat-card::before {
676
+ content: '';
677
+ position: absolute;
678
+ inset: 0 auto auto 0;
679
+ width: 100%;
680
+ height: 6px;
681
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-4) 52%, var(--accent-2) 100%);
682
+ }
683
+ .stat-number {
684
+ font-family: 'Oswald', sans-serif;
685
+ font-size: 72px;
686
+ font-weight: 500;
687
+ color: var(--text-primary);
688
+ line-height: 0.95;
689
+ letter-spacing: -0.03em;
690
+ margin-bottom: 12px;
691
+ }
692
+ .stat-label {
693
+ font-size: 15px;
694
+ color: var(--text-muted);
695
+ text-transform: uppercase;
696
+ letter-spacing: 0.12em;
697
+ margin-bottom: 12px;
698
+ }
699
+ .stat-desc {
700
+ font-size: 15px;
701
+ color: var(--text-secondary);
702
+ line-height: 1.5;
703
+ }
704
+ ```
705
+
706
+ <!-- @component:stat-card:end -->
707
+
708
+ <!-- @component:quote-block:start -->
709
+ #### Quote Block (.quote-block)
710
+
711
+ ```css
712
+ .quote-block {
713
+ display: flex;
714
+ flex-direction: column;
715
+ justify-content: center;
716
+ border-left: 8px solid var(--accent);
717
+ padding-left: 32px;
718
+ max-width: 1200px;
719
+ }
720
+ .quote-block blockquote {
721
+ font-family: 'Oswald', sans-serif;
722
+ font-size: 52px;
723
+ font-weight: 400;
724
+ text-transform: uppercase;
725
+ line-height: 1.02;
726
+ letter-spacing: -0.02em;
727
+ margin-bottom: 18px;
728
+ }
729
+ .quote-block cite {
730
+ font-size: 14px;
731
+ color: var(--text-muted);
732
+ letter-spacing: 0.08em;
733
+ text-transform: uppercase;
734
+ font-style: normal;
735
+ font-family: 'Inter', sans-serif;
736
+ }
737
+ .quote-deco {
738
+ position: absolute;
739
+ font-family: 'Oswald', sans-serif;
740
+ font-size: 320px;
741
+ line-height: 0.8;
742
+ color: rgba(244,90,42,0.10);
743
+ pointer-events: none;
744
+ z-index: 0;
745
+ }
746
+ ```
747
+
748
+ <!-- @component:quote-block:end -->
749
+
750
+ <!-- @component:step-flow:start -->
751
+ #### Step Flow (.step-flow)
752
+
753
+ ```css
754
+ .step-flow { display: flex; align-items: flex-start; max-width: 1600px; margin: 0 auto; }
755
+ .step { flex: 1; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 16px; }
756
+ .step-circle {
757
+ width: 66px;
758
+ height: 66px;
759
+ border-radius: 50%;
760
+ background: linear-gradient(135deg, var(--accent) 0%, #E24216 100%);
761
+ color: #FFFFFF;
762
+ font-family: 'Oswald', sans-serif;
763
+ font-size: 26px;
764
+ font-weight: 500;
765
+ display: flex;
766
+ align-items: center;
767
+ justify-content: center;
768
+ flex-shrink: 0;
769
+ box-shadow: 0 8px 18px rgba(244, 90, 42, 0.22);
770
+ }
771
+ .step-connector {
772
+ flex: 0 0 32px;
773
+ height: 4px;
774
+ background: linear-gradient(90deg, var(--accent-4) 0%, var(--accent-2) 100%);
775
+ margin-top: 31px;
776
+ align-self: flex-start;
777
+ }
778
+ .step-title {
779
+ font-family: 'Oswald', sans-serif;
780
+ font-size: 22px;
781
+ line-height: 1.0;
782
+ text-transform: uppercase;
783
+ }
784
+ .step-desc {
785
+ font-size: 16px;
786
+ color: var(--text-secondary);
787
+ line-height: 1.55;
788
+ max-width: 220px;
789
+ }
790
+ ```
791
+
792
+ <!-- @component:step-flow:end -->
793
+
794
+ <!-- @component:evidence-list:start -->
795
+ #### Evidence List (.evidence-list)
796
+
797
+ ```css
798
+ .evidence-list {
799
+ list-style: none;
800
+ padding: 0;
801
+ display: flex;
802
+ flex-direction: column;
803
+ gap: 12px;
804
+ }
805
+ .evidence-list li {
806
+ padding-left: 20px;
807
+ position: relative;
808
+ font-size: 18px;
809
+ color: var(--text-primary);
810
+ line-height: 1.5;
811
+ }
812
+ .evidence-list li::before {
813
+ content: '';
814
+ position: absolute;
815
+ left: 0;
816
+ top: 11px;
817
+ width: 10px;
818
+ height: 3px;
819
+ background: var(--accent);
820
+ }
821
+ ```
822
+
823
+ <!-- @component:evidence-list:end -->
824
+
825
+ <!-- @component:chart-container:start -->
826
+ #### Chart Container (.chart-container)
827
+
828
+ ```css
829
+ .chart-container { position: relative; flex-shrink: 0; }
830
+ ```
831
+
832
+ <!-- @component:chart-container:end -->
833
+
834
+ <!-- @component:text-helpers:start -->
835
+ ### Text Helpers
836
+
837
+ ```css
838
+ h1 {
839
+ font-family: 'Oswald', sans-serif;
840
+ font-size: 92px;
841
+ font-weight: 500;
842
+ line-height: 0.96;
843
+ letter-spacing: -0.02em;
844
+ text-transform: uppercase;
845
+ }
846
+ h2 {
847
+ font-family: 'Oswald', sans-serif;
848
+ font-size: 58px;
849
+ font-weight: 500;
850
+ line-height: 1.0;
851
+ letter-spacing: -0.02em;
852
+ text-transform: uppercase;
853
+ }
854
+ .label {
855
+ font-size: 13px;
856
+ letter-spacing: 0.12em;
857
+ text-transform: uppercase;
858
+ color: var(--text-muted);
859
+ font-weight: 500;
860
+ }
861
+ .subtitle {
862
+ font-size: 22px;
863
+ color: var(--text-secondary);
864
+ font-weight: 400;
865
+ line-height: 1.55;
866
+ }
867
+ .body-text {
868
+ font-size: 18px;
869
+ color: var(--text-secondary);
870
+ line-height: 1.68;
871
+ }
872
+ ```
873
+
874
+ Rules for text:
875
+ - Headings should default to uppercase.
876
+ - Major titles should be tall, condensed, and vertically stacked when useful.
877
+ - Use strong alignment and line breaks to create poster-like impact.
878
+ - Avoid serif typography entirely in this theme.
879
+
880
+ <!-- @component:text-helpers:end -->
881
+
882
+ <!-- @component:dividers:start -->
883
+ ### Dividers and Rules
884
+
885
+ ```css
886
+ .divider {
887
+ border: none;
888
+ border-top: 1px solid rgba(17,17,17,0.16);
889
+ margin: 24px 0;
890
+ }
891
+ .divider-wide {
892
+ border: none;
893
+ border-top: 4px solid var(--accent);
894
+ width: 260px;
895
+ }
896
+ .heading-rule {
897
+ border: none;
898
+ border-bottom: 2px solid rgba(17,17,17,0.14);
899
+ padding-bottom: 18px;
900
+ margin-bottom: 26px;
901
+ }
902
+ ```
903
+
904
+ Use thicker graphic rules on sparse slides and lighter rules on content slides.
905
+
906
+ <!-- @component:dividers:end -->
907
+
908
+ <!-- @component:icons:start -->
909
+ ### Icons (Lucide)
910
+
911
+ Icons remain optional.
912
+
913
+ - Default icon color: `var(--accent)`
914
+ - Keep stroke icons simple and editorial
915
+ - Good choices: `arrow-up-right`, `circle`, `sparkles`, `target`, `book-open`, `activity`
916
+ - Do not overuse icons; typography should still dominate
917
+
918
+ <!-- @component:icons:end -->
919
+
920
+ <!-- @component:deco-fills:start -->
921
+ ### Decorative Fills (.deco-circle, .deco-rule)
922
+
923
+ Decorative fills should echo the ribbon language from the reference image.
924
+
925
+ ```css
926
+ .deco-circle { position: absolute; border-radius: 50%; pointer-events: none; z-index: 0; }
927
+ .deco-rule { position: absolute; pointer-events: none; z-index: 0; }
928
+ .slide-canvas > *:not(.deco-circle):not(.deco-rule):not(.quote-deco) { position: relative; z-index: 1; }
929
+ ```
930
+
931
+ Recommended usage:
932
+ - On title slides: use 2–3 oversized diagonal blocks or soft ribbon glows.
933
+ - On content slides: use at most one subtle blush, mint, or pale-blue block tucked into a corner.
934
+ - Section title slides may use vivid orange, mint, and pink gradients.
935
+
936
+ Suggested styles:
937
+ - `.deco-circle`: `background: radial-gradient(circle, rgba(244,90,42,0.30) 0%, rgba(244,90,42,0) 72%)`
938
+ - `.deco-rule`: `height: 6px; background: linear-gradient(90deg, var(--accent), var(--accent-2))`
939
+
940
+ <!-- @component:deco-fills:end -->
941
+
942
+ <!-- @section:components:end -->
943
+
944
+ <!-- @section:layouts:start -->
945
+
946
+ ### Layout Primitives
947
+
948
+ All layout primitives remain unchanged.
949
+
950
+ #### Centered Stack
951
+
952
+ Use for cover, quote, closing, and section-divider slides. Same structure as original.
953
+
954
+ #### Top-Aligned Stack
955
+
956
+ Use for content-heavy report slides. Same structure as original.
957
+
958
+ #### Two-Column Grid (.two-col)
959
+
960
+ Keep exactly the same layout definition:
961
+
962
+ ```css
963
+ .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: stretch; max-width: 1600px; margin: 0 auto; flex: 1; }
964
+ .two-col-main { display: flex; flex-direction: column; justify-content: center; }
965
+ .two-col-main h2 { font-size: 52px; margin-bottom: 16px; }
966
+ .two-col-main p { font-size: 18px; color: var(--text-secondary); line-height: 1.68; }
967
+ .two-col-aside { display: flex; flex-direction: column; justify-content: center; }
968
+ .two-col-aside .card { flex: 1; }
969
+ ```
970
+
971
+ #### Three-Column Grid
972
+
973
+ Keep same structure; only art changes.
974
+
975
+ ```css
976
+ .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; max-width: 1600px; margin: 0 auto; flex: 1; align-content: stretch; }
977
+ .stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; max-width: 1600px; margin: 0 auto; }
978
+ ```
979
+
980
+ #### Horizontal Flow
981
+
982
+ Keep same structure.
983
+
984
+ <!-- @section:layouts:end -->
985
+
986
+ <!-- @section:charts:start -->
987
+
988
+ ### Data Visualization (ECharts)
989
+
990
+ Charts must adopt the same editorial report look.
991
+
992
+ #### Shared chart styles
993
+
994
+ - **Color palette**: `['#F45A2A', '#82DCC2', '#F3B7D9', '#D6E4F5', '#111111']`
995
+ - **Background**: `'transparent'`
996
+ - **Axis labels / legend text**: `#5D5B56`, `14px`, `Inter`
997
+ - **Grid lines**: `rgba(17,17,17,0.10)`
998
+ - **Tooltip**: warm white background with thin dark border
999
+ - **Animation**: subtle fade only; no bouncy motion
1000
+
1001
+ #### Pie / Donut
1002
+
1003
+ - Donuts are encouraged for impact-score slides
1004
+ - Use thin rings and generous whitespace
1005
+ - Center labels should use `Oswald`
1006
+ - Segment colors should prioritize orange, mint, pink, then pale blue
1007
+ - Ring backgrounds should be very light neutral grey
1008
+
1009
+ #### Bar Charts
1010
+
1011
+ - Use flat fills, not gradients, unless the chart is on a cover or divider slide
1012
+ - Prefer horizontal bars for rankings
1013
+ - Use orange for primary series and mint/pink for secondary series
1014
+ - Avoid rounded corners larger than `3px`
1015
+
1016
+ #### Line / Area Charts
1017
+
1018
+ - Line width: `2.5px`
1019
+ - Use smooth curves only when it improves readability
1020
+ - Area fills may use 8–14% opacity in mint, orange, or pink
1021
+ - No glow effects
1022
+
1023
+ <!-- @section:charts:end -->
1024
+
1025
+ <!-- @section:guide:start -->
1026
+
1027
+ ### Composition Guide
1028
+
1029
+ #### Overall Visual Behavior
1030
+
1031
+ - Cover slides: bold, graphic, asymmetrical, high color contrast
1032
+ - Section slides: can use saturated ribbons and oversized typography
1033
+ - Standard content slides: mostly pale paper backgrounds with one focal graphic accent
1034
+ - Dense content slides: let typography and charts carry the slide; use minimal decoration
1035
+
1036
+ #### Common Recipes
1037
+
1038
+ | Content Pattern | Suggested Recipe | Editorial Ribbon Notes |
1039
+ |---|---|---|
1040
+ | 3–4 parallel features | 3-col grid + card ×3 | Use colored left-edge accents in cards |
1041
+ | Key metrics | 3-col grid + stat-card ×3 | Large Oswald numerals, colored top bars |
1042
+ | Narrative + evidence | two-col + large condensed title + card | Clean annual-report feel |
1043
+ | Sequential process | horizontal flow + step-flow | Graphic circles and thicker gradient connectors |
1044
+ | Memorable quote | centered stack + quote-block | Poster-like uppercase quote |
1045
+ | Data-heavy insight | two-col or full-width chart | Keep backgrounds quiet |
1046
+ | Section divider | centered stack + huge heading + ribbon background | Strong graphic page |
1047
+
1048
+ #### Element Usage Rules
1049
+
1050
+ - Typography should do most of the work.
1051
+ - Use uppercase headlines generously.
1052
+ - Use vivid accent color sparingly but deliberately.
1053
+ - Keep content slides mostly light; do not flood every slide with strong orange/mint ribbons.
1054
+ - Preserve the original spacing, grid logic, and component placement.
1055
+
1056
+ #### Common Mistakes
1057
+
1058
+ - Making every slide look like the cover
1059
+ - Adding too many gradients inside content cards
1060
+ - Using soft startup-style UI treatment instead of print-editorial sharpness
1061
+ - Switching layout structure to imitate the reference — do not do this
1062
+ - Over-rounding corners — keep corners mostly square
1063
+ - Returning to monochrome minimalism — this theme needs controlled color
1064
+
1065
+ ### Code Blocks (if any)
1066
+
1067
+ ```css
1068
+ pre, code {
1069
+ background: #F7F3EE;
1070
+ border: 1px solid rgba(17,17,17,0.10);
1071
+ border-radius: 0;
1072
+ font-family: 'SFMono-Regular', 'Menlo', ui-monospace, monospace;
1073
+ font-size: 14px;
1074
+ color: var(--text-primary);
1075
+ }
1076
+ ```
1077
+
1078
+ ### Do & Don't
1079
+
1080
+ - **Do** keep the original component and layout system intact
1081
+ - **Do** use condensed editorial typography
1082
+ - **Do** use pale paper backgrounds for most content slides
1083
+ - **Do** use vivid orange/mint/pink accents for covers, dividers, and data highlights
1084
+ - **Do** create strong poster-like hierarchy with line breaks and uppercase text
1085
+ - **Do** use flat or lightly translucent cards with square edges
1086
+ - **Don't** use serif fonts
1087
+ - **Don't** use heavy glassmorphism
1088
+ - **Don't** use neon or tech-blue UI styling
1089
+ - **Don't** alter layout primitives or component HTML
1090
+ - **Don't** make every slide dark; only covers/section dividers may be dark or heavily graphic
1091
+
1092
+ <!-- @section:guide:end -->