create-slide-deck 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 (105) hide show
  1. package/dist/index.js +119 -0
  2. package/package.json +36 -0
  3. package/template-full/README.md +99 -0
  4. package/template-full/package.json +47 -0
  5. package/template-full/src/reveal/components/auto-layout.ts +229 -0
  6. package/template-full/src/reveal/components/charts.tsx +213 -0
  7. package/template-full/src/reveal/core/blocks.ts +172 -0
  8. package/template-full/src/reveal/core/deck-init.ts +60 -0
  9. package/template-full/src/reveal/core/design.ts +46 -0
  10. package/template-full/src/reveal/core/layout.ts +187 -0
  11. package/template-full/src/reveal/core/mount-registry.ts +41 -0
  12. package/template-full/src/reveal/core/presets.ts +189 -0
  13. package/template-full/src/reveal/core/runtime.ts +141 -0
  14. package/template-full/src/reveal/core/types.ts +114 -0
  15. package/template-full/src/reveal/data/algorithms.ts +78 -0
  16. package/template-full/src/reveal/data/benchmark.ts +79 -0
  17. package/template-full/src/reveal/decks/demo-showcase/components/demo-arc-progress.tsx +153 -0
  18. package/template-full/src/reveal/decks/demo-showcase/components/demo-before-after.tsx +164 -0
  19. package/template-full/src/reveal/decks/demo-showcase/components/demo-bigtext.tsx +70 -0
  20. package/template-full/src/reveal/decks/demo-showcase/components/demo-card-flip.tsx +118 -0
  21. package/template-full/src/reveal/decks/demo-showcase/components/demo-chat-bubbles.tsx +257 -0
  22. package/template-full/src/reveal/decks/demo-showcase/components/demo-code.tsx +136 -0
  23. package/template-full/src/reveal/decks/demo-showcase/components/demo-concept-map.tsx +336 -0
  24. package/template-full/src/reveal/decks/demo-showcase/components/demo-counter.tsx +194 -0
  25. package/template-full/src/reveal/decks/demo-showcase/components/demo-cover.tsx +188 -0
  26. package/template-full/src/reveal/decks/demo-showcase/components/demo-dark-dashboard.tsx +166 -0
  27. package/template-full/src/reveal/decks/demo-showcase/components/demo-eval-matrix.tsx +191 -0
  28. package/template-full/src/reveal/decks/demo-showcase/components/demo-force-graph.tsx +169 -0
  29. package/template-full/src/reveal/decks/demo-showcase/components/demo-fullbleed-bars.tsx +109 -0
  30. package/template-full/src/reveal/decks/demo-showcase/components/demo-fullbleed-flow.tsx +177 -0
  31. package/template-full/src/reveal/decks/demo-showcase/components/demo-heatmap.tsx +135 -0
  32. package/template-full/src/reveal/decks/demo-showcase/components/demo-icon-wall.tsx +143 -0
  33. package/template-full/src/reveal/decks/demo-showcase/components/demo-math.tsx +103 -0
  34. package/template-full/src/reveal/decks/demo-showcase/components/demo-number-morph.tsx +126 -0
  35. package/template-full/src/reveal/decks/demo-showcase/components/demo-path.tsx +185 -0
  36. package/template-full/src/reveal/decks/demo-showcase/components/demo-radar.tsx +124 -0
  37. package/template-full/src/reveal/decks/demo-showcase/components/demo-rough.tsx +169 -0
  38. package/template-full/src/reveal/decks/demo-showcase/components/demo-sankey.tsx +144 -0
  39. package/template-full/src/reveal/decks/demo-showcase/components/demo-screenshot-annotate.tsx +181 -0
  40. package/template-full/src/reveal/decks/demo-showcase/components/demo-stacked-cards.tsx +159 -0
  41. package/template-full/src/reveal/decks/demo-showcase/components/demo-tabs.tsx +206 -0
  42. package/template-full/src/reveal/decks/demo-showcase/components/demo-timeline.tsx +162 -0
  43. package/template-full/src/reveal/decks/demo-showcase/components/demo-treemap.tsx +161 -0
  44. package/template-full/src/reveal/decks/demo-showcase/components/demo-zoom-focus.tsx +223 -0
  45. package/template-full/src/reveal/decks/demo-showcase/components/registry.ts +63 -0
  46. package/template-full/src/reveal/decks/demo-showcase/demo.css +237 -0
  47. package/template-full/src/reveal/decks/demo-showcase/index.html +24 -0
  48. package/template-full/src/reveal/decks/demo-showcase/main.ts +7 -0
  49. package/template-full/src/reveal/decks/demo-showcase/slides.ts +271 -0
  50. package/template-full/src/reveal/decks/fse26-rca/components/aws-cascade.tsx +295 -0
  51. package/template-full/src/reveal/decks/fse26-rca/components/bench-compare.tsx +64 -0
  52. package/template-full/src/reveal/decks/fse26-rca/components/bench-deficiency.tsx +104 -0
  53. package/template-full/src/reveal/decks/fse26-rca/components/bench-loop.tsx +402 -0
  54. package/template-full/src/reveal/decks/fse26-rca/components/bench-needs.tsx +78 -0
  55. package/template-full/src/reveal/decks/fse26-rca/components/closing-takeaway.tsx +165 -0
  56. package/template-full/src/reveal/decks/fse26-rca/components/cloud-incidents.tsx +88 -0
  57. package/template-full/src/reveal/decks/fse26-rca/components/failure-modes.tsx +59 -0
  58. package/template-full/src/reveal/decks/fse26-rca/components/fault-heatmap.tsx +85 -0
  59. package/template-full/src/reveal/decks/fse26-rca/components/hierarchy-tree.tsx +93 -0
  60. package/template-full/src/reveal/decks/fse26-rca/components/incident-hard.tsx +72 -0
  61. package/template-full/src/reveal/decks/fse26-rca/components/rca-pipeline.tsx +193 -0
  62. package/template-full/src/reveal/decks/fse26-rca/components/registry.ts +37 -0
  63. package/template-full/src/reveal/decks/fse26-rca/components/simple-rca.tsx +216 -0
  64. package/template-full/src/reveal/decks/fse26-rca/components/sota-collapse.tsx +63 -0
  65. package/template-full/src/reveal/decks/fse26-rca/components/srca-results.tsx +115 -0
  66. package/template-full/src/reveal/decks/fse26-rca/images/aws-outage-2025-deployflow.png +0 -0
  67. package/template-full/src/reveal/decks/fse26-rca/images/aws-post-event-summary.png +0 -0
  68. package/template-full/src/reveal/decks/fse26-rca/images/bbc-crowdstrike.png +0 -0
  69. package/template-full/src/reveal/decks/fse26-rca/images/cnn-meta-outage-2021.png +0 -0
  70. package/template-full/src/reveal/decks/fse26-rca/images/cover.png +0 -0
  71. package/template-full/src/reveal/decks/fse26-rca/images/nyt-facebook-2021.png +0 -0
  72. package/template-full/src/reveal/decks/fse26-rca/images/qr-repo.png +0 -0
  73. package/template-full/src/reveal/decks/fse26-rca/images/verge-crowdstrike-2024.png +0 -0
  74. package/template-full/src/reveal/decks/fse26-rca/images/wiki-meta-outage-2021.png +0 -0
  75. package/template-full/src/reveal/decks/fse26-rca/index.html +30 -0
  76. package/template-full/src/reveal/decks/fse26-rca/main.ts +8 -0
  77. package/template-full/src/reveal/decks/fse26-rca/slides.ts +175 -0
  78. package/template-full/src/reveal/env.d.ts +38 -0
  79. package/template-full/src/reveal/theme.css +762 -0
  80. package/template-full/src/reveal/tools/dev.mjs +120 -0
  81. package/template-full/src/reveal/tools/export-pdf.mjs +86 -0
  82. package/template-full/src/reveal/tools/preview.mjs +132 -0
  83. package/template-full/tsconfig.json +19 -0
  84. package/template-full/vite.config.ts +95 -0
  85. package/template-minimal/package.json +42 -0
  86. package/template-minimal/src/reveal/components/auto-layout.ts +229 -0
  87. package/template-minimal/src/reveal/components/charts.tsx +213 -0
  88. package/template-minimal/src/reveal/core/blocks.ts +172 -0
  89. package/template-minimal/src/reveal/core/deck-init.ts +60 -0
  90. package/template-minimal/src/reveal/core/design.ts +46 -0
  91. package/template-minimal/src/reveal/core/layout.ts +187 -0
  92. package/template-minimal/src/reveal/core/mount-registry.ts +41 -0
  93. package/template-minimal/src/reveal/core/presets.ts +189 -0
  94. package/template-minimal/src/reveal/core/runtime.ts +141 -0
  95. package/template-minimal/src/reveal/core/types.ts +114 -0
  96. package/template-minimal/src/reveal/data/.gitkeep +0 -0
  97. package/template-minimal/src/reveal/decks/my-deck/components/example-component.tsx +28 -0
  98. package/template-minimal/src/reveal/decks/my-deck/components/registry.ts +9 -0
  99. package/template-minimal/src/reveal/decks/my-deck/index.html +14 -0
  100. package/template-minimal/src/reveal/decks/my-deck/main.ts +5 -0
  101. package/template-minimal/src/reveal/decks/my-deck/slides.ts +34 -0
  102. package/template-minimal/src/reveal/env.d.ts +38 -0
  103. package/template-minimal/src/reveal/theme.css +762 -0
  104. package/template-minimal/tsconfig.json +19 -0
  105. package/template-minimal/vite.config.ts +95 -0
@@ -0,0 +1,153 @@
1
+ import React from "react";
2
+ import { CANVAS, vis, useRevealStep } from "../../../core/presets.ts";
3
+ import type { SlideComponentProps } from "../../../core/types.ts";
4
+
5
+ const anime = (typeof window !== "undefined") ? (window as any).anime : null;
6
+
7
+ const R = 60;
8
+ const CIRC = 2 * Math.PI * R;
9
+
10
+ const GAUGES = [
11
+ { key: "precision", label: "Precision", value: 94.2, suffix: "%", color: "#2563eb", baseline: "78.3%" },
12
+ { key: "recall", label: "Recall", value: 87.3, suffix: "%", color: "#9333ea", baseline: "71.2%" },
13
+ { key: "f1", label: "F1 Score", value: 90.6, suffix: "%", color: "#db2777", baseline: "74.5%" },
14
+ { key: "speed", label: "Speed", value: 7, suffix: "x", color: "#059669", baseline: "3x", max: 10 },
15
+ ];
16
+
17
+ export default function DemoArcProgress(props: SlideComponentProps) {
18
+ const Reveal = props.Reveal;
19
+ const ref = useRevealStep(Reveal);
20
+ const step = ref[0], ready = ref[1], instant = ref[2];
21
+
22
+ // animated fraction (0..1) and displayed number per gauge
23
+ const animRef = React.useState(GAUGES.map(function () { return { frac: 0, num: 0 }; }));
24
+ const anims = animRef[0], setAnims = animRef[1];
25
+
26
+ const playedRef = React.useRef(false);
27
+
28
+ React.useEffect(function () {
29
+ if (step >= 0) {
30
+ if (playedRef.current) return;
31
+ playedRef.current = true;
32
+ if (!anime || !anime.animate) {
33
+ setAnims(GAUGES.map(function (g) {
34
+ const frac = g.value / (g.max || 100);
35
+ return { frac: frac, num: g.value };
36
+ }));
37
+ return;
38
+ }
39
+ GAUGES.forEach(function (g, i) {
40
+ const target = g.value / (g.max || 100);
41
+ const obj = { frac: 0, num: 0 };
42
+ anime.animate(obj, {
43
+ frac: target,
44
+ num: g.value,
45
+ duration: 1400,
46
+ ease: "outCubic",
47
+ update: function () {
48
+ setAnims(function (prev) {
49
+ const next = prev.slice();
50
+ next[i] = { frac: obj.frac, num: obj.num };
51
+ return next;
52
+ });
53
+ },
54
+ });
55
+ });
56
+ } else {
57
+ playedRef.current = false;
58
+ setAnims(GAUGES.map(function () { return { frac: 0, num: 0 }; }));
59
+ }
60
+ }, [step]);
61
+
62
+ return (
63
+ <div style={{
64
+ width: "100%", height: "100%",
65
+ display: "flex", flexDirection: "column",
66
+ alignItems: "center", justifyContent: "center",
67
+ fontFamily: CANVAS.fontFamily,
68
+ opacity: ready ? 1 : 0,
69
+ transition: "opacity 0.2s ease",
70
+ }}>
71
+ <div style={{ display: "flex", gap: 48, alignItems: "flex-start" }}>
72
+ {GAUGES.map(function (g, i) {
73
+ const a = anims[i] || { frac: 0, num: 0 };
74
+ const offset = CIRC * (1 - a.frac);
75
+ const isBest = g.key === "precision";
76
+ const displayNum = g.suffix === "x"
77
+ ? a.num.toFixed(1)
78
+ : a.num.toFixed(1);
79
+ return (
80
+ <div key={g.key} style={{
81
+ display: "flex", flexDirection: "column", alignItems: "center",
82
+ }}>
83
+ <div style={{ position: "relative", width: 160, height: 160 }}>
84
+ <svg viewBox="0 0 160 160" style={{ width: 160, height: 160 }}>
85
+ {isBest && (
86
+ <circle
87
+ cx="80" cy="80" r={R + 12}
88
+ fill="none"
89
+ stroke={g.color}
90
+ strokeWidth="3"
91
+ style={Object.assign({
92
+ transformOrigin: "80px 80px",
93
+ animation: step >= 1 ? "arcPulse 1.6s ease-in-out infinite" : "none",
94
+ opacity: step >= 1 ? 1 : 0,
95
+ transition: instant ? "none" : "opacity 0.4s ease",
96
+ })}
97
+ />
98
+ )}
99
+ <circle
100
+ cx="80" cy="80" r={R}
101
+ fill="none"
102
+ stroke="#e5e7eb"
103
+ strokeWidth="10"
104
+ />
105
+ <circle
106
+ cx="80" cy="80" r={R}
107
+ fill="none"
108
+ stroke={g.color}
109
+ strokeWidth="10"
110
+ strokeLinecap="round"
111
+ strokeDasharray={CIRC}
112
+ strokeDashoffset={offset}
113
+ transform="rotate(-90 80 80)"
114
+ />
115
+ </svg>
116
+ <div style={{
117
+ position: "absolute", top: 0, left: 0,
118
+ width: 160, height: 160,
119
+ display: "flex", alignItems: "center", justifyContent: "center",
120
+ fontSize: 32, fontWeight: 900, color: g.color,
121
+ fontVariantNumeric: "tabular-nums",
122
+ }}>
123
+ {displayNum}{g.suffix}
124
+ </div>
125
+ </div>
126
+ <div style={{ fontSize: 14, fontWeight: 700, color: "#374151", marginTop: 8 }}>
127
+ {g.label}
128
+ </div>
129
+ <div style={Object.assign({
130
+ fontSize: 14, color: "#9ca3af", marginTop: 6,
131
+ }, vis(step, 2, instant))}>
132
+ vs. {g.baseline}
133
+ </div>
134
+ </div>
135
+ );
136
+ })}
137
+ </div>
138
+
139
+ <div style={Object.assign({
140
+ marginTop: 18, fontSize: 14, color: "#9ca3af",
141
+ }, vis(step, 2, instant))}>
142
+ vs. best baseline: 78.3% / 71.2% / 74.5% / 3x
143
+ </div>
144
+
145
+ <style>{
146
+ "@keyframes arcPulse {" +
147
+ "0%,100% { opacity: 0.25; transform: scale(0.98); }" +
148
+ "50% { opacity: 0.8; transform: scale(1.02); }" +
149
+ "}"
150
+ }</style>
151
+ </div>
152
+ );
153
+ }
@@ -0,0 +1,164 @@
1
+ import React from "react";
2
+ import { X, Check, ArrowRight } from "lucide-react";
3
+ import { CANVAS, useRevealStep, vis } from "../../../core/presets.ts";
4
+ import type { SlideComponentProps } from "../../../core/types.ts";
5
+
6
+ const PAIRS = [
7
+ { before: "Manual log analysis", after: "Automated trace parsing" },
8
+ { before: "30+ min mean time to detect", after: "4.2 min detection (7x faster)" },
9
+ { before: "Single-metric alerting", after: "Multi-signal correlation" },
10
+ { before: "Expert-dependent diagnosis", after: "ML-driven root cause ranking" },
11
+ ];
12
+
13
+ const RED = "#dc2626";
14
+ const GREEN = "#16a34a";
15
+ const ROW_TOP = 150;
16
+ const ROW_GAP = 78;
17
+
18
+ export default function DemoBeforeAfter({ Reveal }: SlideComponentProps) {
19
+ const ref = useRevealStep(Reveal);
20
+ const step = ref[0], ready = ref[1], instant = ref[2];
21
+
22
+ const trans = function (extra?: string) {
23
+ return instant ? "none" : (extra || "opacity 0.5s ease, transform 0.5s ease");
24
+ };
25
+
26
+ return (
27
+ <div style={{
28
+ position: "relative", width: "100%", height: "100%",
29
+ overflow: "hidden", fontFamily: CANVAS.fontFamily,
30
+ opacity: ready ? 1 : 0, transition: "opacity 0.2s ease",
31
+ }}>
32
+ {/* Left panel: Before (red), clipped to a 5-degree diagonal on its right edge */}
33
+ <div style={{
34
+ position: "absolute", inset: 0,
35
+ background: "#fef2f2",
36
+ clipPath: "polygon(0 0, 53% 0, 47% 100%, 0 100%)",
37
+ opacity: vis(step, 0, instant).opacity,
38
+ transition: trans("opacity 0.5s ease"),
39
+ }} />
40
+
41
+ {/* Right panel: After (green), slides in from the right */}
42
+ <div style={{
43
+ position: "absolute", inset: 0,
44
+ background: "#f0fdf4",
45
+ clipPath: "polygon(53% 0, 100% 0, 100% 100%, 47% 100%)",
46
+ opacity: step >= 1 ? 1 : 0,
47
+ transform: step >= 1 ? "translateX(0)" : "translateX(60px)",
48
+ transition: trans(),
49
+ }} />
50
+
51
+ {/* Diagonal divider line */}
52
+ <svg viewBox="0 0 1200 560" preserveAspectRatio="none" style={{
53
+ position: "absolute", inset: 0, width: "100%", height: "100%",
54
+ pointerEvents: "none",
55
+ opacity: step >= 1 ? 1 : 0,
56
+ transition: trans("opacity 0.5s ease"),
57
+ }}>
58
+ <line x1="636" y1="0" x2="564" y2="560" stroke="#94a3b8" strokeWidth="3" />
59
+ </svg>
60
+
61
+ {/* Left header */}
62
+ <div style={{
63
+ position: "absolute", top: 48, left: 70,
64
+ display: "flex", alignItems: "center", gap: 14,
65
+ opacity: vis(step, 0, instant).opacity,
66
+ transition: trans("opacity 0.5s ease"),
67
+ }}>
68
+ <div style={{
69
+ width: 48, height: 48, borderRadius: 12, background: RED,
70
+ display: "flex", alignItems: "center", justifyContent: "center",
71
+ }}>
72
+ <X size={30} color="#fff" strokeWidth={3} />
73
+ </div>
74
+ <span style={{ fontSize: 38, fontWeight: 900, color: RED, letterSpacing: 0.5 }}>Before</span>
75
+ </div>
76
+
77
+ {/* Right header */}
78
+ <div style={{
79
+ position: "absolute", top: 48, right: 70,
80
+ display: "flex", alignItems: "center", gap: 14,
81
+ opacity: step >= 1 ? 1 : 0,
82
+ transform: step >= 1 ? "translateX(0)" : "translateX(60px)",
83
+ transition: trans(),
84
+ }}>
85
+ <span style={{ fontSize: 38, fontWeight: 900, color: GREEN, letterSpacing: 0.5 }}>After</span>
86
+ <div style={{
87
+ width: 48, height: 48, borderRadius: 12, background: GREEN,
88
+ display: "flex", alignItems: "center", justifyContent: "center",
89
+ }}>
90
+ <Check size={30} color="#fff" strokeWidth={3} />
91
+ </div>
92
+ </div>
93
+
94
+ {/* Rows */}
95
+ {PAIRS.map(function (p, i) {
96
+ const y = ROW_TOP + i * ROW_GAP;
97
+ return (
98
+ <React.Fragment key={i}>
99
+ {/* Left item */}
100
+ <div style={{
101
+ position: "absolute", left: 70, top: y, width: 430,
102
+ display: "flex", alignItems: "center", gap: 14,
103
+ opacity: vis(step, 0, instant).opacity,
104
+ transition: trans("opacity 0.5s ease"),
105
+ }}>
106
+ <span style={{
107
+ flex: "0 0 auto", width: 12, height: 12, borderRadius: "50%", background: RED,
108
+ }} />
109
+ <span style={{ fontSize: 23, fontWeight: 700, color: "#7f1d1d", lineHeight: 1.2 }}>
110
+ {p.before}
111
+ </span>
112
+ </div>
113
+
114
+ {/* Connecting arrow */}
115
+ <div style={{
116
+ position: "absolute", left: 540, top: y - 4, width: 120,
117
+ display: "flex", alignItems: "center", justifyContent: "center",
118
+ opacity: step >= 2 ? 1 : 0,
119
+ transform: step >= 2 ? "translateX(0)" : "translateX(-20px)",
120
+ transition: trans(),
121
+ }}>
122
+ <ArrowRight size={34} color="#475569" strokeWidth={2.5} />
123
+ </div>
124
+
125
+ {/* Right item */}
126
+ <div style={{
127
+ position: "absolute", right: 70, top: y, width: 430,
128
+ display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 14,
129
+ textAlign: "right",
130
+ opacity: step >= 1 ? 1 : 0,
131
+ transform: step >= 1 ? "translateX(0)" : "translateX(60px)",
132
+ transition: trans(),
133
+ }}>
134
+ <span style={{ fontSize: 23, fontWeight: 700, color: "#14532d", lineHeight: 1.2 }}>
135
+ {p.after}
136
+ </span>
137
+ <span style={{
138
+ flex: "0 0 auto", width: 12, height: 12, borderRadius: "50%", background: GREEN,
139
+ }} />
140
+ </div>
141
+ </React.Fragment>
142
+ );
143
+ })}
144
+
145
+ {/* Bottom banner */}
146
+ <div style={{
147
+ position: "absolute", left: "50%", bottom: 30,
148
+ transform: step >= 3
149
+ ? "translateX(-50%) translateY(0)"
150
+ : "translateX(-50%) translateY(30px)",
151
+ opacity: step >= 3 ? 1 : 0,
152
+ transition: trans(),
153
+ padding: "16px 44px", borderRadius: 14,
154
+ background: "linear-gradient(90deg, #dc2626 0%, #16a34a 100%)",
155
+ boxShadow: "0 8px 28px rgba(0,0,0,0.22)",
156
+ whiteSpace: "nowrap",
157
+ }}>
158
+ <span style={{ fontSize: 26, fontWeight: 900, color: "#fff", letterSpacing: 0.3 }}>
159
+ SRCA bridges this gap automatically
160
+ </span>
161
+ </div>
162
+ </div>
163
+ );
164
+ }
@@ -0,0 +1,70 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { CANVAS, vis, useRevealStep } from "../../../core/presets.ts";
4
+
5
+ export default function DemoBigText({ Reveal }: SlideComponentProps) {
6
+ const ref = useRevealStep(Reveal);
7
+ const step = ref[0], ready = ref[1], instant = ref[2];
8
+ const numRef = React.useRef<HTMLDivElement>(null);
9
+
10
+ React.useEffect(function () {
11
+ if (!ready || step < 0) return;
12
+ const anime = (window as any).anime;
13
+ const el = numRef.current;
14
+ if (!el) return;
15
+ if (instant || !anime || !anime.animate) {
16
+ el.style.transform = "scale(1)";
17
+ el.style.opacity = "1";
18
+ return;
19
+ }
20
+ anime.animate(el, {
21
+ scale: [0.9, 1],
22
+ opacity: [0, 1],
23
+ duration: 700,
24
+ ease: "spring(1, 80, 12, 0)",
25
+ });
26
+ }, [ready, instant]);
27
+
28
+ return (
29
+ <div style={{
30
+ width: "100%", height: "100%", boxSizing: "border-box",
31
+ background: "#ffffff", fontFamily: CANVAS.fontFamily,
32
+ display: "flex", flexDirection: "column",
33
+ alignItems: "center", justifyContent: "center",
34
+ opacity: ready ? 1 : 0, transition: "opacity 0.2s ease",
35
+ }}>
36
+ <div
37
+ ref={numRef}
38
+ style={{
39
+ fontSize: 120, fontWeight: 800, color: "#4361ee",
40
+ lineHeight: 1, letterSpacing: -2,
41
+ }}
42
+ >
43
+ 94.2%
44
+ </div>
45
+
46
+ <div style={{
47
+ marginTop: 28, fontSize: 28, fontWeight: 300, color: "#8b8b8b",
48
+ letterSpacing: 0.3, textAlign: "center",
49
+ ...vis(step, 1, instant),
50
+ }}>
51
+ Top-1 accuracy on 3 benchmark suites
52
+ </div>
53
+
54
+ <div style={{
55
+ marginTop: 48, display: "flex", flexDirection: "column",
56
+ alignItems: "center", gap: 14,
57
+ ...vis(step, 2, instant),
58
+ }}>
59
+ <div style={{ width: 200, height: 1, background: "#d8d8d8" }} />
60
+ <div style={{
61
+ fontSize: 17, fontWeight: 400, color: "#9a9a9a",
62
+ display: "flex", alignItems: "baseline", gap: 12,
63
+ }}>
64
+ <span>vs. 78.3% best baseline</span>
65
+ <span style={{ color: "#2e9e5b", fontWeight: 700 }}>+15.9%</span>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,118 @@
1
+ import React from "react";
2
+ import { Zap, Shield, Layers } from "lucide-react";
3
+ import { CANVAS, useRevealStep } from "../../../core/presets.ts";
4
+ import type { SlideComponentProps } from "../../../core/types.ts";
5
+
6
+ const CARDS = [
7
+ { Icon: Zap, title: "Speed", subtitle: "Fast by default", stat: "12ms", desc: "Sub-frame response times keep every interaction instant and fluid." },
8
+ { Icon: Shield, title: "Security", subtitle: "Locked down", stat: "0 CVE", desc: "End-to-end encryption and zero open vulnerabilities across the stack." },
9
+ { Icon: Layers, title: "Scale", subtitle: "Grows with you", stat: "10x", desc: "Horizontal scaling absorbs traffic spikes without a redeploy." },
10
+ ];
11
+
12
+ export default function DemoCardFlip({ Reveal }: SlideComponentProps) {
13
+ const ref = useRevealStep(Reveal);
14
+ const step = ref[0], ready = ref[1], instant = ref[2];
15
+
16
+ return (
17
+ <div
18
+ style={{
19
+ width: "100%",
20
+ height: "100%",
21
+ display: "flex",
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ gap: 40,
25
+ fontFamily: CANVAS.fontFamily,
26
+ opacity: ready ? 1 : 0,
27
+ transition: "opacity 0.2s ease",
28
+ }}
29
+ >
30
+ {CARDS.map(function (card, i) {
31
+ const flipped = step >= i;
32
+ const Icon = card.Icon;
33
+ return (
34
+ <div
35
+ key={i}
36
+ style={{
37
+ width: 280,
38
+ height: 350,
39
+ perspective: 1200,
40
+ }}
41
+ >
42
+ <div
43
+ style={{
44
+ position: "relative",
45
+ width: "100%",
46
+ height: "100%",
47
+ transformStyle: "preserve-3d",
48
+ transform: flipped ? "rotateY(180deg)" : "rotateY(0deg)",
49
+ transition: instant ? "none" : "transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1)",
50
+ }}
51
+ >
52
+ {/* Front */}
53
+ <div
54
+ style={{
55
+ position: "absolute",
56
+ inset: 0,
57
+ backfaceVisibility: "hidden",
58
+ WebkitBackfaceVisibility: "hidden",
59
+ borderRadius: 16,
60
+ background: "#fff",
61
+ boxShadow: "0 12px 32px rgba(49,49,49,0.16)",
62
+ display: "flex",
63
+ flexDirection: "column",
64
+ alignItems: "center",
65
+ justifyContent: "center",
66
+ gap: 18,
67
+ }}
68
+ >
69
+ <div
70
+ style={{
71
+ width: 96,
72
+ height: 96,
73
+ borderRadius: "50%",
74
+ background: "#eef1ff",
75
+ display: "flex",
76
+ alignItems: "center",
77
+ justifyContent: "center",
78
+ }}
79
+ >
80
+ <Icon size={52} color="#4361ee" strokeWidth={2} />
81
+ </div>
82
+ <div style={{ fontSize: 30, fontWeight: 900, color: "#1d3557" }}>{card.title}</div>
83
+ <div style={{ fontSize: 16, fontWeight: 600, color: "#767676" }}>{card.subtitle}</div>
84
+ </div>
85
+
86
+ {/* Back */}
87
+ <div
88
+ style={{
89
+ position: "absolute",
90
+ inset: 0,
91
+ backfaceVisibility: "hidden",
92
+ WebkitBackfaceVisibility: "hidden",
93
+ transform: "rotateY(180deg)",
94
+ borderRadius: 16,
95
+ background: "linear-gradient(145deg, #4361ee, #3a0ca3)",
96
+ boxShadow: "0 12px 32px rgba(58,12,163,0.32)",
97
+ color: "#fff",
98
+ display: "flex",
99
+ flexDirection: "column",
100
+ alignItems: "center",
101
+ justifyContent: "center",
102
+ gap: 16,
103
+ padding: "0 28px",
104
+ boxSizing: "border-box",
105
+ textAlign: "center",
106
+ }}
107
+ >
108
+ <div style={{ fontSize: 52, fontWeight: 900, lineHeight: 1 }}>{card.stat}</div>
109
+ <div style={{ fontSize: 22, fontWeight: 800 }}>{card.title}</div>
110
+ <div style={{ fontSize: 16, fontWeight: 500, lineHeight: 1.5, opacity: 0.92 }}>{card.desc}</div>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ );
115
+ })}
116
+ </div>
117
+ );
118
+ }