animot-presenter 0.2.3 → 0.2.5

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/README.md CHANGED
@@ -472,7 +472,7 @@ animot-presenter .animot-controls { display: none; }
472
472
 
473
473
  ## Features
474
474
 
475
- - **Morphing animations** — Elements with the same ID across slides smoothly morph position, size, rotation, color, opacity, and border radius
475
+ - **Morphing animations** — Elements with the same ID across slides smoothly morph position, size, rotation, color, opacity, border radius, and CSS filters (blur, brightness, contrast, saturate, grayscale)
476
476
  - **Code highlighting** — Syntax highlighting via Shiki with typewriter, highlight-changes, and instant animation modes
477
477
  - **Shape morphing** — Rectangles, circles, triangles, stars, hexagons with smooth transitions
478
478
  - **Charts** — Animated bar, line, area, pie, and donut charts
@@ -483,7 +483,8 @@ animot-presenter .animot-controls { display: none; }
483
483
  - **Transitions** — Fade, slide, zoom, flip, and morphing (none) transition types
484
484
  - **Responsive** — Automatically scales to fit any container size
485
485
  - **Keyboard navigation** — Arrow keys, spacebar, Home/End
486
- - **Property sequencing** — Fine-grained control over which properties animate first
486
+ - **CSS filters** — Blur, brightness, contrast, saturate, and grayscale on any element, animated across slides
487
+ - **Property sequencing** — Fine-grained control over which properties animate first (including filters via the `blur` property sequence)
487
488
 
488
489
  ## JSON Schema
489
490
 
@@ -517,7 +518,12 @@ Animot JSON files follow this structure:
517
518
  "color": "#ffffff",
518
519
  "rotation": 0,
519
520
  "visible": true,
520
- "zIndex": 1
521
+ "zIndex": 1,
522
+ "blur": 0,
523
+ "brightness": 100,
524
+ "contrast": 100,
525
+ "saturate": 100,
526
+ "grayscale": 0
521
527
  }
522
528
  ]
523
529
  },
@@ -563,6 +569,32 @@ You can replace the base64 data URL with a remote or local URL:
563
569
 
564
570
  This lets you keep your JSON files lightweight by hosting images separately instead of embedding them as base64. The same applies to text elements with `backgroundImage` and background `image` fields.
565
571
 
572
+ ### CSS Filters
573
+
574
+ All element types support CSS filter properties that animate smoothly between slides:
575
+
576
+ ```json
577
+ {
578
+ "id": "hero-image",
579
+ "type": "image",
580
+ "blur": 5,
581
+ "brightness": 120,
582
+ "contrast": 110,
583
+ "saturate": 80,
584
+ "grayscale": 0
585
+ }
586
+ ```
587
+
588
+ | Property | Range | Default | Description |
589
+ |--------------|---------|---------|----------------------------------|
590
+ | `blur` | 0–20 | 0 | Gaussian blur in pixels |
591
+ | `brightness` | 0–200 | 100 | Brightness percentage (100 = normal) |
592
+ | `contrast` | 0–200 | 100 | Contrast percentage (100 = normal) |
593
+ | `saturate` | 0–200 | 100 | Saturation percentage (100 = normal) |
594
+ | `grayscale` | 0–100 | 0 | Grayscale percentage (0 = none) |
595
+
596
+ Set different filter values on the same element across slides to create smooth animated transitions (e.g., blur 10 on slide 1 to blur 0 on slide 2 for a reveal effect). Use the `blur` property sequence in `animationConfig.propertySequences` to control filter animation timing independently.
597
+
566
598
  > **Note:** Remote URLs must allow cross-origin requests (CORS) if served from a different domain.
567
599
 
568
600
  ## Bundle Size
@@ -30,6 +30,11 @@
30
30
  strokeWidth: TweenValue | null;
31
31
  shapeMorph: TweenValue | null;
32
32
  motionPathProgress: TweenValue | null;
33
+ blur: TweenValue;
34
+ brightness: TweenValue;
35
+ contrast: TweenValue;
36
+ saturate: TweenValue;
37
+ grayscale: TweenValue;
33
38
  }
34
39
 
35
40
  interface ShapeMorphState { fromType: string; toType: string; }
@@ -205,8 +210,26 @@
205
210
  textTypewriterState = new Map(textTypewriterState);
206
211
  }
207
212
 
208
- // Arrow draw animation action
209
- function animateStyledArrowDraw(node: SVGPathElement, params: { enabled: boolean; duration: number; dashPattern: string; startX: number; endX: number; slideIndex: number }) {
213
+ // Build SVG path for 3+ control points using Catmull-Rom spline
214
+ function buildCatmullRomPath(start: {x:number,y:number}, cps: {x:number,y:number}[], end: {x:number,y:number}): string {
215
+ const pts = [start, ...cps, end];
216
+ let d = `M ${pts[0].x} ${pts[0].y}`;
217
+ for (let i = 0; i < pts.length - 1; i++) {
218
+ const p0 = pts[i === 0 ? 0 : i - 1];
219
+ const p1 = pts[i];
220
+ const p2 = pts[i + 1];
221
+ const p3 = pts[i + 2 < pts.length ? i + 2 : pts.length - 1];
222
+ const c1x = p1.x + (p2.x - p0.x) / 6;
223
+ const c1y = p1.y + (p2.y - p0.y) / 6;
224
+ const c2x = p2.x - (p3.x - p1.x) / 6;
225
+ const c2y = p2.y - (p3.y - p1.y) / 6;
226
+ d += ` C ${c1x} ${c1y} ${c2x} ${c2y} ${p2.x} ${p2.y}`;
227
+ }
228
+ return d;
229
+ }
230
+
231
+ // Arrow draw/undraw/draw-undraw animation action
232
+ function animateStyledArrowDraw(node: SVGPathElement, params: { enabled: boolean; mode: string; duration: number; dashPattern: string; startX: number; endX: number; slideIndex: number }) {
210
233
  let lastSlideIndex = params.slideIndex;
211
234
  let animationId: number | null = null;
212
235
  function runAnimation() {
@@ -215,24 +238,63 @@
215
238
  const svg = node.closest('svg') as SVGSVGElement | null;
216
239
  if (!svg) return;
217
240
  const goesLeftToRight = params.endX >= params.startX;
218
- svg.style.clipPath = goesLeftToRight ? 'inset(0 100% 0 0)' : 'inset(0 0 0 100%)';
219
- const startTime = performance.now();
241
+ const mode = params.mode;
220
242
  const dur = params.duration;
243
+ const startTime = performance.now();
244
+ if (mode === 'draw' || mode === 'draw-undraw') {
245
+ svg.style.clipPath = goesLeftToRight ? 'inset(0 100% 0 0)' : 'inset(0 0 0 100%)';
246
+ } else if (mode === 'undraw') {
247
+ svg.style.clipPath = 'none';
248
+ }
221
249
  function animate(currentTime: number) {
222
250
  const elapsed = currentTime - startTime;
223
- const progress = Math.min(elapsed / dur, 1);
224
- const eased = 1 - Math.pow(1 - progress, 3);
225
- const inset = 100 * (1 - eased);
226
- svg!.style.clipPath = goesLeftToRight ? `inset(0 ${inset}% 0 0)` : `inset(0 0 0 ${inset}%)`;
227
- if (progress < 1) animationId = requestAnimationFrame(animate);
228
- else { svg!.style.clipPath = 'none'; animationId = null; }
251
+ if (mode === 'draw') {
252
+ const progress = Math.min(elapsed / dur, 1);
253
+ const eased = 1 - Math.pow(1 - progress, 3);
254
+ const inset = 100 * (1 - eased);
255
+ svg!.style.clipPath = goesLeftToRight ? `inset(0 ${inset}% 0 0)` : `inset(0 0 0 ${inset}%)`;
256
+ if (progress < 1) { animationId = requestAnimationFrame(animate); }
257
+ else { svg!.style.clipPath = 'none'; animationId = null; }
258
+ } else if (mode === 'undraw') {
259
+ const progress = Math.min(elapsed / dur, 1);
260
+ const eased = 1 - Math.pow(1 - progress, 3);
261
+ const inset = 100 * eased;
262
+ svg!.style.clipPath = goesLeftToRight ? `inset(0 0 0 ${inset}%)` : `inset(0 ${inset}% 0 0)`;
263
+ if (progress < 1) { animationId = requestAnimationFrame(animate); }
264
+ else { svg!.style.clipPath = 'inset(0 0 0 100%)'; animationId = null; }
265
+ } else if (mode === 'draw-undraw') {
266
+ const halfDur = dur / 2;
267
+ if (elapsed < halfDur) {
268
+ const progress = Math.min(elapsed / halfDur, 1);
269
+ const eased = 1 - Math.pow(1 - progress, 3);
270
+ const inset = 100 * (1 - eased);
271
+ svg!.style.clipPath = goesLeftToRight ? `inset(0 ${inset}% 0 0)` : `inset(0 0 0 ${inset}%)`;
272
+ animationId = requestAnimationFrame(animate);
273
+ } else {
274
+ const progress = Math.min((elapsed - halfDur) / halfDur, 1);
275
+ const eased = 1 - Math.pow(1 - progress, 3);
276
+ const inset = 100 * eased;
277
+ svg!.style.clipPath = goesLeftToRight ? `inset(0 0 0 ${inset}%)` : `inset(0 ${inset}% 0 0)`;
278
+ if (progress < 1) { animationId = requestAnimationFrame(animate); }
279
+ else { svg!.style.clipPath = 'inset(0 0 0 100%)'; animationId = null; }
280
+ }
281
+ }
229
282
  }
230
283
  animationId = requestAnimationFrame(animate);
231
284
  }
232
285
  runAnimation();
233
286
  return {
234
287
  update(newParams: typeof params) {
235
- if (newParams.slideIndex !== lastSlideIndex) { lastSlideIndex = newParams.slideIndex; params = newParams; runAnimation(); }
288
+ const slideChanged = newParams.slideIndex !== lastSlideIndex;
289
+ params = newParams;
290
+ if (!params.enabled) {
291
+ if (animationId) { cancelAnimationFrame(animationId); animationId = null; }
292
+ const svg = node.closest('svg') as SVGSVGElement | null;
293
+ if (svg) svg.style.clipPath = '';
294
+ lastSlideIndex = newParams.slideIndex;
295
+ return;
296
+ }
297
+ if (slideChanged) { lastSlideIndex = newParams.slideIndex; runAnimation(); }
236
298
  },
237
299
  destroy() { if (animationId) cancelAnimationFrame(animationId); }
238
300
  };
@@ -278,7 +340,12 @@
278
340
  strokeColor: shapeEl ? tween(shapeEl.strokeColor, { duration: 500 }) : null,
279
341
  strokeWidth: shapeEl ? tween(shapeEl.strokeWidth, { duration: 500 }) : null,
280
342
  shapeMorph: shapeEl ? tween(1, { duration: 500 }) : null,
281
- motionPathProgress: element.motionPathConfig ? tween(0, { duration: 500 }) : null
343
+ motionPathProgress: element.motionPathConfig ? tween(0, { duration: 500 }) : null,
344
+ blur: tween(element.blur ?? 0, { duration: 500 }),
345
+ brightness: tween(element.brightness ?? 100, { duration: 500 }),
346
+ contrast: tween(element.contrast ?? 100, { duration: 500 }),
347
+ saturate: tween(element.saturate ?? 100, { duration: 500 }),
348
+ grayscale: tween(element.grayscale ?? 0, { duration: 500 })
282
349
  });
283
350
  const currentSlideEl = getElementInSlide(currentSlide, element.id);
284
351
  elementContent.set(element.id, JSON.parse(JSON.stringify(currentSlideEl || element)));
@@ -370,6 +437,11 @@
370
437
  animated.perspective.to(targetEl.perspective ?? 1000, { duration: 0 });
371
438
  animated.opacity.to(1, { duration: 0 });
372
439
  animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: 0 });
440
+ animated.blur.to(targetEl.blur ?? 0, { duration: 0 });
441
+ animated.brightness.to(targetEl.brightness ?? 100, { duration: 0 });
442
+ animated.contrast.to(targetEl.contrast ?? 100, { duration: 0 });
443
+ animated.saturate.to(targetEl.saturate ?? 100, { duration: 0 });
444
+ animated.grayscale.to(targetEl.grayscale ?? 0, { duration: 0 });
373
445
  if (targetEl.type === 'text') animated.fontSize?.to((targetEl as TextElement).fontSize, { duration: 0 });
374
446
  if (targetEl.type === 'shape') {
375
447
  const s = targetEl as ShapeElement;
@@ -433,6 +505,11 @@
433
505
  await animated.tiltY.to(currentEl.tiltY ?? 0, { duration: 0 });
434
506
  await animated.perspective.to(currentEl.perspective ?? 1000, { duration: 0 });
435
507
  await animated.borderRadius.to((currentEl as any).borderRadius ?? 0, { duration: 0 });
508
+ await animated.blur.to(currentEl.blur ?? 0, { duration: 0 });
509
+ await animated.brightness.to(currentEl.brightness ?? 100, { duration: 0 });
510
+ await animated.contrast.to(currentEl.contrast ?? 100, { duration: 0 });
511
+ await animated.saturate.to(currentEl.saturate ?? 100, { duration: 0 });
512
+ await animated.grayscale.to(currentEl.grayscale ?? 0, { duration: 0 });
436
513
  await animated.opacity.to(1, { duration: 0 });
437
514
  if (currentEl.type === 'text' && animated.fontSize) await animated.fontSize.to((currentEl as TextElement).fontSize, { duration: 0 });
438
515
  if (currentEl.type === 'shape') {
@@ -493,6 +570,13 @@
493
570
  if (!sequencedProps.has('skew')) { anims.push(animated.skewX.to(targetEl.skewX ?? 0, { duration: elementDuration, easing })); anims.push(animated.skewY.to(targetEl.skewY ?? 0, { duration: elementDuration, easing })); }
494
571
  if (!sequencedProps.has('size')) { anims.push(animated.width.to(targetEl.size.width, { duration: elementDuration, easing })); anims.push(animated.height.to(targetEl.size.height, { duration: elementDuration, easing })); }
495
572
  if (!sequencedProps.has('borderRadius')) anims.push(animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: elementDuration, easing }));
573
+ if (!sequencedProps.has('blur')) {
574
+ animated.blur.to(targetEl.blur ?? 0, { duration: elementDuration, easing });
575
+ animated.brightness.to(targetEl.brightness ?? 100, { duration: elementDuration, easing });
576
+ animated.contrast.to(targetEl.contrast ?? 100, { duration: elementDuration, easing });
577
+ animated.saturate.to(targetEl.saturate ?? 100, { duration: elementDuration, easing });
578
+ animated.grayscale.to(targetEl.grayscale ?? 0, { duration: elementDuration, easing });
579
+ }
496
580
  if (!sequencedProps.has('perspective')) anims.push(animated.perspective.to(targetEl.perspective ?? 1000, { duration: elementDuration, easing }));
497
581
  const sortedSeqs = [...propertySequences].sort((a, b) => a.order - b.order);
498
582
  let cumulativeDelay = 0;
@@ -506,6 +590,13 @@
506
590
  else if (seq.property === 'skew') { animated.skewX.to(targetEl.skewX ?? 0, { duration: seqDuration, easing }); animated.skewY.to(targetEl.skewY ?? 0, { duration: seqDuration, easing }); }
507
591
  else if (seq.property === 'size') { animated.width.to(targetEl.size.width, { duration: seqDuration, easing }); animated.height.to(targetEl.size.height, { duration: seqDuration, easing }); }
508
592
  else if (seq.property === 'borderRadius') animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: seqDuration, easing });
593
+ else if (seq.property === 'blur') {
594
+ animated.blur.to(targetEl.blur ?? 0, { duration: seqDuration, easing });
595
+ animated.brightness.to(targetEl.brightness ?? 100, { duration: seqDuration, easing });
596
+ animated.contrast.to(targetEl.contrast ?? 100, { duration: seqDuration, easing });
597
+ animated.saturate.to(targetEl.saturate ?? 100, { duration: seqDuration, easing });
598
+ animated.grayscale.to(targetEl.grayscale ?? 0, { duration: seqDuration, easing });
599
+ }
509
600
  else if (seq.property === 'color' && targetEl.type === 'shape') {
510
601
  const s = targetEl as ShapeElement;
511
602
  animated.fillColor?.to(s.fillColor, { duration: seqDuration, easing });
@@ -529,6 +620,11 @@
529
620
  anims.push(animated.tiltY.to(targetEl.tiltY ?? 0, { duration: elementDuration, easing }));
530
621
  anims.push(animated.perspective.to(targetEl.perspective ?? 1000, { duration: elementDuration, easing }));
531
622
  anims.push(animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: elementDuration, easing }));
623
+ anims.push(animated.blur.to(targetEl.blur ?? 0, { duration: elementDuration, easing }));
624
+ anims.push(animated.brightness.to(targetEl.brightness ?? 100, { duration: elementDuration, easing }));
625
+ anims.push(animated.contrast.to(targetEl.contrast ?? 100, { duration: elementDuration, easing }));
626
+ anims.push(animated.saturate.to(targetEl.saturate ?? 100, { duration: elementDuration, easing }));
627
+ anims.push(animated.grayscale.to(targetEl.grayscale ?? 0, { duration: elementDuration, easing }));
532
628
  }
533
629
  // Motion path progress — await reset, then animate forward
534
630
  if (animated.motionPathProgress && targetEl.motionPathConfig) {
@@ -575,6 +671,11 @@
575
671
  anims.push(animated.tiltY.to(targetEl.tiltY ?? 0, { duration: 0 }));
576
672
  anims.push(animated.perspective.to(targetEl.perspective ?? 1000, { duration: 0 }));
577
673
  anims.push(animated.borderRadius.to((targetEl as any).borderRadius ?? 0, { duration: 0 }));
674
+ anims.push(animated.blur.to(targetEl.blur ?? 0, { duration: 0 }));
675
+ anims.push(animated.brightness.to(targetEl.brightness ?? 100, { duration: 0 }));
676
+ anims.push(animated.contrast.to(targetEl.contrast ?? 100, { duration: 0 }));
677
+ anims.push(animated.saturate.to(targetEl.saturate ?? 100, { duration: 0 }));
678
+ anims.push(animated.grayscale.to(targetEl.grayscale ?? 0, { duration: 0 }));
578
679
  if (targetEl.type === 'text' && animated.fontSize) {
579
680
  anims.push(animated.fontSize.to((targetEl as TextElement).fontSize, { duration: 0 }));
580
681
  }
@@ -659,6 +760,13 @@
659
760
  elementContent = newElementContent;
660
761
  currentSlideIndex = targetIndex;
661
762
  isTransitioning = false;
763
+ // Ensure elements not on the new slide are fully hidden
764
+ const newSlide = slides[targetIndex];
765
+ for (const elementId of allElementIds) {
766
+ const onSlide = getElementInSlide(newSlide, elementId);
767
+ const animated = animatedElements.get(elementId);
768
+ if (!onSlide && animated) { animated.opacity.to(0, { duration: 0 }); }
769
+ }
662
770
  animateMotionPaths(slides[targetIndex]);
663
771
  onslidechange?.(targetIndex, slides.length);
664
772
  if (targetIndex === slides.length - 1 && !loop) oncomplete?.();
@@ -736,6 +844,64 @@
736
844
  export function getTotalSlides() { return slides.length; }
737
845
  export function getIsPlaying() { return isAutoplay; }
738
846
 
847
+ // Auto-load Google Fonts used by text elements in the project.
848
+ // Generic CSS font families that don't need loading
849
+ const GENERIC_FONTS = new Set([
850
+ 'serif', 'sans-serif', 'monospace', 'cursive', 'fantasy',
851
+ 'system-ui', 'ui-serif', 'ui-sans-serif', 'ui-monospace', 'ui-rounded',
852
+ 'math', 'emoji', 'fangsong', 'inherit', 'initial', 'unset'
853
+ ]);
854
+
855
+ // Extract individual font names from a CSS font-family string.
856
+ // e.g. '"JetBrains Mono", system-ui, monospace' → ['JetBrains Mono']
857
+ function extractFontNames(fontFamily: string): string[] {
858
+ return fontFamily
859
+ .split(',')
860
+ .map(f => f.trim().replace(/^['"]|['"]$/g, ''))
861
+ .filter(f => f && !GENERIC_FONTS.has(f.toLowerCase()));
862
+ }
863
+
864
+ // Auto-load fonts used by text/counter elements.
865
+ // Uses fontsource CDN (jsDelivr) which registers the SAME font-family names
866
+ // as the app (e.g. "Plus Jakarta Sans Variable"), unlike Google Fonts which
867
+ // strips the "Variable" suffix.
868
+ function loadProjectFonts(proj: AnimotProject) {
869
+ const fonts = new Set<string>();
870
+ for (const slide of proj.slides) {
871
+ for (const el of slide.canvas.elements) {
872
+ if (el.type === 'text' || el.type === 'counter') {
873
+ const f = (el as any).fontFamily as string | undefined;
874
+ if (f) {
875
+ for (const name of extractFontNames(f)) fonts.add(name);
876
+ }
877
+ }
878
+ }
879
+ }
880
+ if (fonts.size === 0) return;
881
+
882
+ // Deduplicate against already-injected links to avoid double-loading
883
+ const loaded = new Set<string>();
884
+ document.querySelectorAll<HTMLLinkElement>('link[data-animot-font]').forEach(l => loaded.add(l.dataset.animotFont!));
885
+
886
+ for (const font of fonts) {
887
+ if (loaded.has(font)) continue;
888
+ const isVariable = /\s+Variable$/i.test(font);
889
+ // Convert font name to fontsource package slug:
890
+ // "Plus Jakarta Sans Variable" → "plus-jakarta-sans"
891
+ // "JetBrains Mono" → "jetbrains-mono"
892
+ const baseName = font.replace(/\s*Variable$/i, '');
893
+ const slug = baseName.toLowerCase().replace(/\s+/g, '-');
894
+ const pkg = isVariable
895
+ ? `@fontsource-variable/${slug}`
896
+ : `@fontsource/${slug}`;
897
+ const link = document.createElement('link');
898
+ link.rel = 'stylesheet';
899
+ link.href = `https://cdn.jsdelivr.net/npm/${pkg}/index.css`;
900
+ link.dataset.animotFont = font;
901
+ document.head.appendChild(link);
902
+ }
903
+ }
904
+
739
905
  // Load data
740
906
  async function loadProject() {
741
907
  loading = true; error = null;
@@ -746,6 +912,7 @@
746
912
  if (!res.ok) throw new Error(`Failed to load: ${res.status}`);
747
913
  project = await res.json();
748
914
  } else { throw new Error('Either src or data prop is required'); }
915
+ loadProjectFonts(project!);
749
916
  currentSlideIndex = startSlide;
750
917
  await new Promise(r => setTimeout(r, 10));
751
918
  initAllAnimatedElements();
@@ -840,33 +1007,59 @@
840
1007
  style:--float-speed="{hasFloat ? computeFloatSpeed(floatCfg, floatGroupId || elementId) : 3}s"
841
1008
  style:--float-delay="{hashFraction(floatGroupId || elementId, 3) * 2}s"
842
1009
  style:animation-name={hasFloat ? getFloatAnimName(floatCfg!.direction, floatGroupId || elementId) : 'none'}
1010
+ style:filter={(() => { const parts: string[] = []; const b = animated.blur.current; const br2 = animated.brightness.current; const c = animated.contrast.current; const s = animated.saturate.current; const g = animated.grayscale.current; if (b) parts.push(`blur(${b}px)`); if (br2 !== 100) parts.push(`brightness(${br2}%)`); if (c !== 100) parts.push(`contrast(${c}%)`); if (s !== 100) parts.push(`saturate(${s}%)`); if (g) parts.push(`grayscale(${g}%)`); return parts.length ? parts.join(' ') : 'none'; })()}
843
1011
  >
844
1012
  {#if element.type === 'code'}
845
1013
  {@const codeEl = element as CodeElement}
846
1014
  {@const morphState = codeMorphState.get(codeEl.id)}
847
- <div class="animot-code-block" class:transparent-bg={codeEl.transparentBackground} style:font-size="{codeEl.fontSize}px" style:font-weight={codeEl.fontWeight || 400} style:padding="{codeEl.padding}px" style:border-radius="{animated.borderRadius.current}px">
1015
+ <div class="animot-code-block" class:transparent-bg={codeEl.transparentBackground} style:font-size="{codeEl.fontSize}px" style:font-weight={codeEl.fontWeight || 400} style:padding="{codeEl.padding}px" style:border-radius="{animated.borderRadius.current}px" style:background={codeEl.bgColor ?? '#0d1117'}>
848
1016
  {#if codeEl.showHeader}
849
- <div class="animot-code-header" class:macos={codeEl.headerStyle === 'macos'}>
1017
+ <div class="animot-code-header" class:macos={codeEl.headerStyle === 'macos'} class:windows={codeEl.headerStyle === 'windows'} style:border-radius="{codeEl.headerRadius ?? animated.borderRadius.current}px {codeEl.headerRadius ?? animated.borderRadius.current}px 0 0">
850
1018
  {#if codeEl.headerStyle === 'macos'}
851
1019
  <div class="animot-window-controls">
852
1020
  <span class="animot-control close"></span>
853
1021
  <span class="animot-control minimize"></span>
854
1022
  <span class="animot-control maximize"></span>
855
1023
  </div>
1024
+ {:else if codeEl.headerStyle === 'windows'}
1025
+ <div class="animot-window-controls">
1026
+ <span class="animot-control win-minimize">
1027
+ <svg width="10" height="10" viewBox="0 0 10 10"><path d="M2 5h6" stroke="currentColor" stroke-width="1.2"/></svg>
1028
+ </span>
1029
+ <span class="animot-control win-maximize">
1030
+ <svg width="10" height="10" viewBox="0 0 10 10"><rect x="1.5" y="1.5" width="7" height="7" rx="0.5" fill="none" stroke="currentColor" stroke-width="1.2"/></svg>
1031
+ </span>
1032
+ <span class="animot-control win-close">
1033
+ <svg width="10" height="10" viewBox="0 0 10 10"><path d="M2 2l6 6M8 2l-6 6" stroke="currentColor" stroke-width="1.2"/></svg>
1034
+ </span>
1035
+ </div>
856
1036
  {/if}
857
- <span class="animot-filename">{codeEl.filename}</span>
1037
+ <div class="animot-filename-tab" style:border-radius="{codeEl.tabRadius ?? 6}px">
1038
+ <svg class="animot-file-icon" width="14" height="14" viewBox="0 0 16 16" fill="none">
1039
+ <path d="M4 1h5.5L13 4.5V14a1 1 0 01-1 1H4a1 1 0 01-1-1V2a1 1 0 011-1z" stroke="currentColor" stroke-width="1.2" opacity="0.5"/>
1040
+ <path d="M9.5 1v3.5H13" stroke="currentColor" stroke-width="1.2" opacity="0.5"/>
1041
+ </svg>
1042
+ <span class="animot-filename">{codeEl.filename}</span>
1043
+ </div>
1044
+ <button class="animot-copy-code-btn" onclick={(e) => { e.stopPropagation(); navigator.clipboard.writeText(codeEl.code); const btn = e.currentTarget as HTMLElement; btn.classList.add('copied'); setTimeout(() => btn.classList.remove('copied'), 1500); }}>
1045
+ <span class="animot-copy-label">Copy</span><span class="animot-copied-label">Copied!</span>
1046
+ <svg class="animot-copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
1047
+ <svg class="animot-check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
1048
+ </button>
858
1049
  </div>
1050
+ {:else}
1051
+ <button class="animot-copy-code-btn animot-floating" onclick={(e) => { e.stopPropagation(); navigator.clipboard.writeText(codeEl.code); const btn = e.currentTarget as HTMLElement; btn.classList.add('copied'); setTimeout(() => btn.classList.remove('copied'), 1500); }}>
1052
+ <span class="animot-copy-label">Copy</span><span class="animot-copied-label">Copied!</span>
1053
+ <svg class="animot-copy-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
1054
+ <svg class="animot-check-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
1055
+ </button>
859
1056
  {/if}
860
1057
  <div class="animot-code-content">
861
1058
  <div class="animot-highlighted-code">
862
- {#if morphState}
863
- {#if morphState.oldCode !== morphState.newCode && morphState.mode !== 'instant'}
864
- {#key currentSlideIndex}
865
- <CodeMorph oldCode={morphState.oldCode} newCode={morphState.newCode} language={codeEl.language} theme={codeEl.theme} mode={morphState.mode} speed={morphState.speed} highlightColor={morphState.highlightColor} highlightDuration={codeEl.animation?.highlightDuration || 1000} showLineNumbers={(getElementInSlide(currentSlide, codeEl.id) as CodeElement | undefined)?.showLineNumbers ?? false} />
866
- {/key}
867
- {:else}
868
- {@html getCodeHighlight(codeEl.id)}
869
- {/if}
1059
+ {#if morphState && morphState.oldCode !== morphState.newCode && morphState.mode !== 'instant'}
1060
+ {#key currentSlideIndex}
1061
+ <CodeMorph oldCode={morphState?.oldCode ?? ''} newCode={morphState?.newCode ?? ''} language={codeEl.language} theme={codeEl.theme} mode={morphState?.mode ?? 'highlight-changes'} speed={morphState?.speed ?? 50} highlightColor={morphState?.highlightColor ?? '#fef08a'} highlightDuration={codeEl.animation?.highlightDuration || 1000} showLineNumbers={(getElementInSlide(currentSlide, codeEl.id) as CodeElement | undefined)?.showLineNumbers ?? false} />
1062
+ {/key}
870
1063
  {:else}
871
1064
  {@html getCodeHighlight(codeEl.id)}
872
1065
  {/if}
@@ -905,25 +1098,27 @@
905
1098
  {:else if element.type === 'arrow'}
906
1099
  {@const arrowEl = element as ArrowElement}
907
1100
  {@const cp = arrowEl.controlPoints || []}
908
- {@const pathD = cp.length === 0 ? `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} L ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}` : cp.length === 1 ? `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} Q ${cp[0].x} ${cp[0].y} ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}` : `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} C ${cp[0].x} ${cp[0].y} ${cp[1].x} ${cp[1].y} ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}`}
909
- {@const endAngle = cp.length === 0 ? Math.atan2(arrowEl.endPoint.y - arrowEl.startPoint.y, arrowEl.endPoint.x - arrowEl.startPoint.x) : cp.length === 1 ? Math.atan2(arrowEl.endPoint.y - cp[0].y, arrowEl.endPoint.x - cp[0].x) : Math.atan2(arrowEl.endPoint.y - cp[1].y, arrowEl.endPoint.x - cp[1].x)}
1101
+ {@const pathD = cp.length === 0 ? `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} L ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}` : cp.length === 1 ? `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} Q ${cp[0].x} ${cp[0].y} ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}` : cp.length === 2 ? `M ${arrowEl.startPoint.x} ${arrowEl.startPoint.y} C ${cp[0].x} ${cp[0].y} ${cp[1].x} ${cp[1].y} ${arrowEl.endPoint.x} ${arrowEl.endPoint.y}` : buildCatmullRomPath(arrowEl.startPoint, cp, arrowEl.endPoint)}
1102
+ {@const lastCp = cp.length > 0 ? cp[cp.length - 1] : arrowEl.startPoint}
1103
+ {@const endAngle = Math.atan2(arrowEl.endPoint.y - lastCp.y, arrowEl.endPoint.x - lastCp.x)}
910
1104
  {@const headAngle = Math.PI / 6}
911
1105
  {@const headSize = arrowEl.headSize}
912
1106
  {@const arrowHeadPath = `M ${arrowEl.endPoint.x - headSize * Math.cos(endAngle - headAngle)} ${arrowEl.endPoint.y - headSize * Math.sin(endAngle - headAngle)} L ${arrowEl.endPoint.x} ${arrowEl.endPoint.y} L ${arrowEl.endPoint.x - headSize * Math.cos(endAngle + headAngle)} ${arrowEl.endPoint.y - headSize * Math.sin(endAngle + headAngle)}`}
913
1107
  {@const arrowAnimMode = arrowEl.animation?.mode ?? 'none'}
914
1108
  {@const arrowAnimDuration = arrowEl.animation?.duration ?? 500}
915
1109
  {@const isStyledArrow = arrowEl.style !== 'solid'}
1110
+ {@const isDrawType = arrowAnimMode === 'draw' || arrowAnimMode === 'undraw' || arrowAnimMode === 'draw-undraw'}
916
1111
  {@const baseDashArray = arrowEl.style === 'dashed' ? '10,5' : arrowEl.style === 'dotted' ? '2,5' : 'none'}
917
- <svg class="animot-arrow-element" class:arrow-animate-draw={arrowAnimMode === 'draw' && !isStyledArrow} class:arrow-animate-grow={arrowAnimMode === 'grow'} viewBox="0 0 {arrowEl.size.width} {arrowEl.size.height}" preserveAspectRatio="none" style="--arrow-anim-duration: {arrowAnimDuration}ms;">
918
- <path class="arrow-path" d={pathD} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-dasharray={baseDashArray} stroke-linecap="round" stroke-linejoin="round" use:animateStyledArrowDraw={{ enabled: arrowAnimMode === 'draw' && isStyledArrow, duration: arrowAnimDuration, dashPattern: baseDashArray, startX: arrowEl.startPoint.x, endX: arrowEl.endPoint.x, slideIndex: currentSlideIndex }} />
1112
+ <svg class="animot-arrow-element" class:arrow-animate-draw={arrowAnimMode === 'draw' && !isStyledArrow} class:arrow-animate-undraw={arrowAnimMode === 'undraw' && !isStyledArrow} class:arrow-animate-draw-undraw={arrowAnimMode === 'draw-undraw' && !isStyledArrow} class:arrow-animate-grow={arrowAnimMode === 'grow'} viewBox="0 0 {arrowEl.size.width} {arrowEl.size.height}" preserveAspectRatio="none" style="--arrow-anim-duration: {arrowAnimDuration}ms;">
1113
+ <path class="arrow-path" d={pathD} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-dasharray={baseDashArray} stroke-linecap="round" stroke-linejoin="round" use:animateStyledArrowDraw={{ enabled: isDrawType && isStyledArrow, mode: arrowAnimMode, duration: arrowAnimDuration, dashPattern: baseDashArray, startX: arrowEl.startPoint.x, endX: arrowEl.endPoint.x, slideIndex: currentSlideIndex }} />
919
1114
  {#if arrowEl.showHead !== false}
920
- <path class="arrow-head" class:arrow-head-styled-draw={arrowAnimMode === 'draw' && isStyledArrow} d={arrowHeadPath} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-linecap="round" stroke-linejoin="round" style={arrowAnimMode === 'draw' && isStyledArrow ? `--arrow-anim-duration: ${arrowAnimDuration}ms;` : ''} />
1115
+ <path class="arrow-head" class:arrow-head-styled-draw={isDrawType && isStyledArrow} class:arrow-head-undraw={arrowAnimMode === 'undraw'} class:arrow-head-draw-undraw={arrowAnimMode === 'draw-undraw'} d={arrowHeadPath} fill="none" stroke={arrowEl.color} stroke-width={arrowEl.strokeWidth} stroke-linecap="round" stroke-linejoin="round" style={isDrawType && isStyledArrow ? `--arrow-anim-duration: ${arrowAnimDuration}ms;` : ''} />
921
1116
  {/if}
922
1117
  </svg>
923
1118
  {:else if element.type === 'image'}
924
1119
  {@const imgEl = element as ImageElement}
925
1120
  {@const clipPath = imgEl.clipMask?.enabled ? (imgEl.clipMask.shapeType === 'circle' ? 'circle(50% at 50% 50%)' : imgEl.clipMask.shapeType === 'ellipse' ? 'ellipse(50% 50% at 50% 50%)' : imgEl.clipMask.shapeType === 'triangle' ? 'polygon(50% 0%, 0% 100%, 100% 100%)' : imgEl.clipMask.shapeType === 'star' ? 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)' : imgEl.clipMask.shapeType === 'hexagon' ? 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)' : (imgEl.clipMask.borderRadius ?? 0) > 0 ? `inset(0 round ${imgEl.clipMask.borderRadius}px)` : 'none') : 'none'}
926
- <img class="animot-image-element" src={imgEl.src} alt="" style:object-fit={imgEl.objectFit} style:border-radius="{imgEl.clipMask?.enabled ? 0 : imgEl.borderRadius}px" style:opacity={imgEl.opacity} style:filter={imgEl.blur ? `blur(${imgEl.blur}px)` : 'none'} style:clip-path={clipPath} style:background-color={imgEl.backgroundColor ?? 'transparent'} />
1121
+ <img class="animot-image-element" src={imgEl.src} alt="" style:object-fit={imgEl.objectFit} style:border-radius="{imgEl.clipMask?.enabled ? 0 : imgEl.borderRadius}px" style:opacity={imgEl.opacity} style:clip-path={clipPath} style:background-color={imgEl.backgroundColor ?? 'transparent'} />
927
1122
  {:else if element.type === 'shape'}
928
1123
  {@const shapeEl = element as ShapeElement}
929
1124
  {@const animFill = animated.fillColor?.current ?? shapeEl.fillColor}
@@ -1031,6 +1226,14 @@
1031
1226
  </script>
1032
1227
 
1033
1228
  <style>
1229
+ /* Universal reset — mirrors the animot app's global * reset to prevent
1230
+ host page defaults (margins on p/h1, padding, box-sizing) from leaking in */
1231
+ .animot-presenter :global(*) {
1232
+ margin: 0;
1233
+ padding: 0;
1234
+ box-sizing: border-box;
1235
+ }
1236
+
1034
1237
  .animot-presenter {
1035
1238
  position: relative;
1036
1239
  width: 100%;
@@ -1080,19 +1283,34 @@
1080
1283
 
1081
1284
  /* Code */
1082
1285
  .animot-code-block {
1083
- width: 100%; height: 100%; background: #0d1117; overflow: hidden;
1286
+ width: 100%; height: 100%; overflow: hidden;
1084
1287
  display: flex; flex-direction: column; box-shadow: 0 8px 32px rgba(0,0,0,0.4);
1085
1288
  margin: 0; box-sizing: border-box;
1086
1289
  }
1087
- .animot-code-block.transparent-bg { background: transparent; box-shadow: none; }
1088
- .animot-code-block.transparent-bg .animot-code-header { background: transparent; border-bottom-color: rgba(255,255,255,0.1); }
1089
- .animot-code-header { display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #161b22; border-bottom: 1px solid #30363d; flex-shrink: 0; }
1090
- .animot-window-controls { display: flex; gap: 8px; }
1091
- .animot-control { width: 12px; height: 12px; border-radius: 50%; display: block; }
1290
+ .animot-code-block.transparent-bg { background: transparent !important; box-shadow: none; }
1291
+ .animot-code-block.transparent-bg .animot-code-header { background: transparent; border-bottom-color: rgba(255,255,255,0.06); }
1292
+ .animot-code-header { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: rgba(0, 0, 0, 0.2); border-bottom: 1px solid rgba(255, 255, 255, 0.06); flex-shrink: 0; min-height: 40px; }
1293
+ .animot-window-controls { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
1294
+ .macos .animot-control { width: 12px; height: 12px; border-radius: 50%; display: block; }
1092
1295
  .macos .animot-control.close { background: #ff5f57; }
1093
1296
  .macos .animot-control.minimize { background: #febc2e; }
1094
1297
  .macos .animot-control.maximize { background: #28c840; }
1095
- .animot-filename { color: #8b949e; font-size: 14px; flex: 1; }
1298
+ .windows .animot-window-controls { order: 99; margin-left: auto; gap: 0; }
1299
+ .windows .animot-control { display: flex; align-items: center; justify-content: center; width: 28px; height: 24px; border-radius: 4px; color: rgba(255,255,255,0.45); }
1300
+ .animot-filename-tab { display: flex; align-items: center; gap: 6px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08); border-radius: 6px; padding: 4px 10px; max-width: 220px; color: rgba(255,255,255,0.4); }
1301
+ .animot-file-icon { flex-shrink: 0; }
1302
+ .animot-filename { color: rgba(255,255,255,0.55); font-size: 12px; line-height: 18px; }
1303
+ .animot-copy-code-btn { display: flex; align-items: center; gap: 5px; height: 28px; padding: 0 8px; margin-left: auto; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; color: rgba(255,255,255,0.4); cursor: pointer; opacity: 0; transition: opacity 0.2s, background 0.15s, color 0.15s; flex-shrink: 0; font-size: 12px; font-family: inherit; white-space: nowrap; }
1304
+ .animot-copy-code-btn:hover { background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.8); }
1305
+ .animot-copy-code-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
1306
+ .animot-copy-code-btn .animot-check-icon { display: none; }
1307
+ .animot-copy-code-btn .animot-copied-label { display: none; }
1308
+ .animot-copy-code-btn.copied .animot-copy-icon { display: none; }
1309
+ .animot-copy-code-btn.copied .animot-copy-label { display: none; }
1310
+ .animot-copy-code-btn.copied .animot-check-icon { display: block; color: #4ade80; }
1311
+ .animot-copy-code-btn.copied .animot-copied-label { display: inline; color: #4ade80; }
1312
+ .animot-copy-code-btn.animot-floating { position: absolute; top: 8px; right: 8px; z-index: 2; }
1313
+ .animot-code-block:hover .animot-copy-code-btn { opacity: 1; }
1096
1314
  .animot-code-content { flex: 1; overflow: hidden; position: relative; }
1097
1315
  .animot-highlighted-code { width: 100%; height: 100%; }
1098
1316
  .animot-code-content :global(pre), .animot-highlighted-code :global(pre) { margin: 0; padding: 16px; background: transparent !important; line-height: 1.6; font-size: inherit; overflow: visible; }
@@ -1107,11 +1325,19 @@
1107
1325
  /* Arrow */
1108
1326
  .animot-arrow-element { width: 100%; height: 100%; }
1109
1327
  .arrow-animate-draw .arrow-path { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: animot-arrow-draw var(--arrow-anim-duration, 500ms) ease-out forwards; }
1328
+ .arrow-animate-undraw .arrow-path { stroke-dasharray: 1000; stroke-dashoffset: 0; animation: animot-arrow-undraw var(--arrow-anim-duration, 500ms) ease-out forwards; }
1329
+ .arrow-animate-draw-undraw .arrow-path { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: animot-arrow-draw-undraw var(--arrow-anim-duration, 500ms) ease-out forwards; }
1110
1330
  .arrow-head-styled-draw { opacity: 0; animation: animot-arrow-head-appear var(--arrow-anim-duration, 500ms) ease-out forwards; animation-delay: calc(var(--arrow-anim-duration, 500ms) * 0.7); }
1111
1331
  .arrow-animate-draw .arrow-head { opacity: 0; animation: animot-arrow-head-appear var(--arrow-anim-duration, 500ms) ease-out forwards; animation-delay: calc(var(--arrow-anim-duration, 500ms) * 0.7); }
1332
+ .arrow-animate-undraw .arrow-head, .arrow-head-undraw { opacity: 1; animation: animot-arrow-head-disappear var(--arrow-anim-duration, 500ms) ease-out forwards; }
1333
+ .arrow-animate-draw-undraw .arrow-head, .arrow-head-draw-undraw { opacity: 0; animation: animot-arrow-head-draw-undraw var(--arrow-anim-duration, 500ms) ease-out forwards; }
1112
1334
  .arrow-animate-grow { transform-origin: left center; animation: animot-arrow-grow var(--arrow-anim-duration, 500ms) ease-out forwards; }
1113
1335
  @keyframes animot-arrow-draw { to { stroke-dashoffset: 0; } }
1336
+ @keyframes animot-arrow-undraw { from { stroke-dashoffset: 0; } to { stroke-dashoffset: 1000; } }
1337
+ @keyframes animot-arrow-draw-undraw { 0% { stroke-dashoffset: 1000; } 50% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 1000; } }
1114
1338
  @keyframes animot-arrow-head-appear { from { opacity: 0; } to { opacity: 1; } }
1339
+ @keyframes animot-arrow-head-disappear { 0% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; } }
1340
+ @keyframes animot-arrow-head-draw-undraw { 0% { opacity: 0; } 35% { opacity: 1; } 65% { opacity: 1; } 100% { opacity: 0; } }
1115
1341
  @keyframes animot-arrow-grow { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: 1; } }
1116
1342
 
1117
1343
  /* Image */
@@ -1 +1 @@
1
- .code-morph.svelte-1hmk0l{width:100%;height:100%;margin:0;padding:0;background:transparent;font-family:var(--font-mono, "JetBrains Mono", monospace);font-size:inherit;line-height:1.6;white-space:pre-wrap;word-wrap:break-word;color:#e6edf3;box-sizing:border-box;overflow:visible}.code-morph.svelte-1hmk0l pre{margin:0;padding:16px;background:transparent!important;font-family:var(--font-mono);font-size:inherit;line-height:1.6;overflow:visible}.code-morph.svelte-1hmk0l code{font-family:inherit;font-size:inherit;font-weight:inherit}.code-morph.svelte-1hmk0l .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.code-morph.svelte-1hmk0l .word-highlight{background:var(--highlight-color, #facc15);color:#000;border-radius:3px;padding:1px 3px;margin:0 -3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;animation:svelte-1hmk0l-fadeHighlight var(--duration, 1s) ease-out forwards}@keyframes svelte-1hmk0l-fadeHighlight{0%{background:var(--highlight-color, #facc15);color:#000}70%{background:var(--highlight-color, #facc15);color:#000}to{background:transparent;color:inherit}}.counter.svelte-1er5jjj{width:100%;height:100%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;overflow:hidden;white-space:nowrap}.chart.svelte-1mq2d7j{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}.chart-title.svelte-1mq2d7j{font-weight:600;text-align:center;margin-bottom:8px}.chart-content.svelte-1mq2d7j{flex:1;min-height:0}.chart-content.svelte-1mq2d7j svg:where(.svelte-1mq2d7j){width:100%;height:100%}.bar-chart.svelte-1mq2d7j,.line-chart.svelte-1mq2d7j{overflow:visible}.icon-element.svelte-2ld65o{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.icon-element.svelte-2ld65o svg{width:100%;height:100%}@keyframes float-vertical{0%,to{translate:0 0}50%{translate:0 calc(-1 * var(--float-amp, 10px))}}@keyframes float-horizontal{0%,to{translate:0 0}50%{translate:var(--float-amp, 10px) 0}}@keyframes float-both-0{0%{translate:0 0}15%{translate:calc(.8 * var(--float-amp, 10px)) calc(-.6 * var(--float-amp, 10px))}35%{translate:calc(-.4 * var(--float-amp, 10px)) calc(-1 * var(--float-amp, 10px))}55%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.3 * var(--float-amp, 10px))}75%{translate:calc(.3 * var(--float-amp, 10px)) calc(.7 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-1{0%{translate:0 0}20%{translate:calc(-.7 * var(--float-amp, 10px)) calc(-.8 * var(--float-amp, 10px))}40%{translate:calc(.5 * var(--float-amp, 10px)) calc(-.3 * var(--float-amp, 10px))}60%{translate:calc(.9 * var(--float-amp, 10px)) calc(.6 * var(--float-amp, 10px))}80%{translate:calc(-.4 * var(--float-amp, 10px)) calc(.9 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-2{0%{translate:0 0}12%{translate:calc(.6 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}30%{translate:calc(1 * var(--float-amp, 10px)) calc(-.4 * var(--float-amp, 10px))}50%{translate:calc(-.3 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}70%{translate:calc(-.8 * var(--float-amp, 10px)) calc(.2 * var(--float-amp, 10px))}88%{translate:calc(.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-3{0%{translate:0 0}17%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.4 * var(--float-amp, 10px))}33%{translate:calc(-.5 * var(--float-amp, 10px)) calc(-.7 * var(--float-amp, 10px))}50%{translate:calc(.7 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}67%{translate:calc(.9 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}83%{translate:calc(-.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}.animot-presenter.svelte-16ocdv9{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;background:transparent;line-height:normal;font-size:16px;font-weight:400;font-style:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-align:left;color:inherit}.animot-canvas-wrapper.svelte-16ocdv9{display:flex;align-items:center;justify-content:center}.animot-canvas.svelte-16ocdv9{position:relative;overflow:hidden}.animot-element.svelte-16ocdv9{position:absolute;box-sizing:border-box;will-change:transform,opacity,left,top,width,height;isolation:isolate}.animot-element.floating.svelte-16ocdv9{animation-duration:var(--float-speed, 3s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-delay:var(--float-delay, 0s)}.animot-code-block.svelte-16ocdv9{width:100%;height:100%;background:#0d1117;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;margin:0;box-sizing:border-box}.animot-code-block.transparent-bg.svelte-16ocdv9{background:transparent;box-shadow:none}.animot-code-block.transparent-bg.svelte-16ocdv9 .animot-code-header:where(.svelte-16ocdv9){background:transparent;border-bottom-color:#ffffff1a}.animot-code-header.svelte-16ocdv9{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#161b22;border-bottom:1px solid #30363d;flex-shrink:0}.animot-window-controls.svelte-16ocdv9{display:flex;gap:8px}.animot-control.svelte-16ocdv9{width:12px;height:12px;border-radius:50%;display:block}.macos.svelte-16ocdv9 .animot-control.close:where(.svelte-16ocdv9){background:#ff5f57}.macos.svelte-16ocdv9 .animot-control.minimize:where(.svelte-16ocdv9){background:#febc2e}.macos.svelte-16ocdv9 .animot-control.maximize:where(.svelte-16ocdv9){background:#28c840}.animot-filename.svelte-16ocdv9{color:#8b949e;font-size:14px;flex:1}.animot-code-content.svelte-16ocdv9{flex:1;overflow:hidden;position:relative}.animot-highlighted-code.svelte-16ocdv9{width:100%;height:100%}.animot-code-content.svelte-16ocdv9 pre,.animot-highlighted-code.svelte-16ocdv9 pre{margin:0;padding:16px;background:transparent!important;line-height:1.6;font-size:inherit;overflow:visible}.animot-highlighted-code.svelte-16ocdv9 code{font-family:inherit;font-size:inherit;font-weight:inherit}.animot-highlighted-code.svelte-16ocdv9 .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.animot-text-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;white-space:pre-wrap;word-wrap:break-word}.animot-typewriter-cursor.svelte-16ocdv9{animation:svelte-16ocdv9-animot-blink .7s infinite;font-weight:100}@keyframes svelte-16ocdv9-animot-blink{0%,50%{opacity:1}51%,to{opacity:0}}.animot-arrow-element.svelte-16ocdv9{width:100%;height:100%}.arrow-animate-draw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:1000;animation:svelte-16ocdv9-animot-arrow-draw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-head-styled-draw.svelte-16ocdv9{opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-draw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9){opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-grow.svelte-16ocdv9{transform-origin:left center;animation:svelte-16ocdv9-animot-arrow-grow var(--arrow-anim-duration, .5s) ease-out forwards}@keyframes svelte-16ocdv9-animot-arrow-draw{to{stroke-dashoffset:0}}@keyframes svelte-16ocdv9-animot-arrow-head-appear{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-arrow-grow{0%{transform:scaleX(0);opacity:0}to{transform:scaleX(1);opacity:1}}.animot-image-element.svelte-16ocdv9{width:100%;height:100%;display:block}.animot-shape-element.svelte-16ocdv9{width:100%;height:100%;display:block;overflow:visible}.animot-canvas.svelte-16ocdv9{--transition-duration: .5s;transition:transform calc(var(--transition-duration) * .4) ease,opacity calc(var(--transition-duration) * .4) ease}.animot-canvas.transition-fade-out.svelte-16ocdv9{opacity:0}.animot-canvas.transition-fade-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-fadeIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.forward.svelte-16ocdv9{transform:translate(-100%);opacity:0}.animot-canvas.transition-slide-left-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromRight calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.backward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-left-in.backward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-right-out.forward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-right-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-up-out.svelte-16ocdv9{transform:translateY(-100%);opacity:0}.animot-canvas.transition-slide-up-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromBottom calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-down-out.svelte-16ocdv9{transform:translateY(100%);opacity:0}.animot-canvas.transition-slide-down-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromTop calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-in-out.svelte-16ocdv9{transform:scale(.5);opacity:0}.animot-canvas.transition-zoom-in-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-out-out.svelte-16ocdv9{transform:scale(1.5);opacity:0}.animot-canvas.transition-zoom-out-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomOut calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-flip-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromBottom{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromTop{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomIn{0%{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomOut{0%{transform:scale(1.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-flipIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-canvas.transition-flip-x-out.svelte-16ocdv9{transform:perspective(1000px) rotateX(90deg);opacity:0}.animot-canvas.transition-flip-x-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipXIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipXIn{0%{transform:perspective(1000px) rotateX(-90deg);opacity:0}to{transform:perspective(1000px) rotateX(0);opacity:1}}.animot-canvas.transition-flip-y-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-y-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipYIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipYIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-svg-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.animot-svg-element.svelte-16ocdv9 svg{width:100%;height:100%}.animot-controls.svelte-16ocdv9{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:8px 16px;background:#000000b3;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:10px;opacity:0;transition:opacity .3s ease .15s;z-index:100}.animot-presenter.svelte-16ocdv9:hover .animot-controls:where(.svelte-16ocdv9),.animot-menu-visible.svelte-16ocdv9 .animot-controls:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9){display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;border:none;cursor:pointer;background:#ffffff1a;color:#fff;transition:background .2s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):hover:not(:disabled){background:#fff3}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-controls.svelte-16ocdv9 button.active:where(.svelte-16ocdv9){background:#6366f199}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9) svg:where(.svelte-16ocdv9){width:16px;height:16px}.animot-slide-indicator.svelte-16ocdv9{font-size:12px;color:#fff;min-width:50px;text-align:center;font-family:system-ui,sans-serif}.animot-arrow.svelte-16ocdv9{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;background:#00000080;color:#fff;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .3s .15s;z-index:100;padding:0;margin:0}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-arrow.svelte-16ocdv9:hover:not(:disabled){background:#000000b3}.animot-arrow.svelte-16ocdv9 svg:where(.svelte-16ocdv9){width:20px;height:20px}.animot-arrow-left.svelte-16ocdv9{left:8px}.animot-arrow-right.svelte-16ocdv9{right:8px}.animot-progress-bar.svelte-16ocdv9{position:absolute;bottom:0;left:0;right:0;height:3px;background:#ffffff1a;z-index:100;opacity:0;transition:opacity .3s .15s}.animot-presenter.svelte-16ocdv9:hover .animot-progress-bar:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-progress-fill.svelte-16ocdv9{height:100%;background:linear-gradient(135deg,#7c3aed,#ec4899);transition:width .6s ease}.animot-loading.svelte-16ocdv9{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.animot-spinner.svelte-16ocdv9{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:#7c3aed;border-radius:50%;animation:svelte-16ocdv9-animot-spin .8s linear infinite}@keyframes svelte-16ocdv9-animot-spin{to{transform:rotate(360deg)}}.animot-error.svelte-16ocdv9{color:#ef4444;padding:20px;text-align:center;font-family:system-ui,sans-serif}
1
+ .code-morph.svelte-1hmk0l{width:100%;height:100%;margin:0;padding:0;background:transparent;font-family:var(--font-mono, "JetBrains Mono", monospace);font-size:inherit;line-height:1.6;white-space:pre-wrap;word-wrap:break-word;color:#e6edf3;box-sizing:border-box;overflow:visible}.code-morph.svelte-1hmk0l pre{margin:0;padding:16px;background:transparent!important;font-family:var(--font-mono);font-size:inherit;line-height:1.6;overflow:visible}.code-morph.svelte-1hmk0l code{font-family:inherit;font-size:inherit;font-weight:inherit}.code-morph.svelte-1hmk0l .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.code-morph.svelte-1hmk0l .word-highlight{background:var(--highlight-color, #facc15);color:#000;border-radius:3px;padding:1px 3px;margin:0 -3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;animation:svelte-1hmk0l-fadeHighlight var(--duration, 1s) ease-out forwards}@keyframes svelte-1hmk0l-fadeHighlight{0%{background:var(--highlight-color, #facc15);color:#000}70%{background:var(--highlight-color, #facc15);color:#000}to{background:transparent;color:inherit}}.counter.svelte-1er5jjj{width:100%;height:100%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;overflow:hidden;white-space:nowrap}.chart.svelte-1mq2d7j{width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden}.chart-title.svelte-1mq2d7j{font-weight:600;text-align:center;margin-bottom:8px}.chart-content.svelte-1mq2d7j{flex:1;min-height:0}.chart-content.svelte-1mq2d7j svg:where(.svelte-1mq2d7j){width:100%;height:100%}.bar-chart.svelte-1mq2d7j,.line-chart.svelte-1mq2d7j{overflow:visible}.icon-element.svelte-2ld65o{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.icon-element.svelte-2ld65o svg{width:100%;height:100%}@keyframes float-vertical{0%,to{translate:0 0}50%{translate:0 calc(-1 * var(--float-amp, 10px))}}@keyframes float-horizontal{0%,to{translate:0 0}50%{translate:var(--float-amp, 10px) 0}}@keyframes float-both-0{0%{translate:0 0}15%{translate:calc(.8 * var(--float-amp, 10px)) calc(-.6 * var(--float-amp, 10px))}35%{translate:calc(-.4 * var(--float-amp, 10px)) calc(-1 * var(--float-amp, 10px))}55%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.3 * var(--float-amp, 10px))}75%{translate:calc(.3 * var(--float-amp, 10px)) calc(.7 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-1{0%{translate:0 0}20%{translate:calc(-.7 * var(--float-amp, 10px)) calc(-.8 * var(--float-amp, 10px))}40%{translate:calc(.5 * var(--float-amp, 10px)) calc(-.3 * var(--float-amp, 10px))}60%{translate:calc(.9 * var(--float-amp, 10px)) calc(.6 * var(--float-amp, 10px))}80%{translate:calc(-.4 * var(--float-amp, 10px)) calc(.9 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-2{0%{translate:0 0}12%{translate:calc(.6 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}30%{translate:calc(1 * var(--float-amp, 10px)) calc(-.4 * var(--float-amp, 10px))}50%{translate:calc(-.3 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}70%{translate:calc(-.8 * var(--float-amp, 10px)) calc(.2 * var(--float-amp, 10px))}88%{translate:calc(.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}@keyframes float-both-3{0%{translate:0 0}17%{translate:calc(-.9 * var(--float-amp, 10px)) calc(.4 * var(--float-amp, 10px))}33%{translate:calc(-.5 * var(--float-amp, 10px)) calc(-.7 * var(--float-amp, 10px))}50%{translate:calc(.7 * var(--float-amp, 10px)) calc(-.9 * var(--float-amp, 10px))}67%{translate:calc(.9 * var(--float-amp, 10px)) calc(.5 * var(--float-amp, 10px))}83%{translate:calc(-.2 * var(--float-amp, 10px)) calc(.8 * var(--float-amp, 10px))}to{translate:0 0}}.animot-presenter.svelte-16ocdv9 *{margin:0;padding:0;box-sizing:border-box}.animot-presenter.svelte-16ocdv9{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;background:transparent;line-height:normal;font-size:16px;font-weight:400;font-style:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-align:left;color:inherit}.animot-canvas-wrapper.svelte-16ocdv9{display:flex;align-items:center;justify-content:center}.animot-canvas.svelte-16ocdv9{position:relative;overflow:hidden}.animot-element.svelte-16ocdv9{position:absolute;box-sizing:border-box;will-change:transform,opacity,left,top,width,height;isolation:isolate}.animot-element.floating.svelte-16ocdv9{animation-duration:var(--float-speed, 3s);animation-timing-function:ease-in-out;animation-iteration-count:infinite;animation-delay:var(--float-delay, 0s)}.animot-code-block.svelte-16ocdv9{width:100%;height:100%;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 8px 32px #0006;margin:0;box-sizing:border-box}.animot-code-block.transparent-bg.svelte-16ocdv9{background:transparent!important;box-shadow:none}.animot-code-block.transparent-bg.svelte-16ocdv9 .animot-code-header:where(.svelte-16ocdv9){background:transparent;border-bottom-color:#ffffff0f}.animot-code-header.svelte-16ocdv9{display:flex;align-items:center;gap:8px;padding:8px 12px;background:#0003;border-bottom:1px solid rgba(255,255,255,.06);flex-shrink:0;min-height:40px}.animot-window-controls.svelte-16ocdv9{display:flex;gap:8px;align-items:center;flex-shrink:0}.macos.svelte-16ocdv9 .animot-control:where(.svelte-16ocdv9){width:12px;height:12px;border-radius:50%;display:block}.macos.svelte-16ocdv9 .animot-control.close:where(.svelte-16ocdv9){background:#ff5f57}.macos.svelte-16ocdv9 .animot-control.minimize:where(.svelte-16ocdv9){background:#febc2e}.macos.svelte-16ocdv9 .animot-control.maximize:where(.svelte-16ocdv9){background:#28c840}.windows.svelte-16ocdv9 .animot-window-controls:where(.svelte-16ocdv9){order:99;margin-left:auto;gap:0}.windows.svelte-16ocdv9 .animot-control:where(.svelte-16ocdv9){display:flex;align-items:center;justify-content:center;width:28px;height:24px;border-radius:4px;color:#ffffff73}.animot-filename-tab.svelte-16ocdv9{display:flex;align-items:center;gap:6px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:4px 10px;max-width:220px;color:#fff6}.animot-file-icon.svelte-16ocdv9{flex-shrink:0}.animot-filename.svelte-16ocdv9{color:#ffffff8c;font-size:12px;line-height:18px}.animot-copy-code-btn.svelte-16ocdv9{display:flex;align-items:center;gap:5px;height:28px;padding:0 8px;margin-left:auto;background:#ffffff0f;border:1px solid rgba(255,255,255,.1);border-radius:6px;color:#fff6;cursor:pointer;opacity:0;transition:opacity .2s,background .15s,color .15s;flex-shrink:0;font-size:12px;font-family:inherit;white-space:nowrap}.animot-copy-code-btn.svelte-16ocdv9:hover{background:#ffffff1f;color:#fffc}.animot-copy-code-btn.svelte-16ocdv9 svg:where(.svelte-16ocdv9){width:14px;height:14px;flex-shrink:0}.animot-copy-code-btn.svelte-16ocdv9 .animot-check-icon:where(.svelte-16ocdv9){display:none}.animot-copy-code-btn.svelte-16ocdv9 .animot-copied-label:where(.svelte-16ocdv9){display:none}.animot-copy-code-btn.copied.svelte-16ocdv9 .animot-copy-icon:where(.svelte-16ocdv9){display:none}.animot-copy-code-btn.copied.svelte-16ocdv9 .animot-copy-label:where(.svelte-16ocdv9){display:none}.animot-copy-code-btn.copied.svelte-16ocdv9 .animot-check-icon:where(.svelte-16ocdv9){display:block;color:#4ade80}.animot-copy-code-btn.copied.svelte-16ocdv9 .animot-copied-label:where(.svelte-16ocdv9){display:inline;color:#4ade80}.animot-copy-code-btn.animot-floating.svelte-16ocdv9{position:absolute;top:8px;right:8px;z-index:2}.animot-code-block.svelte-16ocdv9:hover .animot-copy-code-btn:where(.svelte-16ocdv9){opacity:1}.animot-code-content.svelte-16ocdv9{flex:1;overflow:hidden;position:relative}.animot-highlighted-code.svelte-16ocdv9{width:100%;height:100%}.animot-code-content.svelte-16ocdv9 pre,.animot-highlighted-code.svelte-16ocdv9 pre{margin:0;padding:16px;background:transparent!important;line-height:1.6;font-size:inherit;overflow:visible}.animot-highlighted-code.svelte-16ocdv9 code{font-family:inherit;font-size:inherit;font-weight:inherit}.animot-highlighted-code.svelte-16ocdv9 .line-number{display:inline-block;width:2.5em;margin-right:1em;text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;opacity:.6}.animot-text-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;white-space:pre-wrap;word-wrap:break-word}.animot-typewriter-cursor.svelte-16ocdv9{animation:svelte-16ocdv9-animot-blink .7s infinite;font-weight:100}@keyframes svelte-16ocdv9-animot-blink{0%,50%{opacity:1}51%,to{opacity:0}}.animot-arrow-element.svelte-16ocdv9{width:100%;height:100%}.arrow-animate-draw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:1000;animation:svelte-16ocdv9-animot-arrow-draw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-animate-undraw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:0;animation:svelte-16ocdv9-animot-arrow-undraw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-animate-draw-undraw.svelte-16ocdv9 .arrow-path:where(.svelte-16ocdv9){stroke-dasharray:1000;stroke-dashoffset:1000;animation:svelte-16ocdv9-animot-arrow-draw-undraw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-head-styled-draw.svelte-16ocdv9{opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-draw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9){opacity:0;animation:svelte-16ocdv9-animot-arrow-head-appear var(--arrow-anim-duration, .5s) ease-out forwards;animation-delay:calc(var(--arrow-anim-duration, .5s) * .7)}.arrow-animate-undraw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9),.arrow-head-undraw.svelte-16ocdv9{opacity:1;animation:svelte-16ocdv9-animot-arrow-head-disappear var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-animate-draw-undraw.svelte-16ocdv9 .arrow-head:where(.svelte-16ocdv9),.arrow-head-draw-undraw.svelte-16ocdv9{opacity:0;animation:svelte-16ocdv9-animot-arrow-head-draw-undraw var(--arrow-anim-duration, .5s) ease-out forwards}.arrow-animate-grow.svelte-16ocdv9{transform-origin:left center;animation:svelte-16ocdv9-animot-arrow-grow var(--arrow-anim-duration, .5s) ease-out forwards}@keyframes svelte-16ocdv9-animot-arrow-draw{to{stroke-dashoffset:0}}@keyframes svelte-16ocdv9-animot-arrow-undraw{0%{stroke-dashoffset:0}to{stroke-dashoffset:1000}}@keyframes svelte-16ocdv9-animot-arrow-draw-undraw{0%{stroke-dashoffset:1000}50%{stroke-dashoffset:0}to{stroke-dashoffset:1000}}@keyframes svelte-16ocdv9-animot-arrow-head-appear{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-arrow-head-disappear{0%{opacity:1}70%{opacity:1}to{opacity:0}}@keyframes svelte-16ocdv9-animot-arrow-head-draw-undraw{0%{opacity:0}35%{opacity:1}65%{opacity:1}to{opacity:0}}@keyframes svelte-16ocdv9-animot-arrow-grow{0%{transform:scaleX(0);opacity:0}to{transform:scaleX(1);opacity:1}}.animot-image-element.svelte-16ocdv9{width:100%;height:100%;display:block}.animot-shape-element.svelte-16ocdv9{width:100%;height:100%;display:block;overflow:visible}.animot-canvas.svelte-16ocdv9{--transition-duration: .5s;transition:transform calc(var(--transition-duration) * .4) ease,opacity calc(var(--transition-duration) * .4) ease}.animot-canvas.transition-fade-out.svelte-16ocdv9{opacity:0}.animot-canvas.transition-fade-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-fadeIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.forward.svelte-16ocdv9{transform:translate(-100%);opacity:0}.animot-canvas.transition-slide-left-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromRight calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-left-out.backward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-left-in.backward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-right-out.forward.svelte-16ocdv9{transform:translate(100%);opacity:0}.animot-canvas.transition-slide-right-in.forward.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromLeft calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-up-out.svelte-16ocdv9{transform:translateY(-100%);opacity:0}.animot-canvas.transition-slide-up-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromBottom calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-slide-down-out.svelte-16ocdv9{transform:translateY(100%);opacity:0}.animot-canvas.transition-slide-down-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-slideInFromTop calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-in-out.svelte-16ocdv9{transform:scale(.5);opacity:0}.animot-canvas.transition-zoom-in-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomIn calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-zoom-out-out.svelte-16ocdv9{transform:scale(1.5);opacity:0}.animot-canvas.transition-zoom-out-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-zoomOut calc(var(--transition-duration) * .6) ease forwards}.animot-canvas.transition-flip-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromBottom{0%{transform:translateY(100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-slideInFromTop{0%{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomIn{0%{transform:scale(.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-zoomOut{0%{transform:scale(1.5);opacity:0}to{transform:scale(1);opacity:1}}@keyframes svelte-16ocdv9-animot-flipIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-canvas.transition-flip-x-out.svelte-16ocdv9{transform:perspective(1000px) rotateX(90deg);opacity:0}.animot-canvas.transition-flip-x-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipXIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipXIn{0%{transform:perspective(1000px) rotateX(-90deg);opacity:0}to{transform:perspective(1000px) rotateX(0);opacity:1}}.animot-canvas.transition-flip-y-out.svelte-16ocdv9{transform:perspective(1000px) rotateY(90deg);opacity:0}.animot-canvas.transition-flip-y-in.svelte-16ocdv9{animation:svelte-16ocdv9-animot-flipYIn calc(var(--transition-duration) * .6) ease forwards}@keyframes svelte-16ocdv9-animot-flipYIn{0%{transform:perspective(1000px) rotateY(-90deg);opacity:0}to{transform:perspective(1000px) rotateY(0);opacity:1}}.animot-svg-element.svelte-16ocdv9{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.animot-svg-element.svelte-16ocdv9 svg{width:100%;height:100%}.animot-controls.svelte-16ocdv9{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:8px;padding:8px 16px;background:#000000b3;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:10px;opacity:0;transition:opacity .3s ease .15s;z-index:100}.animot-presenter.svelte-16ocdv9:hover .animot-controls:where(.svelte-16ocdv9),.animot-menu-visible.svelte-16ocdv9 .animot-controls:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9){display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;border:none;cursor:pointer;background:#ffffff1a;color:#fff;transition:background .2s}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):hover:not(:disabled){background:#fff3}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-controls.svelte-16ocdv9 button.active:where(.svelte-16ocdv9){background:#6366f199}.animot-controls.svelte-16ocdv9 button:where(.svelte-16ocdv9) svg:where(.svelte-16ocdv9){width:16px;height:16px}.animot-slide-indicator.svelte-16ocdv9{font-size:12px;color:#fff;min-width:50px;text-align:center;font-family:system-ui,sans-serif}.animot-arrow.svelte-16ocdv9{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;background:#00000080;color:#fff;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .3s .15s;z-index:100;padding:0;margin:0}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-presenter.svelte-16ocdv9:hover .animot-arrow:where(.svelte-16ocdv9):disabled{opacity:.3;cursor:not-allowed}.animot-arrow.svelte-16ocdv9:hover:not(:disabled){background:#000000b3}.animot-arrow.svelte-16ocdv9 svg:where(.svelte-16ocdv9){width:20px;height:20px}.animot-arrow-left.svelte-16ocdv9{left:8px}.animot-arrow-right.svelte-16ocdv9{right:8px}.animot-progress-bar.svelte-16ocdv9{position:absolute;bottom:0;left:0;right:0;height:3px;background:#ffffff1a;z-index:100;opacity:0;transition:opacity .3s .15s}.animot-presenter.svelte-16ocdv9:hover .animot-progress-bar:where(.svelte-16ocdv9){opacity:1;transition-delay:0s}.animot-progress-fill.svelte-16ocdv9{height:100%;background:linear-gradient(135deg,#7c3aed,#ec4899);transition:width .6s ease}.animot-loading.svelte-16ocdv9{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.animot-spinner.svelte-16ocdv9{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:#7c3aed;border-radius:50%;animation:svelte-16ocdv9-animot-spin .8s linear infinite}@keyframes svelte-16ocdv9-animot-spin{to{transform:rotate(360deg)}}.animot-error.svelte-16ocdv9{color:#ef4444;padding:20px;text-align:center;font-family:system-ui,sans-serif}