@edwinvakayil/calligraphy 1.3.0 → 1.4.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.
package/dist/index.esm.js CHANGED
@@ -127,55 +127,250 @@ function preloadFonts(families) {
127
127
 
128
128
  const STYLE_ID = "rts-hero-animations";
129
129
  const CSS = `
130
- @keyframes rts-rise { from{opacity:0;transform:translateY(32px)} to{opacity:1;transform:translateY(0)} }
131
- @keyframes rts-clip { from{clip-path:inset(0 100% 0 0)} to{clip-path:inset(0 0% 0 0)} }
132
- @keyframes rts-pop { 0%{opacity:0;transform:scale(0.75)} 60%{opacity:1;transform:scale(1.04)} 100%{transform:scale(1)} }
133
- @keyframes rts-blur { from{opacity:0;filter:blur(14px);transform:scale(1.04)} to{opacity:1;filter:blur(0);transform:scale(1)} }
134
- @keyframes rts-flip { from{opacity:0;transform:perspective(600px) rotateX(30deg) translateY(20px)} to{opacity:1;transform:perspective(600px) rotateX(0) translateY(0)} }
135
- @keyframes rts-swipe { from{opacity:0;transform:translateX(60px)} to{opacity:1;transform:translateX(0)} }
136
- @keyframes rts-bounce { 0%{opacity:0;transform:translateY(-60px)} 60%{opacity:1;transform:translateY(10px)} 80%{transform:translateY(-5px)} 100%{transform:translateY(0)} }
137
- @keyframes rts-type { from{width:0} to{width:100%} }
138
- @keyframes rts-blink { 50%{border-color:transparent} }
139
- @keyframes rts-word-rise { from{opacity:0;transform:translateY(24px)} to{opacity:1;transform:translateY(0)} }
140
- @keyframes rts-letter-in { from{opacity:0;transform:translateX(-16px) rotate(-4deg)} to{opacity:1;transform:none} }
141
-
142
- /* ── New modern animations ─────────────────────────────────────────────── */
143
-
144
- @keyframes rts-velvet { from{opacity:0;transform:translate(-12px,20px) skewX(4deg)} to{opacity:1;transform:translate(0,0) skewX(0deg)} }
145
- @keyframes rts-curtain { from{clip-path:inset(0 0 100% 0)} to{clip-path:inset(0 0 0% 0)} }
146
- @keyframes rts-morph { 0%{opacity:0;transform:scaleY(0.3) scaleX(1.3) translateY(10px)} 60%{opacity:1;transform:scaleY(1.08) scaleX(0.97)} 100%{transform:scaleY(1) scaleX(1)} }
147
- @keyframes rts-ground { from{transform:translateY(110%);opacity:0} to{transform:translateY(0);opacity:1} }
148
- @keyframes rts-cascade { from{opacity:0;transform:translateY(-28px) translateX(10px) rotate(8deg)} to{opacity:1;transform:none} }
149
- @keyframes rts-spotlight { 0%{opacity:0;letter-spacing:0.3em;transform:scaleX(1.15)} 100%{opacity:1;letter-spacing:-0.03em;transform:scaleX(1)} }
150
- @keyframes rts-ink { 0%{opacity:0;transform:translateY(6px) scale(0.96)} 100%{opacity:1;transform:translateY(0) scale(1)} }
151
- @keyframes rts-hinge { from{opacity:0;transform:perspective(400px) rotateY(-40deg) translateX(-20px)} to{opacity:1;transform:perspective(400px) rotateY(0) translateX(0)} }
152
- @keyframes rts-stretch { 0%{opacity:0;transform:scaleX(0.05)} 60%{transform:scaleX(1.04)} 100%{opacity:1;transform:scaleX(1)} }
153
- @keyframes rts-peel { from{clip-path:inset(100% 0 0 0)} to{clip-path:inset(0% 0 0 0)} }
154
-
155
- /* ── Whole-element classes (no splitting needed) ───────────────────────── */
156
- .rts-rise { animation: rts-rise 0.9s cubic-bezier(0.16,1,0.3,1) both }
157
- .rts-clip { animation: rts-clip 1.1s cubic-bezier(0.77,0,0.18,1) both }
158
- .rts-pop { animation: rts-pop 0.7s cubic-bezier(0.34,1.56,0.64,1) both }
159
- .rts-blur { animation: rts-blur 1s cubic-bezier(0.16,1,0.3,1) both }
160
- .rts-flip { animation: rts-flip 0.9s cubic-bezier(0.16,1,0.3,1) both; transform-origin: center bottom }
161
- .rts-swipe { animation: rts-swipe 0.8s cubic-bezier(0.16,1,0.3,1) both }
162
- .rts-bounce { animation: rts-bounce 0.9s cubic-bezier(0.36,0.07,0.19,0.97) both }
163
- .rts-typewriter { overflow: hidden; white-space: nowrap; border-right: 2px solid currentColor; width: 0; animation: rts-type 1.6s steps(22,end) both, rts-blink 0.7s step-end 1.6s 3 }
164
- .rts-morph { animation: rts-morph 0.8s cubic-bezier(0.34,1.56,0.64,1) both }
165
- .rts-spotlight { animation: rts-spotlight 1s cubic-bezier(0.16,1,0.3,1) both }
166
- .rts-stretch { animation: rts-stretch 0.9s cubic-bezier(0.34,1.56,0.64,1) both }
167
-
168
- /* ── Per-word / per-character span classes ─────────────────────────────── */
169
- .rts-word { display:inline-block;opacity:0;transform:translateY(24px);animation:rts-word-rise 0.7s cubic-bezier(0.16,1,0.3,1) both }
170
- .rts-letter { display:inline-block;opacity:0;transform:translateX(-16px) rotate(-4deg);animation:rts-letter-in 0.5s cubic-bezier(0.16,1,0.3,1) both }
171
- .rts-velvet-word { display:inline-block;opacity:0;animation:rts-velvet 0.65s cubic-bezier(0.16,1,0.3,1) both }
172
- .rts-curtain-word { display:inline-block;overflow:hidden;animation:rts-curtain 0.7s cubic-bezier(0.77,0,0.18,1) both }
173
- .rts-ground-wrap { display:inline-block;overflow:hidden;vertical-align:bottom }
174
- .rts-ground-inner { display:inline-block;animation:rts-ground 0.65s cubic-bezier(0.16,1,0.3,1) both }
175
- .rts-cascade-ch { display:inline-block;opacity:0;animation:rts-cascade 0.45s cubic-bezier(0.34,1.56,0.64,1) both }
176
- .rts-ink-word { display:inline-block;opacity:0;animation:rts-ink 0.9s cubic-bezier(0.16,1,0.3,1) both }
177
- .rts-hinge-word { display:inline-block;opacity:0;transform-origin:left center;animation:rts-hinge 0.6s cubic-bezier(0.16,1,0.3,1) both }
178
- .rts-peel-word { display:inline-block;overflow:hidden;animation:rts-peel 0.6s cubic-bezier(0.77,0,0.18,1) both }
130
+ /* ─── Easing tokens (reused across all keyframes) ──────────────────────────
131
+ All durations are tuned for perceptual smoothness:
132
+ - spring = cubic-bezier(0.16,1,0.3,1) soft, natural overshoot
133
+ - snap = cubic-bezier(0.77,0,0.18,1) fast-in, crisp-out
134
+ - bounce = cubic-bezier(0.34,1.56,0.64,1) elastic overshoot
135
+ - cinema = cubic-bezier(0.4,0,0.2,1) material easing
136
+ ──────────────────────────────────────────────────────────────────────── */
137
+
138
+ /* ── Batch 1 — originals ─────────────────────────────────────────────────── */
139
+ @keyframes rts-rise {from{opacity:0;transform:translateY(32px)}to{opacity:1;transform:translateY(0)}}
140
+ @keyframes rts-clip {from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0% 0 0)}}
141
+ @keyframes rts-pop {0%{opacity:0;transform:scale(0.75)}60%{opacity:1;transform:scale(1.04)}100%{transform:scale(1)}}
142
+ @keyframes rts-blur {from{opacity:0;filter:blur(14px);transform:scale(1.04)}to{opacity:1;filter:blur(0);transform:scale(1)}}
143
+ @keyframes rts-flip {from{opacity:0;transform:perspective(600px) rotateX(30deg) translateY(20px)}to{opacity:1;transform:perspective(600px) rotateX(0) translateY(0)}}
144
+ @keyframes rts-swipe {from{opacity:0;transform:translateX(60px)}to{opacity:1;transform:translateX(0)}}
145
+ @keyframes rts-bounce {0%{opacity:0;transform:translateY(-60px)}60%{opacity:1;transform:translateY(10px)}80%{transform:translateY(-5px)}100%{transform:translateY(0)}}
146
+ @keyframes rts-type {from{width:0}to{width:100%}}
147
+ @keyframes rts-blink {50%{border-color:transparent}}
148
+ @keyframes rts-word-rise {from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:translateY(0)}}
149
+ @keyframes rts-letter-in {from{opacity:0;transform:translateX(-16px) rotate(-4deg)}to{opacity:1;transform:none}}
150
+
151
+ /* ── Batch 2 modern ────────────────────────────────────────────────────── */
152
+ @keyframes rts-velvet {from{opacity:0;transform:translate(-12px,20px) skewX(4deg)}to{opacity:1;transform:none}}
153
+ @keyframes rts-curtain {from{clip-path:inset(0 0 100% 0)}to{clip-path:inset(0 0 0% 0)}}
154
+ @keyframes rts-morph {0%{opacity:0;transform:scaleY(0.3) scaleX(1.3) translateY(10px)}60%{opacity:1;transform:scaleY(1.08) scaleX(0.97)}100%{transform:none}}
155
+ @keyframes rts-ground {from{transform:translateY(110%);opacity:0}to{transform:translateY(0);opacity:1}}
156
+ @keyframes rts-cascade {from{opacity:0;transform:translateY(-28px) translateX(10px) rotate(8deg)}to{opacity:1;transform:none}}
157
+ @keyframes rts-spotlight {0%{opacity:0;letter-spacing:0.3em;transform:scaleX(1.15)}100%{opacity:1;letter-spacing:-0.03em;transform:scaleX(1)}}
158
+ @keyframes rts-ink {0%{opacity:0;transform:translateY(6px) scale(0.96)}100%{opacity:1;transform:none}}
159
+ @keyframes rts-hinge {from{opacity:0;transform:perspective(400px) rotateY(-40deg) translateX(-20px)}to{opacity:1;transform:perspective(400px) rotateY(0) translateX(0)}}
160
+ @keyframes rts-stretch {0%{opacity:0;transform:scaleX(0.05)}60%{transform:scaleX(1.04)}100%{opacity:1;transform:none}}
161
+ @keyframes rts-peel {from{clip-path:inset(100% 0 0 0)}to{clip-path:inset(0% 0 0 0)}}
162
+ @keyframes rts-fold {from{opacity:0;transform:rotateZ(-8deg) translateY(18px)}to{opacity:1;transform:none}}
163
+ @keyframes rts-shear {from{opacity:0;transform:skewY(8deg) translateY(12px)}to{opacity:1;transform:none}}
164
+ /* ripple elastic scale from compressed point, outward wave delay */
165
+ @keyframes rts-ripple-w {
166
+ 0% {opacity:0;transform:scale(0.4) translateY(20px)}
167
+ 55% {opacity:1;transform:scale(1.08) translateY(-3px)}
168
+ 75% {transform:scale(0.97) translateY(1px)}
169
+ 100%{opacity:1;transform:scale(1) translateY(0)}
170
+ }
171
+ /* cinch character pinches on scaleX then snaps open with skew */
172
+ @keyframes rts-cinch {
173
+ 0% {opacity:0;transform:scaleX(0) skewX(20deg)}
174
+ 50% {opacity:1;transform:scaleX(1.12) skewX(-4deg)}
175
+ 75% {transform:scaleX(0.95) skewX(1deg)}
176
+ 100%{opacity:1;transform:scaleX(1) skewX(0)}
177
+ }
178
+ /* tiltrise words rise while untilting from a sideways lean */
179
+ @keyframes rts-tiltrise {
180
+ 0% {opacity:0;transform:translateY(36px) rotate(-6deg) skewX(8deg)}
181
+ 65% {opacity:1;transform:translateY(-3px) rotate(0.5deg) skewX(-1deg)}
182
+ 100%{opacity:1;transform:translateY(0) rotate(0) skewX(0)}
183
+ }
184
+ @keyframes rts-cardflip {from{opacity:0;transform:perspective(300px) rotateX(-90deg)}to{opacity:1;transform:perspective(300px) rotateX(0)}}
185
+ @keyframes rts-conv-l {from{opacity:0;transform:translateX(-40px)}to{opacity:1;transform:translateX(0)}}
186
+ @keyframes rts-conv-r {from{opacity:0;transform:translateX( 40px)}to{opacity:1;transform:translateX(0)}}
187
+ @keyframes rts-splitrise-t{from{opacity:0;transform:translateY(-24px)}to{opacity:1;transform:translateY(0)}}
188
+ @keyframes rts-splitrise-b{from{opacity:0;transform:translateY( 24px)}to{opacity:1;transform:translateY(0)}}
189
+
190
+ /* ── Batch 3 — new mechanics ─────────────────────────────────────────────── */
191
+
192
+ /* tectonic: words slam from alternating sides with skew — unique X+skew combo */
193
+ @keyframes rts-tec-l{from{opacity:0;transform:translateX(-55px) skewX(-7deg)}to{opacity:1;transform:none}}
194
+ @keyframes rts-tec-r{from{opacity:0;transform:translateX( 55px) skewX( 7deg)}to{opacity:1;transform:none}}
195
+
196
+ /* stratify: each word enters from deep Z with blur — different from blur (which is whole-el) */
197
+ @keyframes rts-strat{
198
+ from{opacity:0;transform:perspective(900px) translateZ(-200px) rotateX(10deg);filter:blur(6px)}
199
+ to {opacity:1;transform:perspective(900px) translateZ(0) rotateX(0); filter:blur(0)}
200
+ }
201
+
202
+ /* unfurl: line expands from center — horizontal center-clip, not edge-clip */
203
+ @keyframes rts-unfurl{from{clip-path:inset(0 50% 0 50%)}to{clip-path:inset(0 0% 0 0%)}}
204
+
205
+ /* gravityWell: chars fall from sky with precise physics micro-rotation */
206
+ @keyframes rts-gwell{
207
+ 0% {opacity:0;transform:translateY(-72px) scale(1.18) rotate(-3.5deg)}
208
+ 52% {opacity:1;transform:translateY(7px) scale(0.98) rotate(0.4deg)}
209
+ 78% {transform:translateY(-3px) scale(1.005) rotate(0)}
210
+ 100%{opacity:1;transform:none}
211
+ }
212
+
213
+ /* orbit: words grow from dot, rotating on Z — scale+rotation combo, unique */
214
+ @keyframes rts-orbit{
215
+ from{opacity:0;transform:scale(0.12) rotate(-28deg)}
216
+ 58% {opacity:1;transform:scale(1.07) rotate(2.5deg)}
217
+ 100%{opacity:1;transform:none}
218
+ }
219
+
220
+ /* liquid: per-word squash then overshoot spring — scaleY+scaleX cross-axis */
221
+ @keyframes rts-liquid{
222
+ 0% {opacity:0;transform:scaleY(0.08) scaleX(1.5) translateY(28px)}
223
+ 40% {opacity:1;transform:scaleY(1.12) scaleX(0.94) translateY(-4px)}
224
+ 65% {transform:scaleY(0.97) scaleX(1.015)}
225
+ 100%{opacity:1;transform:none}
226
+ }
227
+
228
+ /* noiseFade: 3 random opacity waveforms per word — signal-lock feel */
229
+ @keyframes rts-nf0{0%{opacity:0}22%{opacity:.7}44%{opacity:.1}66%{opacity:.95}88%{opacity:.4}100%{opacity:1}}
230
+ @keyframes rts-nf1{0%{opacity:0}18%{opacity:.8}38%{opacity:.15}58%{opacity:1}78%{opacity:.5}100%{opacity:1}}
231
+ @keyframes rts-nf2{0%{opacity:0}28%{opacity:.3}50%{opacity:.9}70%{opacity:.05}88%{opacity:.85}100%{opacity:1}}
232
+
233
+ /* slab: scaleX from-left stamp — fastest entrance, print-press energy */
234
+ @keyframes rts-slab{
235
+ 0% {opacity:0;transform:scaleX(0) skewX(8deg)}
236
+ 52% {opacity:1;transform:scaleX(1.05) skewX(-1deg)}
237
+ 100%{opacity:1;transform:none}
238
+ }
239
+
240
+ /* thread: chars ride individual sine-wave Y offsets set via CSS var */
241
+ @keyframes rts-thread{from{opacity:0;transform:translateY(var(--ty,18px))}to{opacity:1;transform:translateY(0)}}
242
+
243
+ /* billboard: whole-line rotateY — different axis from flip (rotateX) */
244
+ @keyframes rts-billboard{
245
+ from{opacity:0;transform:perspective(800px) rotateY(-32deg) translateX(-24px);filter:blur(3px)}
246
+ to {opacity:1;transform:perspective(800px) rotateY(0) translateX(0); filter:blur(0)}
247
+ }
248
+
249
+ /* ═══════════════════════════════════════════════════════════════════════════
250
+ CLASS DECLARATIONS — all animations use animation-fill-mode: both so
251
+ the element stays at its final state without needing JS cleanup.
252
+ ═══════════════════════════════════════════════════════════════════════════ */
253
+
254
+ /* ── Batch 1 whole-element ───────────────────────────────────────────────── */
255
+ .rts-rise {animation:rts-rise 0.9s cubic-bezier(0.16,1,0.3,1) both}
256
+ .rts-clip {animation:rts-clip 1.1s cubic-bezier(0.77,0,0.18,1) both}
257
+ .rts-pop {animation:rts-pop 0.7s cubic-bezier(0.34,1.56,0.64,1) both}
258
+ .rts-blur {animation:rts-blur 1s cubic-bezier(0.16,1,0.3,1) both}
259
+ .rts-flip {animation:rts-flip 0.9s cubic-bezier(0.16,1,0.3,1) both;transform-origin:center bottom}
260
+ .rts-swipe {animation:rts-swipe 0.8s cubic-bezier(0.16,1,0.3,1) both}
261
+ .rts-bounce {animation:rts-bounce 0.9s cubic-bezier(0.36,0.07,0.19,0.97) both}
262
+ .rts-typewriter {overflow:hidden;white-space:nowrap;border-right:2px solid currentColor;width:0;animation:rts-type 1.6s steps(22,end) both,rts-blink 0.7s step-end 1.6s 3}
263
+
264
+ /* ── Batch 2 whole-element ───────────────────────────────────────────────── */
265
+ .rts-morph {animation:rts-morph 0.8s cubic-bezier(0.34,1.56,0.64,1) both}
266
+ .rts-spotlight {animation:rts-spotlight 1s cubic-bezier(0.16,1,0.3,1) both}
267
+ .rts-stretch {animation:rts-stretch 0.9s cubic-bezier(0.34,1.56,0.64,1) both}
268
+
269
+ /* ── Batch 3 whole-element ───────────────────────────────────────────────── */
270
+ .rts-unfurl {animation:rts-unfurl 0.95s cubic-bezier(0.77,0,0.18,1) both}
271
+ .rts-billboard {animation:rts-billboard 0.95s cubic-bezier(0.16,1,0.3,1) both;transform-origin:left center}
272
+
273
+ /* ── Per-word / per-char spans (batch 1+2) ───────────────────────────────── */
274
+ .rts-word {display:inline-block;opacity:0;transform:translateY(24px);animation:rts-word-rise 0.7s cubic-bezier(0.16,1,0.3,1) both}
275
+ .rts-letter {display:inline-block;opacity:0;transform:translateX(-16px) rotate(-4deg);animation:rts-letter-in 0.5s cubic-bezier(0.16,1,0.3,1) both}
276
+ .rts-velvet-word {display:inline-block;opacity:0;animation:rts-velvet 0.65s cubic-bezier(0.16,1,0.3,1) both}
277
+ .rts-curtain-word {display:inline-block;overflow:hidden;animation:rts-curtain 0.7s cubic-bezier(0.77,0,0.18,1) both}
278
+ .rts-ground-wrap {display:inline-block;overflow:hidden;vertical-align:bottom}
279
+ .rts-ground-inner {display:inline-block;animation:rts-ground 0.65s cubic-bezier(0.16,1,0.3,1) both}
280
+ .rts-cascade-ch {display:inline-block;opacity:0;animation:rts-cascade 0.45s cubic-bezier(0.34,1.56,0.64,1) both}
281
+ .rts-ink-word {display:inline-block;opacity:0;animation:rts-ink 0.9s cubic-bezier(0.16,1,0.3,1) both}
282
+ .rts-hinge-word {display:inline-block;opacity:0;transform-origin:left center;animation:rts-hinge 0.6s cubic-bezier(0.16,1,0.3,1) both}
283
+ .rts-peel-word {display:inline-block;overflow:hidden;animation:rts-peel 0.6s cubic-bezier(0.77,0,0.18,1) both}
284
+ .rts-fold-word {display:inline-block;opacity:0;transform-origin:center bottom;animation:rts-fold 0.6s cubic-bezier(0.34,1.4,0.64,1) both}
285
+ .rts-shear-word {display:inline-block;opacity:0;animation:rts-shear 0.65s cubic-bezier(0.16,1,0.3,1) both}
286
+ .rts-ripple-word {display:inline-block;opacity:0;animation:rts-ripple-w 0.75s cubic-bezier(0.34,1.4,0.64,1) both}
287
+ .rts-cinch-ch {display:inline-block;opacity:0;transform-origin:center;animation:rts-cinch 0.55s cubic-bezier(0.34,1.2,0.64,1) both}
288
+ .rts-tiltrise-word{display:inline-block;opacity:0;animation:rts-tiltrise 0.8s cubic-bezier(0.16,1,0.3,1) both}
289
+ .rts-cardflip-ch {display:inline-block;opacity:0;transform-origin:center bottom;animation:rts-cardflip 0.4s cubic-bezier(0.34,1.4,0.64,1) both}
290
+ .rts-conv-l {display:inline-block;opacity:0;animation:rts-conv-l 0.7s cubic-bezier(0.16,1,0.3,1) both}
291
+ .rts-conv-r {display:inline-block;opacity:0;animation:rts-conv-r 0.7s cubic-bezier(0.16,1,0.3,1) both}
292
+ .rts-splitrise-t {display:inline-block;opacity:0;animation:rts-splitrise-t 0.65s cubic-bezier(0.16,1,0.3,1) both}
293
+ .rts-splitrise-b {display:inline-block;opacity:0;animation:rts-splitrise-b 0.65s cubic-bezier(0.16,1,0.3,1) both}
294
+
295
+ /* ── Per-word / per-char spans (batch 3) ────────────────────────────────── */
296
+ .rts-tec-l {display:inline-block;opacity:0;animation:rts-tec-l 0.65s cubic-bezier(0.16,1,0.3,1) both}
297
+ .rts-tec-r {display:inline-block;opacity:0;animation:rts-tec-r 0.65s cubic-bezier(0.16,1,0.3,1) both}
298
+ .rts-strat-word {display:inline-block;opacity:0;animation:rts-strat 0.85s cubic-bezier(0.16,1,0.3,1) both}
299
+ .rts-gwell-ch {display:inline-block;opacity:0;animation:rts-gwell 0.62s cubic-bezier(0.36,0.07,0.19,0.97) both}
300
+ .rts-orbit-word {display:inline-block;opacity:0;transform-origin:center;animation:rts-orbit 0.65s cubic-bezier(0.34,1.4,0.64,1) both}
301
+ .rts-liquid-word{display:inline-block;opacity:0;transform-origin:center bottom;animation:rts-liquid 0.72s cubic-bezier(0.34,1.56,0.64,1) both}
302
+ .rts-nf0 {display:inline-block;opacity:0;animation:rts-nf0 0.9s ease both}
303
+ .rts-nf1 {display:inline-block;opacity:0;animation:rts-nf1 0.9s ease both}
304
+ .rts-nf2 {display:inline-block;opacity:0;animation:rts-nf2 0.9s ease both}
305
+ .rts-slab-word {display:inline-block;opacity:0;transform-origin:left center;animation:rts-slab 0.5s cubic-bezier(0.77,0,0.18,1) both}
306
+ .rts-thread-ch {display:inline-block;opacity:0;animation:rts-thread 0.55s cubic-bezier(0.16,1,0.3,1) both}
307
+
308
+ /* ── Batch 7 — 8 genuinely new mechanics ────────────────────────────────── */
309
+
310
+ /* glassReveal — backdrop-filter blur evaporates as text solidifies */
311
+ @keyframes rts-glass {
312
+ 0% { opacity:0; filter:blur(0px); backdrop-filter:blur(20px); transform:scale(1.03) }
313
+ 35% { opacity:0.6; filter:blur(2px) }
314
+ 100%{ opacity:1; filter:blur(0px); backdrop-filter:blur(0px); transform:scale(1) }
315
+ }
316
+ .rts-glass { animation:rts-glass 1.2s cubic-bezier(0.16,1,0.3,1) both }
317
+
318
+ /* wordPop — per-word springs from 0 at its own center, pure scale */
319
+ @keyframes rts-wpop {
320
+ 0% { opacity:0; transform:scale(0) }
321
+ 55% { opacity:1; transform:scale(1.08) }
322
+ 75% { transform:scale(0.97) }
323
+ 100%{ transform:scale(1) }
324
+ }
325
+ .rts-wpop-word { display:inline-block; opacity:0; animation:rts-wpop 0.55s cubic-bezier(0.34,1.56,0.64,1) both }
326
+
327
+ /* charDrop — pure gravity fall, no overshoot, no rotation */
328
+ @keyframes rts-cdrop {
329
+ 0% { opacity:0; transform:translateY(-48px) }
330
+ 100%{ opacity:1; transform:translateY(0) }
331
+ }
332
+ .rts-cdrop-ch { display:inline-block; opacity:0; animation:rts-cdrop 0.6s cubic-bezier(0.55,0,1,0.45) both }
333
+
334
+ /* scanline — single-pixel horizontal clip expands to full height */
335
+ @keyframes rts-scan {
336
+ 0% { clip-path:inset(49% 0 49% 0); opacity:0 }
337
+ 15% { opacity:1 }
338
+ 100%{ clip-path:inset(0% 0 0% 0) }
339
+ }
340
+ .rts-scan { animation:rts-scan 0.9s cubic-bezier(0.77,0,0.18,1) both }
341
+
342
+ /* chromaShift — RGB channel offsets collapse to zero */
343
+ @keyframes rts-chroma {
344
+ 0% { opacity:0; text-shadow: -8px 0 0 rgba(255,0,80,0.7), 8px 0 0 rgba(0,200,255,0.7) }
345
+ 40% { opacity:1; text-shadow: -4px 0 0 rgba(255,0,80,0.35), 4px 0 0 rgba(0,200,255,0.35) }
346
+ 100%{ text-shadow:none }
347
+ }
348
+ .rts-chroma { animation:rts-chroma 1s cubic-bezier(0.16,1,0.3,1) both }
349
+
350
+ /* wordFade — pure cross-dissolve with warmth scale, no Y movement */
351
+ @keyframes rts-wfade {
352
+ 0% { opacity:0; transform:scale(0.97) }
353
+ 100%{ opacity:1; transform:scale(1) }
354
+ }
355
+ .rts-wfade-word { display:inline-block; opacity:0; animation:rts-wfade 0.9s cubic-bezier(0.4,0,0.2,1) both }
356
+
357
+ /* rotateIn — full Y-axis card flip per word */
358
+ @keyframes rts-rotatein {
359
+ 0% { opacity:0; transform:perspective(500px) rotateY(90deg) }
360
+ 55% { opacity:1; transform:perspective(500px) rotateY(-8deg) }
361
+ 100%{ transform:perspective(500px) rotateY(0deg) }
362
+ }
363
+ .rts-rotatein-word { display:inline-block; opacity:0; transform-origin:center; animation:rts-rotatein 0.6s cubic-bezier(0.34,1.4,0.64,1) both }
364
+
365
+ /* pressIn — presses down to 0.92 then springs outward past 1 */
366
+ @keyframes rts-pressin {
367
+ 0% { opacity:0; transform:scale(0.5) }
368
+ 30% { opacity:1; transform:scale(0.92) }
369
+ 60% { transform:scale(1.06) }
370
+ 80% { transform:scale(0.98) }
371
+ 100%{ transform:scale(1) }
372
+ }
373
+ .rts-pressin-word { display:inline-block; opacity:0; animation:rts-pressin 0.65s cubic-bezier(0.34,1.56,0.64,1) both }
179
374
  `;
180
375
  function injectAnimationStyles() {
181
376
  if (typeof document === "undefined")
@@ -187,7 +382,7 @@ function injectAnimationStyles() {
187
382
  style.textContent = CSS;
188
383
  document.head.appendChild(style);
189
384
  }
190
- // ─── Whole-element class map ──────────────────────────────────────────────────
385
+ // ─── Whole-element animation map ─────────────────────────────────────────────
191
386
  const WHOLE_CLASS_MAP = {
192
387
  rise: "rts-rise",
193
388
  clip: "rts-clip",
@@ -200,35 +395,35 @@ const WHOLE_CLASS_MAP = {
200
395
  morph: "rts-morph",
201
396
  spotlight: "rts-spotlight",
202
397
  stretch: "rts-stretch",
398
+ unfurl: "rts-unfurl",
399
+ billboard: "rts-billboard",
400
+ // Batch 7 — whole-element
401
+ glassReveal: "rts-glass",
402
+ scanline: "rts-scan",
403
+ chromaShift: "rts-chroma",
203
404
  };
204
- /** Returns the CSS class for whole-element animations, or "" for split ones. */
205
405
  function getAnimationClass(animation) {
206
406
  var _a;
207
407
  return (_a = WHOLE_CLASS_MAP[animation]) !== null && _a !== void 0 ? _a : "";
208
408
  }
209
- /** True if the animation needs the HTML to be split into word/char spans. */
210
409
  function isSplitAnimation(animation) {
211
410
  return !(animation in WHOLE_CLASS_MAP);
212
411
  }
213
- // ─── HTML builders for split animations ──────────────────────────────────────
214
- function wrapWords(html, cls, delayStep) {
412
+ // ─── HTML split builders ──────────────────────────────────────────────────────
413
+ function wrapWords(html, cls, step) {
215
414
  var _a;
216
415
  const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
217
- return tokens
218
- .map((tok, i) => {
219
- const delay = (i * delayStep).toFixed(2);
416
+ return tokens.map((tok, i) => {
417
+ const delay = (i * step).toFixed(2);
220
418
  if (tok.startsWith("<em>")) {
221
419
  return `<em><span class="${cls}" style="animation-delay:${delay}s">${tok.slice(4, -5)}</span></em>`;
222
420
  }
223
421
  return `<span class="${cls}" style="animation-delay:${delay}s">${tok}</span>`;
224
- })
225
- .join(" ");
422
+ }).join(" ");
226
423
  }
227
- function wrapChars(html, cls, delayStep) {
424
+ function wrapChars(html, cls, step) {
228
425
  const result = [];
229
- let inEm = false;
230
- let delay = 0;
231
- let i = 0;
426
+ let inEm = false, delay = 0, i = 0;
232
427
  while (i < html.length) {
233
428
  if (html.startsWith("<em>", i)) {
234
429
  inEm = true;
@@ -248,28 +443,96 @@ function wrapChars(html, cls, delayStep) {
248
443
  }
249
444
  const span = `<span class="${cls}" style="animation-delay:${delay.toFixed(2)}s">${ch}</span>`;
250
445
  result.push(inEm ? `<em>${span}</em>` : span);
251
- delay += delayStep;
446
+ delay += step;
252
447
  i++;
253
448
  }
254
449
  return result.join("");
255
450
  }
256
- function wrapGround(html, delayStep) {
451
+ function wrapGround(html, step) {
257
452
  var _a;
258
453
  const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
259
- return tokens
260
- .map((tok, i) => {
261
- const delay = (i * delayStep).toFixed(2);
262
- const inner = tok.startsWith("<em>")
263
- ? `<em>${tok.slice(4, -5)}</em>`
264
- : tok;
454
+ return tokens.map((tok, i) => {
455
+ const delay = (i * step).toFixed(2);
456
+ const inner = tok.startsWith("<em>") ? `<em>${tok.slice(4, -5)}</em>` : tok;
265
457
  return `<span class="rts-ground-wrap"><span class="rts-ground-inner" style="animation-delay:${delay}s">${inner}</span></span>`;
266
- })
267
- .join(" ");
458
+ }).join(" ");
459
+ }
460
+ function wrapTectonic(html) {
461
+ var _a;
462
+ const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
463
+ return tokens.map((tok, i) => {
464
+ const cls = i % 2 === 0 ? "rts-tec-l" : "rts-tec-r";
465
+ const delay = (i * 0.09).toFixed(2);
466
+ if (tok.startsWith("<em>")) {
467
+ return `<em><span class="${cls}" style="animation-delay:${delay}s">${tok.slice(4, -5)}</span></em>`;
468
+ }
469
+ return `<span class="${cls}" style="animation-delay:${delay}s">${tok}</span>`;
470
+ }).join(" ");
471
+ }
472
+ function wrapNoiseFade(html) {
473
+ var _a;
474
+ const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
475
+ return tokens.map((tok, i) => {
476
+ const cls = `rts-nf${i % 3}`;
477
+ const delay = (i * 0.12).toFixed(2);
478
+ if (tok.startsWith("<em>")) {
479
+ return `<em><span class="${cls}" style="animation-delay:${delay}s">${tok.slice(4, -5)}</span></em>`;
480
+ }
481
+ return `<span class="${cls}" style="animation-delay:${delay}s">${tok}</span>`;
482
+ }).join(" ");
483
+ }
484
+ function wrapConverge(html) {
485
+ var _a;
486
+ const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
487
+ const mid = Math.ceil(tokens.length / 2);
488
+ return tokens.map((tok, i) => {
489
+ const isLeft = i < mid;
490
+ const dist = isLeft ? mid - 1 - i : i - mid;
491
+ const delay = (dist * 0.07).toFixed(2);
492
+ const cls = isLeft ? "rts-conv-l" : "rts-conv-r";
493
+ const inner = tok.startsWith("<em>") ? `<em>${tok.slice(4, -5)}</em>` : tok;
494
+ return `<span class="${cls}" style="animation-delay:${delay}s">${inner}</span>`;
495
+ }).join(" ");
496
+ }
497
+ function wrapRipple(html) {
498
+ var _a;
499
+ const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
500
+ const mid = Math.floor(tokens.length / 2);
501
+ return tokens.map((tok, i) => {
502
+ const delay = (Math.abs(i - mid) * 0.1).toFixed(2);
503
+ if (tok.startsWith("<em>")) {
504
+ return `<em><span class="rts-ripple-word" style="animation-delay:${delay}s">${tok.slice(4, -5)}</span></em>`;
505
+ }
506
+ return `<span class="rts-ripple-word" style="animation-delay:${delay}s">${tok}</span>`;
507
+ }).join(" ");
508
+ }
509
+ function wrapSplitRise(html) {
510
+ var _a;
511
+ const tokens = (_a = html.match(/(<em>[\s\S]*?<\/em>|[^\s]+)/g)) !== null && _a !== void 0 ? _a : [];
512
+ return tokens.map((tok, i) => {
513
+ const cls = i % 2 === 0 ? "rts-splitrise-t" : "rts-splitrise-b";
514
+ const delay = (i * 0.08).toFixed(2);
515
+ if (tok.startsWith("<em>")) {
516
+ return `<em><span class="${cls}" style="animation-delay:${delay}s">${tok.slice(4, -5)}</span></em>`;
517
+ }
518
+ return `<span class="${cls}" style="animation-delay:${delay}s">${tok}</span>`;
519
+ }).join(" ");
520
+ }
521
+ /**
522
+ * Thread: each char gets a sine-wave Y offset injected as a CSS custom
523
+ * property --ty so the keyframe can read it. Must be post-processed by
524
+ * Typography after innerHTML is set — see applyThreadOffsets().
525
+ */
526
+ function wrapThread(html) {
527
+ return wrapChars(html, "rts-thread-ch", 0.04);
268
528
  }
529
+ // ─── Public API ───────────────────────────────────────────────────────────────
269
530
  function buildSplitHTML(animation, html) {
270
531
  switch (animation) {
532
+ // Batch 1
271
533
  case "stagger": return wrapWords(html, "rts-word", 0.07);
272
534
  case "letters": return wrapChars(html, "rts-letter", 0.04);
535
+ // Batch 2
273
536
  case "velvet": return wrapWords(html, "rts-velvet-word", 0.08);
274
537
  case "curtain": return wrapWords(html, "rts-curtain-word", 0.10);
275
538
  case "ground": return wrapGround(html, 0.09);
@@ -277,6 +540,29 @@ function buildSplitHTML(animation, html) {
277
540
  case "ink": return wrapWords(html, "rts-ink-word", 0.10);
278
541
  case "hinge": return wrapWords(html, "rts-hinge-word", 0.09);
279
542
  case "peel": return wrapWords(html, "rts-peel-word", 0.10);
543
+ case "fold": return wrapWords(html, "rts-fold-word", 0.09);
544
+ case "shear": return wrapWords(html, "rts-shear-word", 0.08);
545
+ case "ripple": return wrapRipple(html);
546
+ case "cinch": return wrapChars(html, "rts-cinch-ch", 0.048);
547
+ case "tiltrise": return wrapWords(html, "rts-tiltrise-word", 0.09);
548
+ case "cardFlip": return wrapChars(html, "rts-cardflip-ch", 0.045);
549
+ case "converge": return wrapConverge(html);
550
+ case "splitRise": return wrapSplitRise(html);
551
+ // Batch 3
552
+ case "tectonic": return wrapTectonic(html);
553
+ case "stratify": return wrapWords(html, "rts-strat-word", 0.10);
554
+ case "gravityWell": return wrapChars(html, "rts-gwell-ch", 0.05);
555
+ case "orbit": return wrapWords(html, "rts-orbit-word", 0.10);
556
+ case "liquid": return wrapWords(html, "rts-liquid-word", 0.09);
557
+ case "noiseFade": return wrapNoiseFade(html);
558
+ case "slab": return wrapWords(html, "rts-slab-word", 0.11);
559
+ case "thread": return wrapThread(html);
560
+ // Batch 7 — split
561
+ case "wordPop": return wrapWords(html, "rts-wpop-word", 0.07);
562
+ case "charDrop": return wrapChars(html, "rts-cdrop-ch", 0.04);
563
+ case "wordFade": return wrapWords(html, "rts-wfade-word", 0.09);
564
+ case "rotateIn": return wrapWords(html, "rts-rotatein-word", 0.08);
565
+ case "pressIn": return wrapWords(html, "rts-pressin-word", 0.08);
280
566
  default: return html;
281
567
  }
282
568
  }