History

chore(marketing): apply formatting and make beam Row sub optional

Rhys Sullivan committed 6 months ago

Commit 7fe56321 parent 587196f4 files · +300 −185Browse files

Unified · Split

4 files
  1. apps/marketing/src/components/animated-beam-demo.tsx +183 −91
  2. apps/marketing/src/components/ui/animated-beam.tsx +52 −74
  3. apps/marketing/src/lib/utils.ts +3 −3
  4. apps/marketing/src/styles/global.css +62 −17
apps/marketing/src/components/animated-beam-demo.tsx+183−91
@@ -1,26 +1,26 @@
1−"use client"
1+"use client";
22
3−import React, { forwardRef, useRef } from "react"
3+import React, { forwardRef, useRef } from "react";
44
5−import { cn } from "../lib/utils"
6−import { AnimatedBeam } from "./ui/animated-beam"
5+import { cn } from "../lib/utils";
6+import { AnimatedBeam } from "./ui/animated-beam";
77
8−type Variant = "blueprint" | "brutalist" | "pastel" | "cyber" | "editorial" | "stripe"
8+type Variant = "blueprint" | "brutalist" | "pastel" | "cyber" | "editorial" | "stripe";
99
1010 const variantStyles: Record<
1111 Variant,
1212 {
13− nodeBg: string
14− nodeBorder: string
15− nodeRadius: string
16− hubBg: string
17− hubBorder: string
18− label: string
19− sub: string
20− beam: string
21− path: string
22− pathOpacity: number
23− pathWidth: number
13+ nodeBg: string;
14+ nodeBorder: string;
15+ nodeRadius: string;
16+ hubBg: string;
17+ hubBorder: string;
18+ label: string;
19+ sub: string;
20+ beam: string;
21+ path: string;
22+ pathOpacity: number;
23+ pathWidth: number;
2424 }
2525 > = {
2626 blueprint: {
@@ -101,15 +101,15 @@ const variantStyles: Record< · 74 unmodified lines
101101 pathOpacity: 0.4,
102102 pathWidth: 1.25,
103103 },
104−}
104+};
105105
106106 const Node = forwardRef<
107107 HTMLDivElement,
108108 {
109− className?: string
110− children?: React.ReactNode
111− size?: "sm" | "lg"
112− style?: React.CSSProperties
109+ className?: string;
110+ children?: React.ReactNode;
111+ size?: "sm" | "lg";
112+ style?: React.CSSProperties;
113113 }
114114 >(({ className, children, size = "sm", style }, ref) => {
115115 return (
@@ -119,116 +119,190 @@ const Node = forwardRef< · 3 unmodified lines
119119 className={cn(
120120 "z-10 flex items-center justify-center",
121121 size === "sm" ? "size-10 p-2" : "size-16 p-2",
122− className
122+ className,
123123 )}
124124 >
125125 {children}
126126 </div>
127− )
128−})
129−Node.displayName = "Node"
127+ );
128+});
129+Node.displayName = "Node";
130130
131131 export function AnimatedBeamDemo({
132132 className,
133133 variant = "blueprint",
134134 }: {
135− className?: string
136− variant?: Variant
135+ className?: string;
136+ variant?: Variant;
137137 }) {
138− const containerRef = useRef<HTMLDivElement>(null)
139− const agent1 = useRef<HTMLDivElement>(null)
140− const agent2 = useRef<HTMLDivElement>(null)
141− const agent3 = useRef<HTMLDivElement>(null)
142− const hub = useRef<HTMLDivElement>(null)
143− const tool1 = useRef<HTMLDivElement>(null)
144− const tool2 = useRef<HTMLDivElement>(null)
145− const tool3 = useRef<HTMLDivElement>(null)
138+ const containerRef = useRef<HTMLDivElement>(null);
139+ const agent1 = useRef<HTMLDivElement>(null);
140+ const agent2 = useRef<HTMLDivElement>(null);
141+ const agent3 = useRef<HTMLDivElement>(null);
142+ const hub = useRef<HTMLDivElement>(null);
143+ const tool1 = useRef<HTMLDivElement>(null);
144+ const tool2 = useRef<HTMLDivElement>(null);
145+ const tool3 = useRef<HTMLDivElement>(null);
146146
147− const v = variantStyles[variant]
148− const beamColor = v.beam
149− const pathColor = v.path
150− const pathOpacity = v.pathOpacity
151− const pathWidth = v.pathWidth
147+ const v = variantStyles[variant];
148+ const beamColor = v.beam;
149+ const pathColor = v.path;
150+ const pathOpacity = v.pathOpacity;
151+ const pathWidth = v.pathWidth;
152152 const nodeStyle: React.CSSProperties = {
153153 background: v.nodeBg,
154154 border: `1px solid ${v.nodeBorder}`,
155155 borderRadius: v.nodeRadius,
156− }
156+ };
157157 const hubStyle: React.CSSProperties = {
158158 background: v.hubBg,
159159 border: `${variant === "brutalist" ? "2px" : "1px"} solid ${v.hubBorder}`,
160160 borderRadius: v.nodeRadius,
161− }
162− const labelStyle: React.CSSProperties = { color: v.label }
163− const subStyle: React.CSSProperties = { color: v.sub }
161+ };
162+ const labelStyle: React.CSSProperties = { color: v.label };
163+ const subStyle: React.CSSProperties = { color: v.sub };
164164
165165 return (
166166 <div
167167 ref={containerRef}
168− className={cn(
169− "relative flex w-full items-center justify-center py-2",
170− className
171− )}
168+ className={cn("relative flex w-full items-center justify-center py-2", className)}
172169 >
173170 <div className="flex w-full flex-row items-stretch justify-between gap-6 sm:gap-8">
174−
175171 {/* Agents (left) */}
176172 <div className="flex flex-col justify-center gap-7">
177173 <Row label="Claude Code" reverse={false} labelStyle={labelStyle}>
178− <Node ref={agent1} style={nodeStyle}><Icons.claude /></Node>
174+ <Node ref={agent1} style={nodeStyle}>
175+ <Icons.claude />
176+ </Node>
179177 </Row>
180178 <Row label="Cursor" reverse={false} labelStyle={labelStyle}>
181− <Node ref={agent2} style={nodeStyle}><Icons.cursor /></Node>
179+ <Node ref={agent2} style={nodeStyle}>
180+ <Icons.cursor />
181+ </Node>
182182 </Row>
183183 <Row label="Codex" reverse={false} labelStyle={labelStyle}>
184− <Node ref={agent3} style={nodeStyle}><Icons.openai /></Node>
184+ <Node ref={agent3} style={nodeStyle}>
185+ <Icons.openai />
186+ </Node>
185187 </Row>
186188 </div>
187189
188190 {/* Hub (center) */}
189191 <div className="flex flex-col justify-center">
190192 <Node ref={hub} size="lg" style={hubStyle}>
191− <img src="/favicon-192.png" alt="Executor" className="w-full h-full object-contain" style={variant === "cyber" || variant === "brutalist" ? { filter: "invert(1)" } : undefined} />
193+ <img
194+ src="/favicon-192.png"
195+ alt="Executor"
196+ className="w-full h-full object-contain"
197+ style={
198+ variant === "cyber" || variant === "brutalist" ? { filter: "invert(1)" } : undefined
199+ }
200+ />
192201 </Node>
193202 </div>
194203
195204 {/* Tools (right) */}
196205 <div className="flex flex-col justify-center gap-7">
197206 <Row label="Sentry" sub="OpenAPI" reverse labelStyle={labelStyle} subStyle={subStyle}>
198− <Node ref={tool1} style={nodeStyle}><Icons.sentry /></Node>
207+ <Node ref={tool1} style={nodeStyle}>
208+ <Icons.sentry />
209+ </Node>
199210 </Row>
200211 <Row label="GitHub" sub="GraphQL" reverse labelStyle={labelStyle} subStyle={subStyle}>
201− <Node ref={tool2} style={nodeStyle}><Icons.github /></Node>
212+ <Node ref={tool2} style={nodeStyle}>
213+ <Icons.github />
214+ </Node>
202215 </Row>
203216 <Row label="Linear" sub="MCP" reverse labelStyle={labelStyle} subStyle={subStyle}>
204− <Node ref={tool3} style={nodeStyle}><Icons.linear /></Node>
217+ <Node ref={tool3} style={nodeStyle}>
218+ <Icons.linear />
219+ </Node>
205220 </Row>
206221 </div>
207222 </div>
208223
209224 {/* Beams: agents → hub */}
210− <AnimatedBeam containerRef={containerRef} fromRef={agent1} toRef={hub}
211− curvature={-50} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
212− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} />
213− <AnimatedBeam containerRef={containerRef} fromRef={agent2} toRef={hub}
214− curvature={0} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
215− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} delay={0.3} />
216− <AnimatedBeam containerRef={containerRef} fromRef={agent3} toRef={hub}
217− curvature={50} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
218− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} delay={0.6} />
225+ <AnimatedBeam
226+ containerRef={containerRef}
227+ fromRef={agent1}
228+ toRef={hub}
229+ curvature={-50}
230+ pathColor={pathColor}
231+ pathOpacity={pathOpacity}
232+ pathWidth={pathWidth}
233+ gradientStartColor={beamColor}
234+ gradientStopColor={beamColor}
235+ duration={4}
236+ />
237+ <AnimatedBeam
238+ containerRef={containerRef}
239+ fromRef={agent2}
240+ toRef={hub}
241+ curvature={0}
242+ pathColor={pathColor}
243+ pathOpacity={pathOpacity}
244+ pathWidth={pathWidth}
245+ gradientStartColor={beamColor}
246+ gradientStopColor={beamColor}
247+ duration={4}
248+ delay={0.3}
249+ />
250+ <AnimatedBeam
251+ containerRef={containerRef}
252+ fromRef={agent3}
253+ toRef={hub}
254+ curvature={50}
255+ pathColor={pathColor}
256+ pathOpacity={pathOpacity}
257+ pathWidth={pathWidth}
258+ gradientStartColor={beamColor}
259+ gradientStopColor={beamColor}
260+ duration={4}
261+ delay={0.6}
262+ />
219263
220264 {/* Beams: hub → tools */}
221− <AnimatedBeam containerRef={containerRef} fromRef={hub} toRef={tool1}
222− curvature={50} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
223− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} delay={0.15} />
224− <AnimatedBeam containerRef={containerRef} fromRef={hub} toRef={tool2}
225− curvature={0} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
226− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} delay={0.45} />
227− <AnimatedBeam containerRef={containerRef} fromRef={hub} toRef={tool3}
228− curvature={-50} pathColor={pathColor} pathOpacity={pathOpacity} pathWidth={pathWidth}
229− gradientStartColor={beamColor} gradientStopColor={beamColor} duration={4} delay={0.75} />
265+ <AnimatedBeam
266+ containerRef={containerRef}
267+ fromRef={hub}
268+ toRef={tool1}
269+ curvature={50}
270+ pathColor={pathColor}
271+ pathOpacity={pathOpacity}
272+ pathWidth={pathWidth}
273+ gradientStartColor={beamColor}
274+ gradientStopColor={beamColor}
275+ duration={4}
276+ delay={0.15}
277+ />
278+ <AnimatedBeam
279+ containerRef={containerRef}
280+ fromRef={hub}
281+ toRef={tool2}
282+ curvature={0}
283+ pathColor={pathColor}
284+ pathOpacity={pathOpacity}
285+ pathWidth={pathWidth}
286+ gradientStartColor={beamColor}
287+ gradientStopColor={beamColor}
288+ duration={4}
289+ delay={0.45}
290+ />
291+ <AnimatedBeam
292+ containerRef={containerRef}
293+ fromRef={hub}
294+ toRef={tool3}
295+ curvature={-50}
296+ pathColor={pathColor}
297+ pathOpacity={pathOpacity}
298+ pathWidth={pathWidth}
299+ gradientStartColor={beamColor}
300+ gradientStopColor={beamColor}
301+ duration={4}
302+ delay={0.75}
303+ />
230304 </div>
231− )
305+ );
232306 }
233307
234308 function Row({
@@ -239,41 +313,59 @@ function Row({ · 4 unmodified lines
239313 labelStyle,
240314 subStyle,
241315 }: {
242− children: React.ReactNode
243− label: string
244− sub: string
245− reverse: boolean
246− labelStyle?: React.CSSProperties
247− subStyle?: React.CSSProperties
316+ children: React.ReactNode;
317+ label: string;
318+ sub?: string;
319+ reverse: boolean;
320+ labelStyle?: React.CSSProperties;
321+ subStyle?: React.CSSProperties;
248322 }) {
249323 return (
250324 <div className={cn("flex items-center gap-3", reverse && "flex-row-reverse text-right")}>
251325 {children}
252326 <div className="flex flex-col leading-tight">
253− <span className="text-[12px] font-medium" style={labelStyle}>{label}</span>
254− <span className="font-mono text-[10px] tracking-tight" style={subStyle}>{sub}</span>
327+ <span className="text-[12px] font-medium" style={labelStyle}>
328+ {label}
329+ </span>
330+ {sub ? (
331+ <span className="font-mono text-[10px] tracking-tight" style={subStyle}>
332+ {sub}
333+ </span>
334+ ) : null}
255335 </div>
256336 </div>
257− )
337+ );
258338 }
259339
260340 const Icons = {
261341 sentry: () => (
262− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#362D59]"><path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z"/></svg>
342+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#362D59]">
343+ <path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z" />
344+ </svg>
263345 ),
264346 github: () => (
265− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
347+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
348+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
349+ </svg>
266350 ),
267351 linear: () => (
268− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#5E6AD2]"><path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z"/></svg>
352+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#5E6AD2]">
353+ <path d="M2.886 4.18A11.982 11.982 0 0 1 11.99 0C18.624 0 24 5.376 24 12.009c0 3.64-1.62 6.903-4.18 9.105L2.887 4.18ZM1.817 5.626l16.556 16.556c-.524.33-1.075.62-1.65.866L.951 7.277c.247-.575.537-1.126.866-1.65ZM.322 9.163l14.515 14.515c-.71.172-1.443.282-2.195.322L0 11.358a12 12 0 0 1 .322-2.195Zm-.17 4.862 9.823 9.824a12.02 12.02 0 0 1-9.824-9.824Z" />
354+ </svg>
269355 ),
270356 claude: () => (
271− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#D97757]"><path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z"/></svg>
357+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[#D97757]">
358+ <path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z" />
359+ </svg>
272360 ),
273361 cursor: () => (
274− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"><path d="M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23"/></svg>
362+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
363+ <path d="M11.503.131 1.891 5.678a.84.84 0 0 0-.42.726v11.188c0 .3.162.575.42.724l9.609 5.55a1 1 0 0 0 .998 0l9.61-5.55a.84.84 0 0 0 .42-.724V6.404a.84.84 0 0 0-.42-.726L12.497.131a1.01 1.01 0 0 0-.996 0M2.657 6.338h18.55c.263 0 .43.287.297.515L12.23 22.918c-.062.107-.229.064-.229-.06V12.335a.59.59 0 0 0-.295-.51l-9.11-5.257c-.109-.063-.064-.23.061-.23" />
364+ </svg>
275365 ),
276366 openai: () => (
277− <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]"><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/></svg>
367+ <svg viewBox="0 0 24 24" fill="currentColor" className="w-full h-full text-[var(--color-ink)]">
368+ <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
369+ </svg>
278370 ),
279−}
371+};
apps/marketing/src/components/ui/animated-beam.tsx+52−74
@@ -1,30 +1,30 @@
1−"use client"
1+"use client";
22
3−import { useEffect, useId, useState, type RefObject } from "react"
4−import { motion } from "motion/react"
3+import { useEffect, useId, useState, type RefObject } from "react";
4+import { motion } from "motion/react";
55
6−import { cn } from "../../lib/utils"
6+import { cn } from "../../lib/utils";
77
88 export interface AnimatedBeamProps {
9− className?: string
10− containerRef: RefObject<HTMLElement | null> // Container ref
11− fromRef: RefObject<HTMLElement | null>
12− toRef: RefObject<HTMLElement | null>
13− curvature?: number
14− reverse?: boolean
15− pathColor?: string
16− pathWidth?: number
17− pathOpacity?: number
18− gradientStartColor?: string
19− gradientStopColor?: string
20− delay?: number
21− duration?: number
22− repeat?: number
23− repeatDelay?: number
24− startXOffset?: number
25− startYOffset?: number
26− endXOffset?: number
27− endYOffset?: number
9+ className?: string;
10+ containerRef: RefObject<HTMLElement | null>; // Container ref
11+ fromRef: RefObject<HTMLElement | null>;
12+ toRef: RefObject<HTMLElement | null>;
13+ curvature?: number;
14+ reverse?: boolean;
15+ pathColor?: string;
16+ pathWidth?: number;
17+ pathOpacity?: number;
18+ gradientStartColor?: string;
19+ gradientStopColor?: string;
20+ delay?: number;
21+ duration?: number;
22+ repeat?: number;
23+ repeatDelay?: number;
24+ startXOffset?: number;
25+ startYOffset?: number;
26+ endXOffset?: number;
27+ endYOffset?: number;
2828 }
2929
3030 export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({
@@ -48,9 +48,9 @@ export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({ · 17 unmodified lines
4848 endXOffset = 0,
4949 endYOffset = 0,
5050 }) => {
51− const id = useId()
52− const [pathD, setPathD] = useState("")
53− const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 })
51+ const id = useId();
52+ const [pathD, setPathD] = useState("");
53+ const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 });
5454
5555 // Calculate the gradient coordinates based on the reverse prop
5656 const gradientCoordinates = reverse
@@ -65,63 +65,48 @@ export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({ · 8 unmodified lines
6565 x2: ["0%", "100%"],
6666 y1: ["0%", "0%"],
6767 y2: ["0%", "0%"],
68− }
68+ };
6969
7070 useEffect(() => {
7171 const updatePath = () => {
7272 if (containerRef.current && fromRef.current && toRef.current) {
73− const containerRect = containerRef.current.getBoundingClientRect()
74− const rectA = fromRef.current.getBoundingClientRect()
75− const rectB = toRef.current.getBoundingClientRect()
73+ const containerRect = containerRef.current.getBoundingClientRect();
74+ const rectA = fromRef.current.getBoundingClientRect();
75+ const rectB = toRef.current.getBoundingClientRect();
7676
77− const svgWidth = containerRect.width
78− const svgHeight = containerRect.height
79− setSvgDimensions({ width: svgWidth, height: svgHeight })
77+ const svgWidth = containerRect.width;
78+ const svgHeight = containerRect.height;
79+ setSvgDimensions({ width: svgWidth, height: svgHeight });
8080
81− const startX =
82− rectA.left - containerRect.left + rectA.width / 2 + startXOffset
83− const startY =
84− rectA.top - containerRect.top + rectA.height / 2 + startYOffset
85− const endX =
86− rectB.left - containerRect.left + rectB.width / 2 + endXOffset
87− const endY =
88− rectB.top - containerRect.top + rectB.height / 2 + endYOffset
81+ const startX = rectA.left - containerRect.left + rectA.width / 2 + startXOffset;
82+ const startY = rectA.top - containerRect.top + rectA.height / 2 + startYOffset;
83+ const endX = rectB.left - containerRect.left + rectB.width / 2 + endXOffset;
84+ const endY = rectB.top - containerRect.top + rectB.height / 2 + endYOffset;
8985
90− const controlY = startY - curvature
91− const d = `M ${startX},${startY} Q ${
92− (startX + endX) / 2
93− },${controlY} ${endX},${endY}`
94− setPathD(d)
86+ const controlY = startY - curvature;
87+ const d = `M ${startX},${startY} Q ${(startX + endX) / 2},${controlY} ${endX},${endY}`;
88+ setPathD(d);
9589 }
96− }
90+ };
9791
9892 // Initialize ResizeObserver
9993 const resizeObserver = new ResizeObserver(() => {
100− updatePath()
101− })
94+ updatePath();
95+ });
10296
10397 // Observe the container element
10498 if (containerRef.current) {
105− resizeObserver.observe(containerRef.current)
99+ resizeObserver.observe(containerRef.current);
106100 }
107101
108102 // Call the updatePath initially to set the initial path
109− updatePath()
103+ updatePath();
110104
111105 // Clean up the observer on component unmount
112106 return () => {
113− resizeObserver.disconnect()
114− }
115− }, [
116− containerRef,
117− fromRef,
118− toRef,
119− curvature,
120− startXOffset,
121− startYOffset,
122− endXOffset,
123− endYOffset,
124− ])
107+ resizeObserver.disconnect();
108+ };
109+ }, [containerRef, fromRef, toRef, curvature, startXOffset, startYOffset, endXOffset, endYOffset]);
125110
126111 return (
127112 <svg
@@ -129,10 +114,7 @@ export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({ · 1 unmodified line
129114 width={svgDimensions.width}
130115 height={svgDimensions.height}
131116 xmlns="http://www.w3.org/2000/svg"
132− className={cn(
133− "pointer-events-none absolute top-0 left-0 transform-gpu stroke-2",
134− className
135− )}
117+ className={cn("pointer-events-none absolute top-0 left-0 transform-gpu stroke-2", className)}
136118 viewBox={`0 0 ${svgDimensions.width} ${svgDimensions.height}`}
137119 >
138120 <path
@@ -177,13 +159,9 @@ export const AnimatedBeam: React.FC<AnimatedBeamProps> = ({ · 38 unmodified lines
177159 <stop stopColor={gradientStartColor} stopOpacity="0"></stop>
178160 <stop stopColor={gradientStartColor}></stop>
179161 <stop offset="32.5%" stopColor={gradientStopColor}></stop>
180− <stop
181− offset="100%"
182− stopColor={gradientStopColor}
183− stopOpacity="0"
184− ></stop>
162+ <stop offset="100%" stopColor={gradientStopColor} stopOpacity="0"></stop>
185163 </motion.linearGradient>
186164 </defs>
187165 </svg>
188− )
189−}
166+ );
167+};
apps/marketing/src/lib/utils.ts+3−3
@@ -1,6 +1,6 @@
1−import { type ClassValue, clsx } from "clsx"
2−import { twMerge } from "tailwind-merge"
1+import { type ClassValue, clsx } from "clsx";
2+import { twMerge } from "tailwind-merge";
33
44 export function cn(...inputs: ClassValue[]) {
5− return twMerge(clsx(inputs))
5+ return twMerge(clsx(inputs));
66 }
apps/marketing/src/styles/global.css+62−17
@@ -54,7 +54,13 @@ body { · 53 unmodified lines
5454 pointer-events: none;
5555 z-index: 1;
5656 }
57−.pattern-graph { -webkit-mask-image: url("/pattern-graph-paper.svg"); mask-image: url("/pattern-graph-paper.svg"); -webkit-mask-size: 100px 100px; mask-size: 100px 100px; opacity: 0.08; }
57+.pattern-graph {
58+ -webkit-mask-image: url("/pattern-graph-paper.svg");
59+ mask-image: url("/pattern-graph-paper.svg");
60+ -webkit-mask-size: 100px 100px;
61+ mask-size: 100px 100px;
62+ opacity: 0.08;
63+}
5864
5965 /* Soft fade overlay — paint the surface color back over the pattern's bottom */
6066 .pattern-fade-overlay {
@@ -76,14 +82,30 @@ body { · 15 unmodified lines
7682
7783 /* Page-load reveals */
7884 @keyframes rise {
79− from { opacity: 0; transform: translateY(10px); }
80− to { opacity: 1; transform: translateY(0); }
81−}
82−.rise { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; }
83−.rise-1 { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.05s both; }
84−.rise-2 { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.13s both; }
85−.rise-3 { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.21s both; }
86−.rise-4 { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.30s both; }
85+ from {
86+ opacity: 0;
87+ transform: translateY(10px);
88+ }
89+ to {
90+ opacity: 1;
91+ transform: translateY(0);
92+ }
93+}
94+.rise {
95+ animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
96+}
97+.rise-1 {
98+ animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.05s both;
99+}
100+.rise-2 {
101+ animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.13s both;
102+}
103+.rise-3 {
104+ animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.21s both;
105+}
106+.rise-4 {
107+ animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;
108+}
87109
88110 /* Hairline-bordered surface for the beam panel — minimal elevation */
89111 .surface-card {
@@ -106,7 +128,9 @@ body { · 16 unmodified lines
106128 letter-spacing: -0.005em;
107129 transition: background 0.15s ease;
108130 }
109−.btn-primary:hover { background: #1f1f24; }
131+.btn-primary:hover {
132+ background: #1f1f24;
133+}
110134
111135 .btn-link {
112136 color: var(--color-ink-2);
@@ -118,7 +142,9 @@ body { · 5 unmodified lines
118142 gap: 0.4rem;
119143 transition: color 0.15s ease;
120144 }
121−.btn-link:hover { color: var(--color-ink); }
145+.btn-link:hover {
146+ color: var(--color-ink);
147+}
122148
123149 /* Legal pages */
124150 .legal-prose {
@@ -126,7 +152,9 @@ body { · 1 unmodified line
126152 line-height: 1.75;
127153 font-size: 15.5px;
128154 }
129−.legal-prose > :first-child { margin-top: 0; }
155+.legal-prose > :first-child {
156+ margin-top: 0;
157+}
130158 .legal-prose h2 {
131159 margin-top: 3rem;
132160 margin-bottom: 0.85rem;
@@ -136,10 +164,24 @@ body { · 3 unmodified lines
136164 font-weight: 600;
137165 border-top: 1px solid var(--color-rule);
138166 }
139−.legal-prose h3 { margin-top: 1.6rem; margin-bottom: 0.5rem; font-size: 0.95rem; font-weight: 600; }
140−.legal-prose p, .legal-prose ul { margin: 0.85rem 0; }
141−.legal-prose ul { list-style: none; padding-left: 0; }
142−.legal-prose li { position: relative; padding-left: 1.25rem; }
167+.legal-prose h3 {
168+ margin-top: 1.6rem;
169+ margin-bottom: 0.5rem;
170+ font-size: 0.95rem;
171+ font-weight: 600;
172+}
173+.legal-prose p,
174+.legal-prose ul {
175+ margin: 0.85rem 0;
176+}
177+.legal-prose ul {
178+ list-style: none;
179+ padding-left: 0;
180+}
181+.legal-prose li {
182+ position: relative;
183+ padding-left: 1.25rem;
184+}
143185 .legal-prose li::before {
144186 content: "";
145187 position: absolute;
@@ -157,4 +199,7 @@ body { · 11 unmodified lines
157199 text-decoration-color: color-mix(in srgb, var(--color-accent) 35%, transparent);
158200 text-underline-offset: 3px;
159201 }
160−.legal-prose strong { color: var(--color-ink); font-weight: 600; }
202+.legal-prose strong {
203+ color: var(--color-ink);
204+ font-weight: 600;
205+}