create-mcp-use-app 2.0.0-beta.3 → 2.0.0-beta.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 (45) hide show
  1. package/README.md +68 -37
  2. package/dist/index.js +10 -8
  3. package/dist/templates/blank/README.md +1 -1
  4. package/dist/templates/blank/tsconfig.json +2 -1
  5. package/dist/templates/mcp-apps/README.md +1 -1
  6. package/dist/templates/mcp-apps/index.ts +71 -101
  7. package/dist/templates/mcp-apps/mcp-env.d.ts +1 -1
  8. package/dist/templates/mcp-apps/public/logo-mcp-use.svg +8 -0
  9. package/dist/templates/mcp-apps/tsconfig.json +2 -1
  10. package/dist/templates/mcp-apps/views/my-view/MeshGradient.tsx +299 -0
  11. package/dist/templates/mcp-apps/views/my-view/Tooltip.tsx +75 -0
  12. package/dist/templates/mcp-apps/views/my-view/icons.tsx +142 -0
  13. package/dist/templates/mcp-apps/views/my-view/view.css +5 -0
  14. package/dist/templates/mcp-apps/views/my-view/view.tsx +396 -0
  15. package/dist/templates/{starter → mcp-server}/README.md +2 -2
  16. package/dist/templates/{starter → mcp-server}/index.ts +0 -18
  17. package/dist/templates/{starter → mcp-server}/tsconfig.json +2 -1
  18. package/package.json +2 -2
  19. package/dist/templates/mcp-apps/public/fruits/apple.webp +0 -0
  20. package/dist/templates/mcp-apps/public/fruits/apricot.webp +0 -0
  21. package/dist/templates/mcp-apps/public/fruits/avocado.webp +0 -0
  22. package/dist/templates/mcp-apps/public/fruits/banana.webp +0 -0
  23. package/dist/templates/mcp-apps/public/fruits/blueberry.webp +0 -0
  24. package/dist/templates/mcp-apps/public/fruits/cherries.webp +0 -0
  25. package/dist/templates/mcp-apps/public/fruits/coconut.webp +0 -0
  26. package/dist/templates/mcp-apps/public/fruits/grapes.webp +0 -0
  27. package/dist/templates/mcp-apps/public/fruits/lemon.webp +0 -0
  28. package/dist/templates/mcp-apps/public/fruits/mango.webp +0 -0
  29. package/dist/templates/mcp-apps/public/fruits/orange.webp +0 -0
  30. package/dist/templates/mcp-apps/public/fruits/pear.webp +0 -0
  31. package/dist/templates/mcp-apps/public/fruits/pineapple.webp +0 -0
  32. package/dist/templates/mcp-apps/public/fruits/plum.webp +0 -0
  33. package/dist/templates/mcp-apps/public/fruits/strawberry.webp +0 -0
  34. package/dist/templates/mcp-apps/public/fruits/watermelon.webp +0 -0
  35. package/dist/templates/mcp-apps/views/product-search-result/components/Accordion.tsx +0 -35
  36. package/dist/templates/mcp-apps/views/product-search-result/components/AccordionItem.tsx +0 -35
  37. package/dist/templates/mcp-apps/views/product-search-result/components/Carousel.tsx +0 -52
  38. package/dist/templates/mcp-apps/views/product-search-result/components/CarouselItem.tsx +0 -91
  39. package/dist/templates/mcp-apps/views/product-search-result/components/CarouselSkeleton.tsx +0 -18
  40. package/dist/templates/mcp-apps/views/product-search-result/hooks/useCarouselAnimation.ts +0 -72
  41. package/dist/templates/mcp-apps/views/product-search-result/view.css +0 -81
  42. package/dist/templates/mcp-apps/views/product-search-result/view.tsx +0 -371
  43. /package/dist/templates/{starter → mcp-server}/gitignore +0 -0
  44. /package/dist/templates/{starter → mcp-server}/package.json +0 -0
  45. /package/dist/templates/{starter → mcp-server}/public/icon.svg +0 -0
@@ -0,0 +1,299 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ // Hero mesh palette (light landing surfaces)
4
+ const MESH_COLORS = ["#e0eaff", "#f9ffbd", "#dedede", "#ffffff"];
5
+ const HERO_MESH_SPEED = 1.15;
6
+ const HERO_MESH_SWIRL = 0.95;
7
+ const ORB_MAX_WIDTH_PX = 800;
8
+
9
+ const VERT = `#version 300 es
10
+ precision mediump float;
11
+ in vec4 a_position;
12
+ uniform vec2 u_resolution;
13
+ out vec2 v_objectUV;
14
+ void main(){
15
+ gl_Position=a_position;
16
+ vec2 uv=a_position.xy*0.5;
17
+ float box=min(u_resolution.x,u_resolution.y);
18
+ vec2 scale=box>0.0?u_resolution/box:vec2(1.);
19
+ v_objectUV=uv*scale;
20
+ }`;
21
+
22
+ const FRAG = `#version 300 es
23
+ precision mediump float;
24
+ uniform float u_time;
25
+ uniform vec4 u_colors[4];
26
+ uniform float u_colorsCount;
27
+ uniform float u_distortion;
28
+ uniform float u_swirl;
29
+ uniform float u_grainMixer;
30
+ uniform float u_grainOverlay;
31
+ in vec2 v_objectUV;
32
+ out vec4 fragColor;
33
+ vec2 rotate(vec2 uv,float th){return mat2(cos(th),sin(th),-sin(th),cos(th))*uv;}
34
+ float hash21(vec2 p){
35
+ p=fract(p*vec2(0.3183099,0.3678794))+0.1;
36
+ p+=dot(p,p+19.19);
37
+ return fract(p.x*p.y);
38
+ }
39
+ float valueNoise(vec2 st){
40
+ vec2 i=floor(st);vec2 f=fract(st);
41
+ float a=hash21(i),b=hash21(i+vec2(1.,0.)),c=hash21(i+vec2(0.,1.)),d=hash21(i+vec2(1.,1.));
42
+ vec2 u=f*f*(3.-2.*f);
43
+ return mix(mix(a,b,u.x),mix(c,d,u.x),u.y);
44
+ }
45
+ vec2 getPosition(int i,float t){
46
+ float a=float(i)*.37,b=.6+fract(float(i)/3.)*.9,c=.8+fract(float(i+1)/4.);
47
+ float x=sin(t*b+a),y=cos(t*c+a*1.5);
48
+ return .5+.5*vec2(x,y);
49
+ }
50
+ void main(){
51
+ vec2 uv=v_objectUV;uv+=.5;
52
+ vec2 grainUV=uv*1000.;
53
+ float grain=valueNoise(grainUV);
54
+ float mixerGrain=.4*u_grainMixer*(grain-.5);
55
+ float t=.5*(u_time+41.5);
56
+ float radius=smoothstep(0.,1.,length(uv-.5));
57
+ float center=1.-radius;
58
+ for(float i=1.;i<=2.;i++){
59
+ uv.x+=u_distortion*center/i*sin(t+i*.4*smoothstep(0.,1.,uv.y))*cos(.2*t+i*2.4*smoothstep(0.,1.,uv.y));
60
+ uv.y+=u_distortion*center/i*cos(t+i*2.*smoothstep(0.,1.,uv.x));
61
+ }
62
+ vec2 uvR=uv-vec2(.5);
63
+ float angle=3.*u_swirl*radius;
64
+ uvR=rotate(uvR,-angle);uvR+=vec2(.5);
65
+ vec3 color=vec3(0.);float opacity=0.,totalWeight=0.;
66
+ for(int i=0;i<4;i++){
67
+ if(i>=int(u_colorsCount))break;
68
+ vec2 pos=getPosition(i,t)+mixerGrain;
69
+ vec3 cf=u_colors[i].rgb*u_colors[i].a;
70
+ float opacityF=u_colors[i].a;
71
+ float dist=length(uvR-pos);
72
+ dist=pow(dist,3.5);
73
+ float w=1./(dist+1e-3);
74
+ color+=cf*w;opacity+=opacityF*w;totalWeight+=w;
75
+ }
76
+ color/=max(1e-4,totalWeight);
77
+ opacity/=max(1e-4,totalWeight);
78
+ float grainO=valueNoise(rotate(grainUV,1.)+vec2(3.));
79
+ grainO=mix(grainO,valueNoise(rotate(grainUV,2.)+vec2(-1.)),.5);
80
+ grainO=pow(grainO,1.3);
81
+ float grainOV=grainO*2.-1.;
82
+ vec3 grainOC=vec3(step(0.,grainOV));
83
+ float grainOS=u_grainOverlay*abs(grainOV);
84
+ grainOS=pow(grainOS,.8);
85
+ color=mix(color,grainOC,.35*grainOS);
86
+ opacity+=.5*grainOS;
87
+ fragColor=vec4(color,clamp(opacity,0.,1.));
88
+ }`;
89
+
90
+ function hexToRgba(hex: string): [number, number, number, number] {
91
+ return [
92
+ parseInt(hex.slice(1, 3), 16) / 255,
93
+ parseInt(hex.slice(3, 5), 16) / 255,
94
+ parseInt(hex.slice(5, 7), 16) / 255,
95
+ 1,
96
+ ];
97
+ }
98
+
99
+ /** WebGL2 mesh gradient canvas (self-contained; no external deps). */
100
+ function MeshGradientCanvas({
101
+ className,
102
+ colors = MESH_COLORS,
103
+ distortion = 0.8,
104
+ swirl = 0.1,
105
+ grainMixer = 0,
106
+ grainOverlay = 0.2,
107
+ speed = 1,
108
+ }: {
109
+ className?: string;
110
+ colors?: string[];
111
+ distortion?: number;
112
+ swirl?: number;
113
+ grainMixer?: number;
114
+ grainOverlay?: number;
115
+ speed?: number;
116
+ }) {
117
+ const canvasRef = useRef<HTMLCanvasElement>(null);
118
+ const wrapRef = useRef<HTMLDivElement>(null);
119
+ const propsRef = useRef({
120
+ colors,
121
+ distortion,
122
+ swirl,
123
+ grainMixer,
124
+ grainOverlay,
125
+ speed,
126
+ });
127
+ propsRef.current = {
128
+ colors,
129
+ distortion,
130
+ swirl,
131
+ grainMixer,
132
+ grainOverlay,
133
+ speed,
134
+ };
135
+
136
+ useEffect(() => {
137
+ const canvas = canvasRef.current;
138
+ const wrap = wrapRef.current;
139
+ if (!canvas || !wrap) return;
140
+
141
+ const gl = canvas.getContext("webgl2");
142
+ if (!gl) return;
143
+
144
+ const compileShader = (type: number, src: string) => {
145
+ const shader = gl.createShader(type);
146
+ if (!shader) return null;
147
+ gl.shaderSource(shader, src);
148
+ gl.compileShader(shader);
149
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
150
+ console.error(gl.getShaderInfoLog(shader));
151
+ return null;
152
+ }
153
+ return shader;
154
+ };
155
+
156
+ const vs = compileShader(gl.VERTEX_SHADER, VERT);
157
+ const fs = compileShader(gl.FRAGMENT_SHADER, FRAG);
158
+ if (!vs || !fs) return;
159
+
160
+ const prog = gl.createProgram();
161
+ if (!prog) return;
162
+ gl.attachShader(prog, vs);
163
+ gl.attachShader(prog, fs);
164
+ gl.linkProgram(prog);
165
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
166
+ console.error(gl.getProgramInfoLog(prog));
167
+ return;
168
+ }
169
+ gl.useProgram(prog);
170
+
171
+ const posLoc = gl.getAttribLocation(prog, "a_position");
172
+ const buf = gl.createBuffer();
173
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
174
+ gl.bufferData(
175
+ gl.ARRAY_BUFFER,
176
+ new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),
177
+ gl.STATIC_DRAW
178
+ );
179
+ gl.enableVertexAttribArray(posLoc);
180
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
181
+
182
+ const uTime = gl.getUniformLocation(prog, "u_time");
183
+ const uResolution = gl.getUniformLocation(prog, "u_resolution");
184
+ const uColors = [0, 1, 2, 3].map((i) =>
185
+ gl.getUniformLocation(prog, `u_colors[${i}]`)
186
+ );
187
+ const uColorsCount = gl.getUniformLocation(prog, "u_colorsCount");
188
+ const uDistortion = gl.getUniformLocation(prog, "u_distortion");
189
+ const uSwirl = gl.getUniformLocation(prog, "u_swirl");
190
+ const uGrainMixer = gl.getUniformLocation(prog, "u_grainMixer");
191
+ const uGrainOverlay = gl.getUniformLocation(prog, "u_grainOverlay");
192
+
193
+ let frameMs = 0;
194
+ let lastNow = performance.now();
195
+ let raf = 0;
196
+
197
+ const resize = () => {
198
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
199
+ canvas.width = Math.floor(wrap.clientWidth * dpr);
200
+ canvas.height = Math.floor(wrap.clientHeight * dpr);
201
+ gl.viewport(0, 0, canvas.width, canvas.height);
202
+ };
203
+
204
+ const draw = (now: number) => {
205
+ if (document.hidden) {
206
+ lastNow = now;
207
+ raf = requestAnimationFrame(draw);
208
+ return;
209
+ }
210
+ const {
211
+ colors: currentColors,
212
+ distortion: currentDistortion,
213
+ swirl: currentSwirl,
214
+ grainMixer: currentGrainMixer,
215
+ grainOverlay: currentGrainOverlay,
216
+ speed: currentSpeed,
217
+ } = propsRef.current;
218
+ const dt = now - lastNow;
219
+ lastNow = now;
220
+ frameMs += dt * currentSpeed;
221
+ const rgbaColors = currentColors.map(hexToRgba);
222
+ gl.uniform1f(uTime, frameMs * 1e-3);
223
+ gl.uniform2f(uResolution, canvas.width, canvas.height);
224
+ for (let i = 0; i < 4; i++) {
225
+ const loc = uColors[i];
226
+ if (loc) gl.uniform4fv(loc, rgbaColors[i] ?? [1, 1, 1, 1]);
227
+ }
228
+ gl.uniform1f(uColorsCount, rgbaColors.length);
229
+ gl.uniform1f(uDistortion, currentDistortion);
230
+ gl.uniform1f(uSwirl, currentSwirl);
231
+ gl.uniform1f(uGrainMixer, currentGrainMixer);
232
+ gl.uniform1f(uGrainOverlay, currentGrainOverlay);
233
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
234
+ raf = requestAnimationFrame(draw);
235
+ };
236
+
237
+ resize();
238
+ const ro = new ResizeObserver(resize);
239
+ ro.observe(wrap);
240
+ raf = requestAnimationFrame(draw);
241
+
242
+ return () => {
243
+ cancelAnimationFrame(raf);
244
+ ro.disconnect();
245
+ };
246
+ }, []);
247
+
248
+ return (
249
+ <div ref={wrapRef} className={className}>
250
+ <canvas ref={canvasRef} className="h-full w-full" aria-hidden />
251
+ </div>
252
+ );
253
+ }
254
+
255
+ function MeshOrbContent() {
256
+ return (
257
+ <>
258
+ <div className="absolute inset-0 bg-[#edf2ff]" />
259
+ <MeshGradientCanvas
260
+ className="absolute inset-0 h-full w-full"
261
+ colors={MESH_COLORS}
262
+ distortion={0.8}
263
+ swirl={HERO_MESH_SWIRL}
264
+ grainMixer={0}
265
+ grainOverlay={0.3}
266
+ speed={HERO_MESH_SPEED}
267
+ />
268
+ </>
269
+ );
270
+ }
271
+
272
+ /** Mesh disc peeking from the bottom — 80% of card width, capped at 800px. */
273
+ export function HeroMeshOrb({ flow = false }: { flow?: boolean }) {
274
+ if (flow) {
275
+ return (
276
+ <div
277
+ className="relative z-0 mt-2 w-full overflow-hidden pb-[22%]"
278
+ aria-hidden
279
+ >
280
+ <div
281
+ className="absolute bottom-0 left-1/2 aspect-square w-full -translate-x-1/2 translate-y-[78%] overflow-hidden rounded-full opacity-90"
282
+ style={{ maxWidth: ORB_MAX_WIDTH_PX }}
283
+ >
284
+ <MeshOrbContent />
285
+ </div>
286
+ </div>
287
+ );
288
+ }
289
+
290
+ return (
291
+ <div
292
+ className="pointer-events-none absolute bottom-0 left-1/2 z-0 aspect-square w-[80%] -translate-x-1/2 translate-y-[78%] overflow-hidden rounded-full opacity-90"
293
+ style={{ maxWidth: ORB_MAX_WIDTH_PX }}
294
+ aria-hidden
295
+ >
296
+ <MeshOrbContent />
297
+ </div>
298
+ );
299
+ }
@@ -0,0 +1,75 @@
1
+ import { useRef, useState, type ReactNode } from "react";
2
+
3
+ /** Hover/focus tooltip clamped to the nearest [data-card-shell] ancestor. */
4
+ export function Tooltip({
5
+ label,
6
+ children,
7
+ multiline = false,
8
+ }: {
9
+ label: string;
10
+ children: ReactNode;
11
+ multiline?: boolean;
12
+ }) {
13
+ const wrapRef = useRef<HTMLSpanElement>(null);
14
+ const tipRef = useRef<HTMLSpanElement>(null);
15
+ const [open, setOpen] = useState(false);
16
+ const [pos, setPos] = useState({ left: 0, maxWidth: 320 });
17
+
18
+ const show = () => {
19
+ const wrap = wrapRef.current;
20
+ const tip = tipRef.current;
21
+ const card = wrap?.closest("[data-card-shell]");
22
+ if (!wrap || !tip || !card) {
23
+ setOpen(true);
24
+ return;
25
+ }
26
+ const pad = 12;
27
+ const cardR = card.getBoundingClientRect();
28
+ const wrapR = wrap.getBoundingClientRect();
29
+ const maxWidth = Math.min(320, Math.max(160, cardR.width - pad * 2));
30
+ Object.assign(tip.style, {
31
+ maxWidth: `${maxWidth}px`,
32
+ visibility: "hidden",
33
+ opacity: "1",
34
+ });
35
+ const w = tip.offsetWidth;
36
+ tip.style.visibility = "";
37
+ tip.style.opacity = "";
38
+ const left =
39
+ Math.max(
40
+ cardR.left + pad,
41
+ Math.min(wrapR.left + wrapR.width / 2 - w / 2, cardR.right - pad - w)
42
+ ) - wrapR.left;
43
+ setPos({ left, maxWidth });
44
+ setOpen(true);
45
+ };
46
+
47
+ return (
48
+ <span
49
+ ref={wrapRef}
50
+ className="relative inline-flex"
51
+ onMouseEnter={show}
52
+ onMouseLeave={() => setOpen(false)}
53
+ onFocus={show}
54
+ onBlur={() => setOpen(false)}
55
+ >
56
+ {children}
57
+ <span
58
+ ref={tipRef}
59
+ role="tooltip"
60
+ style={{
61
+ left: pos.left,
62
+ maxWidth: pos.maxWidth,
63
+ opacity: open ? 1 : 0,
64
+ }}
65
+ className={`pointer-events-none absolute top-full z-30 mt-1.5 w-max rounded-lg bg-zinc-900 px-3 py-2 text-[11px] font-medium text-white transition-opacity duration-200 dark:bg-zinc-800 dark:text-neutral-100 ${
66
+ multiline
67
+ ? "whitespace-normal text-left leading-snug"
68
+ : "whitespace-nowrap"
69
+ }`}
70
+ >
71
+ {label}
72
+ </span>
73
+ </span>
74
+ );
75
+ }
@@ -0,0 +1,142 @@
1
+ export function ExpandIcon() {
2
+ return (
3
+ <svg
4
+ width="14"
5
+ height="14"
6
+ viewBox="0 0 24 24"
7
+ fill="none"
8
+ stroke="currentColor"
9
+ strokeWidth="2"
10
+ aria-hidden
11
+ >
12
+ <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export function PipIcon() {
18
+ return (
19
+ <svg
20
+ width="14"
21
+ height="14"
22
+ viewBox="0 0 24 24"
23
+ fill="none"
24
+ stroke="currentColor"
25
+ strokeWidth="2"
26
+ aria-hidden
27
+ >
28
+ <rect x="2" y="4" width="20" height="14" rx="2" />
29
+ <rect x="13" y="11" width="7" height="5" rx="1" fill="currentColor" />
30
+ </svg>
31
+ );
32
+ }
33
+
34
+ export function CloseIcon() {
35
+ return (
36
+ <svg
37
+ width="14"
38
+ height="14"
39
+ viewBox="0 0 24 24"
40
+ fill="none"
41
+ stroke="currentColor"
42
+ strokeWidth="2"
43
+ aria-hidden
44
+ >
45
+ <line x1="18" y1="6" x2="6" y2="18" />
46
+ <line x1="6" y1="6" x2="18" y2="18" />
47
+ </svg>
48
+ );
49
+ }
50
+
51
+ export function SunIcon() {
52
+ return (
53
+ <svg
54
+ width="14"
55
+ height="14"
56
+ viewBox="0 0 24 24"
57
+ fill="none"
58
+ stroke="currentColor"
59
+ strokeWidth="2"
60
+ strokeLinecap="round"
61
+ strokeLinejoin="round"
62
+ aria-hidden
63
+ >
64
+ <circle cx="12" cy="12" r="4" />
65
+ <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" />
66
+ </svg>
67
+ );
68
+ }
69
+
70
+ export function MoonIcon() {
71
+ return (
72
+ <svg
73
+ width="14"
74
+ height="14"
75
+ viewBox="0 0 24 24"
76
+ fill="none"
77
+ stroke="currentColor"
78
+ strokeWidth="2"
79
+ strokeLinecap="round"
80
+ strokeLinejoin="round"
81
+ aria-hidden
82
+ >
83
+ <path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401" />
84
+ </svg>
85
+ );
86
+ }
87
+
88
+ export function SmartphoneIcon() {
89
+ return (
90
+ <svg
91
+ width="14"
92
+ height="14"
93
+ viewBox="0 0 24 24"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ strokeWidth="2"
97
+ strokeLinecap="round"
98
+ strokeLinejoin="round"
99
+ aria-hidden
100
+ >
101
+ <rect width="14" height="20" x="5" y="2" rx="2" ry="2" />
102
+ <path d="M12 18h.01" />
103
+ </svg>
104
+ );
105
+ }
106
+
107
+ export function MonitorIcon() {
108
+ return (
109
+ <svg
110
+ width="14"
111
+ height="14"
112
+ viewBox="0 0 24 24"
113
+ fill="none"
114
+ stroke="currentColor"
115
+ strokeWidth="2"
116
+ strokeLinecap="round"
117
+ strokeLinejoin="round"
118
+ aria-hidden
119
+ >
120
+ <rect width="20" height="14" x="2" y="3" rx="2" />
121
+ <path d="M8 21h8M12 17v4" />
122
+ </svg>
123
+ );
124
+ }
125
+
126
+ export function ExternalLinkIcon() {
127
+ return (
128
+ <svg
129
+ width="12"
130
+ height="12"
131
+ viewBox="0 0 24 24"
132
+ fill="none"
133
+ stroke="currentColor"
134
+ strokeWidth="2"
135
+ aria-hidden
136
+ >
137
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
138
+ <polyline points="15 3 21 3 21 9" />
139
+ <line x1="10" y1="14" x2="21" y2="3" />
140
+ </svg>
141
+ );
142
+ }
@@ -0,0 +1,5 @@
1
+ @import url("https://fonts.googleapis.com/css2?family=Outfit:wght@500&display=swap");
2
+
3
+ .hero-title {
4
+ font-family: "Outfit", sans-serif;
5
+ }