@dotcraft/avatar 0.6.3 → 0.6.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.
Files changed (83) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +46 -43
  3. package/dist/accessories.css +14 -14
  4. package/dist/avatar.css +62 -62
  5. package/dist/composer/ComposerMascot.d.ts +1 -1
  6. package/dist/composer/ComposerMascot.d.ts.map +1 -1
  7. package/dist/composer/ComposerMascot.js +33 -107
  8. package/dist/composer/ComposerMascot.js.map +1 -1
  9. package/dist/composer/MascotIdleStage.d.ts +11 -0
  10. package/dist/composer/MascotIdleStage.d.ts.map +1 -0
  11. package/dist/composer/MascotIdleStage.js +9 -0
  12. package/dist/composer/MascotIdleStage.js.map +1 -0
  13. package/dist/composer/constants.d.ts +12 -1
  14. package/dist/composer/constants.d.ts.map +1 -1
  15. package/dist/composer/constants.js +9 -4
  16. package/dist/composer/constants.js.map +1 -1
  17. package/dist/composer/styles/energy.css +99 -99
  18. package/dist/composer/styles/expressions.css +20 -20
  19. package/dist/composer/styles/feedback.css +188 -188
  20. package/dist/composer/styles/focus.css +72 -72
  21. package/dist/composer/styles/foundation.css +301 -287
  22. package/dist/composer/styles/idle.css +449 -438
  23. package/dist/composer/styles/overdrive.css +264 -264
  24. package/dist/composer/styles/props.css +21 -21
  25. package/dist/composer/types.d.ts +1 -0
  26. package/dist/composer/types.d.ts.map +1 -1
  27. package/dist/composer/useComposerAvatarBehavior.d.ts +1 -0
  28. package/dist/composer/useComposerAvatarBehavior.d.ts.map +1 -1
  29. package/dist/composer/useComposerAvatarBehavior.js +2 -2
  30. package/dist/composer/useComposerAvatarBehavior.js.map +1 -1
  31. package/dist/composer/useMascotActiveIdle.d.ts +33 -0
  32. package/dist/composer/useMascotActiveIdle.d.ts.map +1 -0
  33. package/dist/composer/useMascotActiveIdle.js +128 -0
  34. package/dist/composer/useMascotActiveIdle.js.map +1 -0
  35. package/dist/react.d.ts +5 -0
  36. package/dist/react.d.ts.map +1 -1
  37. package/dist/react.js +4 -0
  38. package/dist/react.js.map +1 -1
  39. package/dist/rig.css +265 -265
  40. package/dist/styles.css +13 -13
  41. package/package.json +56 -55
  42. package/src/AnimatedDecoration.tsx +43 -0
  43. package/src/AppearanceRig.tsx +15 -0
  44. package/src/Avatar.tsx +85 -0
  45. package/src/DecorationShapes.tsx +8 -0
  46. package/src/Decorations.tsx +31 -0
  47. package/src/Faces.tsx +37 -0
  48. package/src/HatDecorations.tsx +75 -0
  49. package/src/HeldDecorations.tsx +48 -0
  50. package/src/MascotRig.tsx +191 -0
  51. package/src/ObjectDecorations.tsx +55 -0
  52. package/src/accessories.css +14 -0
  53. package/src/appearanceModel.ts +65 -0
  54. package/src/avatar.css +62 -0
  55. package/src/characters.ts +5 -0
  56. package/src/composer/ComposerMascot.tsx +445 -0
  57. package/src/composer/ComposerMascotShadow.tsx +5 -0
  58. package/src/composer/MascotIdleStage.tsx +23 -0
  59. package/src/composer/constants.ts +89 -0
  60. package/src/composer/mascotHandoff.ts +19 -0
  61. package/src/composer/styles/energy.css +99 -0
  62. package/src/composer/styles/expressions.css +20 -0
  63. package/src/composer/styles/feedback.css +188 -0
  64. package/src/composer/styles/focus.css +72 -0
  65. package/src/composer/styles/foundation.css +301 -0
  66. package/src/composer/styles/idle.css +449 -0
  67. package/src/composer/styles/overdrive.css +264 -0
  68. package/src/composer/styles/props.css +21 -0
  69. package/src/composer/types.ts +32 -0
  70. package/src/composer/useComposerAvatarBehavior.ts +98 -0
  71. package/src/composer/useComposerMotion.ts +15 -0
  72. package/src/composer/useComposerProfile.ts +50 -0
  73. package/src/composer/useMascotActiveIdle.ts +170 -0
  74. package/src/decorationCatalog.ts +45 -0
  75. package/src/decorationMotion.ts +68 -0
  76. package/src/environment.ts +46 -0
  77. package/src/index.ts +5 -0
  78. package/src/palette.ts +52 -0
  79. package/src/react.ts +11 -0
  80. package/src/rig.css +265 -0
  81. package/src/styles.css +13 -0
  82. package/src/useEventReplay.ts +13 -0
  83. package/src/useGesture.ts +51 -0
@@ -0,0 +1,89 @@
1
+ export const MASCOT_SIZE = 58
2
+ export const MASCOT_SCALE = 0.75
3
+ export const MASCOT_HIDDEN_RATIO = 0.06
4
+ export const MASCOT_RAISE = 3
5
+ export const MASCOT_SLEEP_AFTER_MS = 90_000
6
+ export const MASCOT_WAVE_DURATION_MS = 1_600
7
+ export const MASCOT_ACTIVE_IDLE_MIN_MS = 35_000
8
+ export const MASCOT_ACTIVE_IDLE_JITTER_MS = 30_000
9
+ export const MASCOT_ACTIVE_IDLE_ACTIVITY_THROTTLE_MS = 500
10
+
11
+ export type MascotActiveIdleMotion = 'hop' | 'rocket' | 'hover'
12
+ export type MascotActiveIdlePhase = 'outbound' | 'away' | 'inbound'
13
+ export type MascotIdleDirection = 'left' | 'right'
14
+ export const MASCOT_ACTIVE_IDLE_MOTIONS: readonly MascotActiveIdleMotion[] = ['hop', 'rocket', 'hover']
15
+
16
+ export interface MascotActiveIdleState {
17
+ motion: MascotActiveIdleMotion
18
+ phase: MascotActiveIdlePhase
19
+ }
20
+
21
+ export interface MascotActiveIdle extends MascotActiveIdleState {
22
+ /** Where the trip heads; the inbound leg travels the other way. */
23
+ direction: MascotIdleDirection
24
+ travel: MascotIdleDirection
25
+ }
26
+
27
+ export interface MascotActiveIdlePlan {
28
+ direction?: MascotIdleDirection
29
+ motions?: readonly MascotActiveIdleMotion[]
30
+ }
31
+
32
+ export interface MascotProfileTransition {
33
+ fromAccent: string
34
+ toAccent: string
35
+ }
36
+
37
+ export const MASCOT_PROFILE_TRANSITION_SWAP_MS = 620
38
+ export const MASCOT_PROFILE_TRANSITION_DURATION_MS = 1240
39
+
40
+ export const MASCOT_ACTIVE_IDLE_TRAVEL_MS: Record<MascotActiveIdleMotion, number> = {
41
+ hop: 2400,
42
+ rocket: 1800,
43
+ hover: 1800
44
+ }
45
+
46
+ export const MASCOT_ACTIVE_IDLE_HOLD_MS: Record<MascotActiveIdleMotion, number> = {
47
+ hop: 1400,
48
+ rocket: 1400,
49
+ hover: 2800
50
+ }
51
+
52
+ export function pickMascotActiveIdle(
53
+ random: number,
54
+ previous: MascotActiveIdleMotion | null,
55
+ allowed: readonly MascotActiveIdleMotion[] = MASCOT_ACTIVE_IDLE_MOTIONS
56
+ ): MascotActiveIdleMotion {
57
+ const preferred: MascotActiveIdleMotion = random < 0.65 ? 'hop' : random < 0.9 ? 'rocket' : 'hover'
58
+ const selected = allowed.includes(preferred)
59
+ ? preferred
60
+ : MASCOT_ACTIVE_IDLE_MOTIONS.find((motion) => allowed.includes(motion)) ?? preferred
61
+ if (selected !== previous || allowed.length < 2) return selected
62
+ const alternate: MascotActiveIdleMotion = selected === 'hop' ? 'rocket' : selected === 'rocket' ? 'hop' : 'rocket'
63
+ return allowed.includes(alternate) ? alternate : allowed.find((motion) => motion !== selected) ?? selected
64
+ }
65
+
66
+ export const MASCOT_SPARKLES = Array.from({ length: 7 }, (_, i) => {
67
+ const angle = ((-150 + i * 40) * Math.PI) / 180
68
+ const radius = 26 + (i % 3) * 9
69
+ return {
70
+ dx: `${(Math.cos(angle) * radius).toFixed(1)}px`,
71
+ dy: `${(Math.sin(angle) * radius - 8).toFixed(1)}px`,
72
+ delay: `${i * 40}ms`
73
+ }
74
+ })
75
+
76
+ export function prefersReducedMotion(): boolean {
77
+ const configuredPreference =
78
+ typeof document !== 'undefined' ? document.documentElement.dataset.reduceMotion : undefined
79
+ if (configuredPreference === 'on') return true
80
+ if (configuredPreference === 'off') return false
81
+
82
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
83
+ ? window.matchMedia('(prefers-reduced-motion: reduce)')?.matches ?? false
84
+ : false
85
+ }
86
+
87
+ export function mascotNameKey(name?: string): string {
88
+ return name?.trim().normalize('NFC') ?? ''
89
+ }
@@ -0,0 +1,19 @@
1
+ interface MascotHandoffRecord {
2
+ top: number
3
+ time: number
4
+ }
5
+
6
+ let record: MascotHandoffRecord | null = null
7
+
8
+ export function recordMascotHandoff(el: HTMLElement): void {
9
+ record = { top: el.getBoundingClientRect().top, time: performance.now() }
10
+ }
11
+
12
+ export function consumeMascotHandoff(el: HTMLElement): number | null {
13
+ if (!record) return null
14
+ const { top, time } = record
15
+ record = null
16
+ if (performance.now() - time > 400) return null
17
+ const dy = top - el.getBoundingClientRect().top
18
+ return Math.abs(dy) < 8 ? null : dy
19
+ }
@@ -0,0 +1,99 @@
1
+ [data-mascot-effort] .dca-robot :is(.dca-paint-body, .dca-paint-mark) stop {
2
+ transition: stop-color 360ms ease;
3
+ }
4
+
5
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(1) {
6
+ animation: composer-mascot-energy-medium-dark 6s ease-in-out infinite;
7
+ }
8
+
9
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(2) {
10
+ animation: composer-mascot-energy-medium-mid 6s ease-in-out infinite;
11
+ }
12
+
13
+ [data-mascot-effort='medium'] .dca-robot .dca-paint-body stop:nth-child(3) {
14
+ animation: composer-mascot-energy-medium-light 6s ease-in-out infinite;
15
+ }
16
+
17
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(1) {
18
+ animation: composer-mascot-energy-high-dark 3.8s ease-in-out infinite;
19
+ }
20
+
21
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(2) {
22
+ animation: composer-mascot-energy-high-mid 3.8s ease-in-out infinite;
23
+ }
24
+
25
+ [data-mascot-effort='high'] .dca-robot .dca-paint-body stop:nth-child(3) {
26
+ animation: composer-mascot-energy-high-light 3.8s ease-in-out infinite;
27
+ }
28
+
29
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(1) {
30
+ animation: composer-mascot-energy-high-mark-dark 3.8s ease-in-out infinite;
31
+ }
32
+
33
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(2),
34
+ [data-mascot-effort='high'] .dca-robot .dca-paint-mark stop:nth-child(3) {
35
+ animation: composer-mascot-energy-high-mark-light 3.8s ease-in-out infinite;
36
+ }
37
+
38
+ [data-mascot-effort='high'] .dca-robot {
39
+ filter: drop-shadow(0 0 5px color-mix(in srgb,
40
+ oklch(from var(--mascot-energy-accent)
41
+ clamp(0.28, calc(l - 0.0112), 0.96)
42
+ clamp(0, calc(c * 1.116), 0.3)
43
+ calc(h - 3.09)) 28%, transparent));
44
+ }
45
+
46
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(1) {
47
+ animation: composer-mascot-energy-extra-dark 2.8s ease-in-out infinite;
48
+ }
49
+
50
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(2) {
51
+ animation: composer-mascot-energy-extra-mid 2.8s ease-in-out infinite;
52
+ }
53
+
54
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-body stop:nth-child(3) {
55
+ animation: composer-mascot-energy-extra-light 2.8s ease-in-out infinite;
56
+ }
57
+
58
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(1) {
59
+ animation: composer-mascot-energy-extra-mark-dark 2.8s ease-in-out infinite;
60
+ }
61
+
62
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(2),
63
+ [data-mascot-effort='extraHigh'] .dca-robot .dca-paint-mark stop:nth-child(3) {
64
+ animation: composer-mascot-energy-extra-mark-light 2.8s ease-in-out infinite;
65
+ }
66
+
67
+ [data-mascot-effort='extraHigh'] .dca-robot {
68
+ filter: drop-shadow(0 0 7px color-mix(in srgb,
69
+ oklch(from var(--mascot-energy-accent)
70
+ clamp(0.28, calc(l - 0.0456), 0.96)
71
+ clamp(0, calc(c * 1.231), 0.3)
72
+ calc(h + 27.55)) 36%, transparent));
73
+ }
74
+
75
+ [data-mascot-context='max'] .dca-robot {
76
+ animation: composer-mascot-max-overdrive-edge 3.8s ease-in-out infinite;
77
+ will-change: filter;
78
+ }
79
+
80
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(1) {
81
+ animation: composer-mascot-max-coat-dark 3.8s ease-in-out infinite;
82
+ }
83
+
84
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(2) {
85
+ animation: composer-mascot-max-coat-mid 3.8s ease-in-out infinite;
86
+ }
87
+
88
+ [data-mascot-context='max'] .dca-robot .dca-paint-body stop:nth-child(3) {
89
+ animation: composer-mascot-max-coat-light 3.8s ease-in-out infinite;
90
+ }
91
+
92
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(1) {
93
+ animation: composer-mascot-max-coat-mark-dark 3.8s ease-in-out infinite;
94
+ }
95
+
96
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(2),
97
+ [data-mascot-context='max'] .dca-robot .dca-paint-mark stop:nth-child(3) {
98
+ animation: composer-mascot-max-coat-mark-light 3.8s ease-in-out infinite;
99
+ }
@@ -0,0 +1,20 @@
1
+ @keyframes composer-mascot-nod {
2
+ 0%, 100% { transform: translateY(0); }
3
+ 40% { transform: translateY(2.5px) scale(1.015, 0.985); }
4
+ }
5
+
6
+ .composer-mascot-nod {
7
+ animation: composer-mascot-nod 140ms ease-out;
8
+ transform-origin: bottom center;
9
+ }
10
+
11
+ @keyframes composer-mascot-think-sway {
12
+ 0%, 100% { transform: rotate(-2.2deg); }
13
+ 50% { transform: rotate(2.2deg); }
14
+ }
15
+
16
+ .composer-mascot-think {
17
+ animation: composer-mascot-think-sway 2.6s ease-in-out infinite;
18
+ transform-origin: bottom center;
19
+ will-change: transform;
20
+ }
@@ -0,0 +1,188 @@
1
+ @keyframes composer-mascot-cheer {
2
+ 0% {
3
+ transform: translateY(0) scale(1, 1);
4
+ }
5
+ 12% {
6
+ transform: translateY(2px) scale(1.08, 0.9);
7
+ }
8
+ 30% {
9
+ transform: translateY(-13px) scale(0.96, 1.06);
10
+ }
11
+ 46% {
12
+ transform: translateY(0) scale(1.05, 0.94);
13
+ }
14
+ 60% {
15
+ transform: translateY(-8px) scale(0.98, 1.03);
16
+ }
17
+ 74% {
18
+ transform: translateY(0) scale(1.02, 0.98);
19
+ }
20
+ 100% {
21
+ transform: translateY(0) scale(1, 1);
22
+ }
23
+ }
24
+
25
+ .composer-mascot-cheer {
26
+ animation: composer-mascot-cheer 1s ease-out;
27
+ transform-origin: bottom center;
28
+ }
29
+
30
+ .composer-mascot-sparkles {
31
+ position: absolute;
32
+ inset: 0;
33
+ pointer-events: none;
34
+ }
35
+
36
+ .composer-mascot-sparkles i {
37
+ position: absolute;
38
+ left: 50%;
39
+ top: 6%;
40
+ width: 7px;
41
+ height: 7px;
42
+ background: var(--dca-theme-success);
43
+ clip-path: polygon(50% 0, 64% 36%, 100% 50%, 64% 64%, 50% 100%, 36% 64%, 0 50%, 36% 36%);
44
+ animation: composer-mascot-sparkle 900ms ease-out forwards;
45
+ }
46
+
47
+ @keyframes composer-mascot-sparkle {
48
+ 0% {
49
+ opacity: 0;
50
+ transform: translate(0, 0) scale(0.4) rotate(0);
51
+ }
52
+ 18% {
53
+ opacity: 1;
54
+ }
55
+ 100% {
56
+ opacity: 0;
57
+ transform: translate(var(--dx), var(--dy)) scale(1) rotate(160deg);
58
+ }
59
+ }
60
+
61
+ @keyframes composer-mascot-shake {
62
+ 0%,
63
+ 100% {
64
+ transform: translateX(0) rotate(0);
65
+ }
66
+ 18% {
67
+ transform: translateX(-5px) rotate(-2.4deg);
68
+ }
69
+ 38% {
70
+ transform: translateX(5px) rotate(2.4deg);
71
+ }
72
+ 58% {
73
+ transform: translateX(-4px) rotate(-1.8deg);
74
+ }
75
+ 78% {
76
+ transform: translateX(3px) rotate(1.2deg);
77
+ }
78
+ }
79
+
80
+ .composer-mascot-shake {
81
+ animation: composer-mascot-shake 520ms ease-in-out;
82
+ transform-origin: bottom center;
83
+ }
84
+
85
+ @keyframes composer-mascot-eager-hop {
86
+ 0%,
87
+ 100% {
88
+ transform: translateY(0) scale(1, 1);
89
+ }
90
+ 50% {
91
+ transform: translateY(-4px) scale(0.99, 1.02);
92
+ }
93
+ }
94
+
95
+ .composer-mascot-eager {
96
+ animation: composer-mascot-eager-hop 460ms ease-in-out infinite;
97
+ transform-origin: bottom center;
98
+ }
99
+
100
+ @keyframes composer-mascot-sleep-breathe {
101
+ 0%,
102
+ 100% {
103
+ transform: scale(1);
104
+ }
105
+ 50% {
106
+ transform: scale(1.05, 1.06);
107
+ }
108
+ }
109
+
110
+ .composer-mascot-sleep-breathe {
111
+ animation: composer-mascot-sleep-breathe 5.6s ease-in-out infinite;
112
+ transform-origin: bottom center;
113
+ }
114
+
115
+ .composer-mascot-zzz {
116
+ position: absolute;
117
+ inset: 0;
118
+ pointer-events: none;
119
+ }
120
+
121
+ .composer-mascot-zzz span {
122
+ position: absolute;
123
+ right: -2px;
124
+ top: 2px;
125
+ font-family: Georgia, serif;
126
+ font-size: 13px;
127
+ font-weight: 700;
128
+ color: var(--dca-theme-dimmed);
129
+ opacity: 0;
130
+ animation: composer-mascot-zzz 2.8s ease-out infinite;
131
+ }
132
+
133
+ .composer-mascot-zzz span:nth-child(2) {
134
+ animation-delay: 0.9s;
135
+ right: 2px;
136
+ }
137
+
138
+ .composer-mascot-zzz span:nth-child(3) {
139
+ animation-delay: 1.8s;
140
+ right: 6px;
141
+ }
142
+
143
+ @keyframes composer-mascot-zzz {
144
+ 0% {
145
+ opacity: 0;
146
+ transform: translate(0, 0) scale(0.7);
147
+ }
148
+ 25% {
149
+ opacity: 0.9;
150
+ }
151
+ 100% {
152
+ opacity: 0;
153
+ transform: translate(13px, -26px) scale(1.15);
154
+ }
155
+ }
156
+
157
+ @keyframes composer-mascot-startle {
158
+ 0% {
159
+ transform: translateY(0) scale(1);
160
+ }
161
+ 30% {
162
+ transform: translateY(-7px) scale(1.1, 0.96);
163
+ }
164
+ 60% {
165
+ transform: translateY(0) scale(0.97, 1.03);
166
+ }
167
+ 100% {
168
+ transform: translateY(0) scale(1);
169
+ }
170
+ }
171
+
172
+ .composer-mascot-startle {
173
+ animation: composer-mascot-startle 320ms ease-out;
174
+ transform-origin: bottom center;
175
+ }
176
+
177
+ @keyframes composer-mascot-push-lift {
178
+ 0% { transform: translateY(0) scale(1); }
179
+ 38% { transform: translateY(-7px) scale(0.98, 1.035); }
180
+ 72% { transform: translateY(1px) scale(1.025, 0.975); }
181
+ 88% { transform: translateY(-1px) scale(0.995, 1.01); }
182
+ 100% { transform: translateY(0) scale(1); }
183
+ }
184
+
185
+ .composer-mascot-push-lift {
186
+ animation: composer-mascot-push-lift 360ms cubic-bezier(0.22, 0.72, 0.24, 1);
187
+ transform-origin: bottom center;
188
+ }
@@ -0,0 +1,72 @@
1
+ @keyframes composer-mascot-launch {
2
+ 0% {
3
+ transform: translateY(0) scale(1, 1);
4
+ }
5
+ 14% {
6
+ transform: translateY(3px) scale(1.14, 0.8);
7
+ }
8
+ 38% {
9
+ transform: translateY(-20px) scale(0.9, 1.14);
10
+ }
11
+ 58% {
12
+ transform: translateY(0) scale(1.1, 0.88);
13
+ }
14
+ 74% {
15
+ transform: translateY(-6px) scale(0.98, 1.04);
16
+ }
17
+ 88% {
18
+ transform: translateY(0) scale(1.02, 0.98);
19
+ }
20
+ 100% {
21
+ transform: translateY(0) scale(1, 1);
22
+ }
23
+ }
24
+
25
+ .composer-mascot-launch {
26
+ animation: composer-mascot-launch 820ms cubic-bezier(0.3, 0.9, 0.4, 1);
27
+ transform-origin: bottom center;
28
+ }
29
+
30
+ @keyframes composer-mascot-jelly {
31
+ 0% {
32
+ transform: scale(1, 1);
33
+ }
34
+ 25% {
35
+ transform: scale(1.16, 0.84);
36
+ }
37
+ 45% {
38
+ transform: scale(0.9, 1.1);
39
+ }
40
+ 62% {
41
+ transform: scale(1.06, 0.94);
42
+ }
43
+ 78% {
44
+ transform: scale(0.98, 1.02);
45
+ }
46
+ 100% {
47
+ transform: scale(1, 1);
48
+ }
49
+ }
50
+
51
+ .composer-mascot-jelly {
52
+ transform-origin: bottom center;
53
+ }
54
+
55
+ .composer-mascot-jelly:hover {
56
+ animation: composer-mascot-jelly 600ms ease-out;
57
+ }
58
+
59
+ @keyframes composer-mascot-bubble-in {
60
+ 0% {
61
+ opacity: 0;
62
+ transform: translateY(6px) scale(0.94);
63
+ }
64
+ 100% {
65
+ opacity: 1;
66
+ transform: translateY(0) scale(1);
67
+ }
68
+ }
69
+
70
+ .composer-mascot-bubble {
71
+ animation: composer-mascot-bubble-in 260ms cubic-bezier(0.34, 1.56, 0.64, 1);
72
+ }