fix: emcn component library design engineering polish (#3672)

* chore: fix conflicts

* chore: fix conflicts

* chore: pause marquee

* chore: fix conflicts

* chore: fix conflicts

* chore: address review comments

* chore: fix conflicts

* chore: other bug fixes

* chore: fix conflicts

* chore: fix radius

* chore: fix review changes

* chore: fix conflicts

* chore: revert other

* chore: fix conflicts

* chore: fix review changes

* chore: fix conflicts

* chore: fix review changes

* chore: fix button state

* chore: fix button state

* chore: fix review changes

* chore: fix lint

* chore: fix conflicts

* chore: add metadata

* chore: fix things

* chore: fix overwritten states

* chore: fix warnings

* chore: fix button state

* chore: fix review changes

* chore: fix review changes

* chore: fix hover state

* chore: fix popover

* chore: fix review changes

* chore: fix review changes
This commit is contained in:
Adithya Krishna
2026-03-26 11:40:23 -07:00
committed by GitHub
parent 3774f33d39
commit 5aa0b4d5d4
464 changed files with 5213 additions and 4869 deletions
-2
View File
@@ -8,8 +8,6 @@ export default function RootLayout({ children }: { children: ReactNode }) {
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#ffffff' },
{ media: '(prefers-color-scheme: dark)', color: '#0c0c0c' },
+2 -2
View File
@@ -14,8 +14,8 @@ export default function AuthLayoutClient({ children }: { children: React.ReactNo
return (
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-full flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<main className='relative flex min-h-full flex-col text-[var(--landing-text)]'>
<header className='shrink-0 bg-[var(--landing-bg)]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
@@ -9,7 +9,7 @@ type AuthBackgroundProps = {
export default function AuthBackground({ className, children }: AuthBackgroundProps) {
return (
<div className={cn('fixed inset-0 overflow-hidden', className)}>
<div className='-z-50 pointer-events-none absolute inset-0 bg-[#1C1C1C]' />
<div className='-z-50 pointer-events-none absolute inset-0 bg-[var(--landing-bg)]' />
<AuthBackgroundSVG />
<div className='relative z-20 h-full overflow-auto'>{children}</div>
</div>
@@ -55,9 +55,9 @@ export const BrandedButton = forwardRef<HTMLButtonElement, BrandedButtonProps>(
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={cn(
'group inline-flex h-[32px] items-center justify-center gap-[8px] rounded-[5px] border px-[10px] font-[430] font-season text-[14px] transition-colors disabled:cursor-not-allowed disabled:opacity-50',
'group inline-flex h-[32px] items-center justify-center gap-2 rounded-[5px] border px-2.5 font-[430] font-season text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50',
!hasCustomColor &&
'border-[#FFFFFF] bg-[#FFFFFF] text-black hover:border-[#E0E0E0] hover:bg-[#E0E0E0]',
'border-[var(--white)] bg-[var(--white)] text-black hover:border-[#E0E0E0] hover:bg-[#E0E0E0]',
fullWidth && 'w-full',
className
)}
@@ -35,7 +35,7 @@ export function SSOLoginButton({
const primaryBtnClasses = cn(
primaryClassName || 'branded-button-gradient',
'flex w-full items-center justify-center gap-2 rounded-[10px] border font-medium text-[15px] text-white transition-all duration-200'
'flex w-full items-center justify-center gap-2 rounded-[10px] border font-medium text-base text-white transition-all duration-200'
)
const outlineBtnClasses = cn('w-full rounded-[10px]')
@@ -18,18 +18,18 @@ export function StatusPageLayout({
}: StatusPageLayoutProps) {
return (
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-full flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<main className='relative flex min-h-full flex-col text-[var(--landing-text)]'>
<header className='shrink-0 bg-[var(--landing-bg)]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
<div className='w-full max-w-lg px-4'>
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
{title}
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
{description}
</p>
</div>
@@ -11,12 +11,12 @@ export function SupportFooter({ position = 'fixed' }: SupportFooterProps) {
return (
<div
className={`right-0 bottom-0 left-0 z-50 pb-8 text-center font-[340] text-[#999] text-[13px] leading-relaxed ${position}`}
className={`right-0 bottom-0 left-0 z-50 pb-8 text-center font-[340] text-[var(--landing-text-muted)] text-small leading-relaxed ${position}`}
>
Need help?{' '}
<a
href={`mailto:${brandConfig.supportEmail}`}
className='text-[#999] underline-offset-4 transition hover:text-[#ECECEC] hover:underline'
className='text-[var(--landing-text-muted)] underline-offset-4 transition hover:text-[var(--landing-text)] hover:underline'
>
Contact support
</a>
+6 -6
View File
@@ -4,21 +4,21 @@ export default function LoginLoading() {
return (
<div className='flex flex-col items-center'>
<Skeleton className='h-[38px] w-[80px] rounded-[4px]' />
<div className='mt-[32px] w-full space-y-[8px]'>
<div className='mt-8 w-full space-y-2'>
<Skeleton className='h-[14px] w-[40px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<div className='mt-[16px] w-full space-y-[8px]'>
<div className='mt-4 w-full space-y-2'>
<Skeleton className='h-[14px] w-[64px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-[24px] h-[1px] w-full rounded-[1px]' />
<div className='mt-[24px] flex w-full gap-[12px]'>
<Skeleton className='mt-6 h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-6 h-[1px] w-full rounded-[1px]' />
<div className='mt-6 flex w-full gap-3'>
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[14px] w-[200px] rounded-[4px]' />
<Skeleton className='mt-6 h-[14px] w-[200px] rounded-[4px]' />
</div>
)
}
+13 -11
View File
@@ -174,7 +174,7 @@ export default function LoginPage({
callbackURL: safeCallbackUrl,
},
{
onError: (ctx) => {
onError: (ctx: any) => {
logger.error('Login error:', ctx.error)
if (ctx.error.code?.includes('EMAIL_NOT_VERIFIED')) {
@@ -342,10 +342,10 @@ export default function LoginPage({
return (
<>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Sign in
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
Enter your details
</p>
</div>
@@ -399,7 +399,7 @@ export default function LoginPage({
<button
type='button'
onClick={() => setForgotPasswordOpen(true)}
className='font-medium text-[#999] text-xs transition hover:text-[#ECECEC]'
className='font-medium text-[var(--landing-text-muted)] text-xs transition hover:text-[var(--landing-text)]'
>
Forgot password?
</button>
@@ -426,7 +426,7 @@ export default function LoginPage({
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] transition hover:text-[#ECECEC]'
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--landing-text-muted)] transition hover:text-[var(--landing-text)]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
@@ -469,10 +469,12 @@ export default function LoginPage({
{showDivider && (
<div className='relative my-6 font-light'>
<div className='absolute inset-0 flex items-center'>
<div className='w-full border-[#2A2A2A] border-t' />
<div className='w-full border-[var(--landing-bg-elevated)] border-t' />
</div>
<div className='relative flex justify-center text-sm'>
<span className='bg-[#1C1C1C] px-4 font-[340] text-[#999]'>Or continue with</span>
<span className='bg-[var(--landing-bg)] px-4 font-[340] text-[var(--landing-text-muted)]'>
Or continue with
</span>
</div>
</div>
)}
@@ -502,20 +504,20 @@ export default function LoginPage({
<span className='font-normal'>Don't have an account? </span>
<Link
href={isInviteFlow ? `/signup?invite_flow=true&callbackUrl=${callbackUrl}` : '/signup'}
className='font-medium text-[#ECECEC] underline-offset-4 transition hover:text-white hover:underline'
className='font-medium text-[var(--landing-text)] underline-offset-4 transition hover:text-white hover:underline'
>
Sign up
</Link>
</div>
)}
<div className='absolute right-0 bottom-0 left-0 px-8 pb-8 text-center font-[340] text-[#999] text-[13px] leading-relaxed sm:px-8 md:px-[44px]'>
<div className='absolute right-0 bottom-0 left-0 px-8 pb-8 text-center font-[340] text-[13px] text-[var(--landing-text-muted)] leading-relaxed sm:px-8 md:px-11'>
By signing in, you agree to our{' '}
<Link
href='/terms'
target='_blank'
rel='noopener noreferrer'
className='text-[#999] underline-offset-4 transition hover:text-[#ECECEC] hover:underline'
className='text-[var(--landing-text-muted)] underline-offset-4 transition hover:text-[var(--landing-text)] hover:underline'
>
Terms of Service
</Link>{' '}
@@ -524,7 +526,7 @@ export default function LoginPage({
href='/privacy'
target='_blank'
rel='noopener noreferrer'
className='text-[#999] underline-offset-4 transition hover:text-[#ECECEC] hover:underline'
className='text-[var(--landing-text-muted)] underline-offset-4 transition hover:text-[var(--landing-text)] hover:underline'
>
Privacy Policy
</Link>
@@ -3,16 +3,16 @@ import { Skeleton } from '@/components/emcn'
export default function OAuthConsentLoading() {
return (
<div className='flex flex-col items-center'>
<div className='flex items-center gap-[16px]'>
<div className='flex items-center gap-4'>
<Skeleton className='h-[48px] w-[48px] rounded-[12px]' />
<Skeleton className='h-[20px] w-[20px] rounded-[4px]' />
<Skeleton className='h-[48px] w-[48px] rounded-[12px]' />
</div>
<Skeleton className='mt-[24px] h-[38px] w-[220px] rounded-[4px]' />
<Skeleton className='mt-[8px] h-[14px] w-[280px] rounded-[4px]' />
<Skeleton className='mt-[24px] h-[56px] w-full rounded-[8px]' />
<Skeleton className='mt-[16px] h-[120px] w-full rounded-[8px]' />
<div className='mt-[24px] flex w-full max-w-[410px] gap-[12px]'>
<Skeleton className='mt-6 h-[38px] w-[220px] rounded-[4px]' />
<Skeleton className='mt-2 h-[14px] w-[280px] rounded-[4px]' />
<Skeleton className='mt-6 h-[56px] w-full rounded-[8px]' />
<Skeleton className='mt-4 h-[120px] w-full rounded-[8px]' />
<div className='mt-6 flex w-full max-w-[410px] gap-3'>
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
</div>
+22 -17
View File
@@ -127,10 +127,10 @@ export default function OAuthConsentPage() {
return (
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Authorize Application
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
Loading application details...
</p>
</div>
@@ -142,10 +142,10 @@ export default function OAuthConsentPage() {
return (
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Authorization Error
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
{error}
</p>
</div>
@@ -170,11 +170,11 @@ export default function OAuthConsentPage() {
className='rounded-[10px]'
/>
) : (
<div className='flex h-12 w-12 items-center justify-center rounded-[10px] bg-[#2A2A2A] font-medium text-[#999] text-[18px]'>
<div className='flex h-12 w-12 items-center justify-center rounded-[10px] bg-[var(--landing-bg-elevated)] font-medium text-[var(--landing-text-muted)] text-lg'>
{(clientName ?? '?').charAt(0).toUpperCase()}
</div>
)}
<ArrowLeftRight className='h-5 w-5 text-[#999]' />
<ArrowLeftRight className='h-5 w-5 text-[var(--landing-text-muted)]' />
<Image
src='/new/logo/colorized-bg.svg'
alt='Sim'
@@ -185,17 +185,17 @@ export default function OAuthConsentPage() {
</div>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Authorize Application
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<span className='font-medium text-[#ECECEC]'>{clientName}</span> is requesting access to
your account
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
<span className='font-medium text-[var(--landing-text)]'>{clientName}</span> is requesting
access to your account
</p>
</div>
{session?.user && (
<div className='mt-5 flex items-center gap-3 rounded-lg border border-[#2A2A2A] px-4 py-3'>
<div className='mt-5 flex items-center gap-3 rounded-lg border border-[var(--landing-bg-elevated)] px-4 py-3'>
{session.user.image ? (
<Image
src={session.user.image}
@@ -206,20 +206,22 @@ export default function OAuthConsentPage() {
unoptimized
/>
) : (
<div className='flex h-8 w-8 items-center justify-center rounded-full bg-[#2A2A2A] font-medium text-[#999] text-[13px]'>
<div className='flex h-8 w-8 items-center justify-center rounded-full bg-[var(--landing-bg-elevated)] font-medium text-[var(--landing-text-muted)] text-small'>
{(session.user.name ?? session.user.email ?? '?').charAt(0).toUpperCase()}
</div>
)}
<div className='min-w-0'>
{session.user.name && (
<p className='truncate font-medium text-[14px]'>{session.user.name}</p>
<p className='truncate font-medium text-sm'>{session.user.name}</p>
)}
<p className='truncate text-[#999] text-[13px]'>{session.user.email}</p>
<p className='truncate text-[var(--landing-text-muted)] text-small'>
{session.user.email}
</p>
</div>
<button
type='button'
onClick={handleSwitchAccount}
className='ml-auto text-[#999] text-[13px] underline-offset-2 transition-colors hover:text-[#ECECEC] hover:underline'
className='ml-auto text-[var(--landing-text-muted)] text-small underline-offset-2 transition-colors hover:text-[var(--landing-text)] hover:underline'
>
Switch
</button>
@@ -229,10 +231,13 @@ export default function OAuthConsentPage() {
{scopes.length > 0 && (
<div className='mt-5 w-full max-w-[410px]'>
<div className='rounded-lg border p-4'>
<p className='mb-3 font-medium text-[14px]'>This will allow the application to:</p>
<p className='mb-3 font-medium text-sm'>This will allow the application to:</p>
<ul className='space-y-2'>
{scopes.map((s) => (
<li key={s} className='flex items-start gap-2 font-normal text-[#999] text-[13px]'>
<li
key={s}
className='flex items-start gap-2 font-normal text-[var(--landing-text-muted)] text-small'
>
<span className='mt-0.5 text-green-500'>&#10003;</span>
<span>{SCOPE_DESCRIPTIONS[s] ?? s}</span>
</li>
@@ -4,13 +4,13 @@ export default function ResetPasswordLoading() {
return (
<div className='flex flex-col items-center'>
<Skeleton className='h-[38px] w-[160px] rounded-[4px]' />
<Skeleton className='mt-[12px] h-[14px] w-[280px] rounded-[4px]' />
<div className='mt-[32px] w-full space-y-[8px]'>
<Skeleton className='mt-3 h-[14px] w-[280px] rounded-[4px]' />
<div className='mt-8 w-full space-y-2'>
<Skeleton className='h-[14px] w-[40px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-[24px] h-[14px] w-[120px] rounded-[4px]' />
<Skeleton className='mt-6 h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-6 h-[14px] w-[120px] rounded-[4px]' />
</div>
)
}
@@ -69,10 +69,10 @@ function ResetPasswordContent() {
return (
<>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Reset your password
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
Enter a new password for your account
</p>
</div>
@@ -87,10 +87,10 @@ function ResetPasswordContent() {
/>
</div>
<div className='pt-6 text-center font-light text-[14px]'>
<div className='pt-6 text-center font-light text-sm'>
<Link
href='/login'
className='font-medium text-[#ECECEC] underline-offset-4 transition hover:text-white hover:underline'
className='font-medium text-[var(--landing-text)] underline-offset-4 transition hover:text-white hover:underline'
>
Back to login
</Link>
@@ -46,7 +46,7 @@ export function RequestResetForm({
disabled={isSubmitting}
required
/>
<p className='text-[#999] text-sm'>
<p className='text-[var(--landing-text-muted)] text-sm'>
We'll send a password reset link to this email address.
</p>
</div>
@@ -54,7 +54,10 @@ export function RequestResetForm({
{/* Status message display */}
{statusType && statusMessage && (
<div
className={cn('text-xs', statusType === 'success' ? 'text-[#4CAF50]' : 'text-red-400')}
className={cn(
'text-xs',
statusType === 'success' ? 'text-[var(--success)]' : 'text-red-400'
)}
>
<p>{statusMessage}</p>
</div>
@@ -162,7 +165,7 @@ export function SetNewPasswordForm({
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] transition hover:text-[#ECECEC]'
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--landing-text-muted)] transition hover:text-[var(--landing-text)]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
@@ -190,7 +193,7 @@ export function SetNewPasswordForm({
<button
type='button'
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] transition hover:text-[#ECECEC]'
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--landing-text-muted)] transition hover:text-[var(--landing-text)]'
aria-label={showConfirmPassword ? 'Hide password' : 'Show password'}
>
{showConfirmPassword ? <EyeOff size={18} /> : <Eye size={18} />}
@@ -208,7 +211,7 @@ export function SetNewPasswordForm({
<div
className={cn(
'mt-1 space-y-1 text-xs',
statusType === 'success' ? 'text-[#4CAF50]' : 'text-red-400'
statusType === 'success' ? 'text-[var(--success)]' : 'text-red-400'
)}
>
<p>{statusMessage}</p>
+7 -7
View File
@@ -4,25 +4,25 @@ export default function SignupLoading() {
return (
<div className='flex flex-col items-center'>
<Skeleton className='h-[38px] w-[100px] rounded-[4px]' />
<div className='mt-[32px] w-full space-y-[8px]'>
<div className='mt-8 w-full space-y-2'>
<Skeleton className='h-[14px] w-[40px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<div className='mt-[16px] w-full space-y-[8px]'>
<div className='mt-4 w-full space-y-2'>
<Skeleton className='h-[14px] w-[40px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<div className='mt-[16px] w-full space-y-[8px]'>
<div className='mt-4 w-full space-y-2'>
<Skeleton className='h-[14px] w-[64px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-[24px] h-[1px] w-full rounded-[1px]' />
<div className='mt-[24px] flex w-full gap-[12px]'>
<Skeleton className='mt-6 h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-6 h-[1px] w-full rounded-[1px]' />
<div className='mt-6 flex w-full gap-3'>
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
<Skeleton className='h-[44px] flex-1 rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[14px] w-[220px] rounded-[4px]' />
<Skeleton className='mt-6 h-[14px] w-[220px] rounded-[4px]' />
</div>
)
}
+131 -89
View File
@@ -363,10 +363,10 @@ function SignupFormContent({
return (
<>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
Create an account
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
Create an account or log in
</p>
</div>
@@ -390,105 +390,144 @@ function SignupFormContent({
{/* Email/Password Form - show unless explicitly disabled */}
{!isFalsy(getEnv('NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED')) && (
<form onSubmit={onSubmit} className='mt-8 space-y-8'>
<form onSubmit={onSubmit} className='mt-8 space-y-10'>
<div className='space-y-6'>
<div className='space-y-2'>
<div className='flex items-center justify-between'>
<Label htmlFor='name'>Full name</Label>
</div>
<Input
id='name'
name='name'
placeholder='Enter your name'
type='text'
autoCapitalize='words'
autoComplete='name'
title='Name can only contain letters, spaces, hyphens, and apostrophes'
value={name}
onChange={handleNameChange}
className={cn(
showNameValidationError &&
nameErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
/>
{showNameValidationError && nameErrors.length > 0 && (
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{nameErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
<div className='relative'>
<Input
id='name'
name='name'
placeholder='Enter your name'
type='text'
autoCapitalize='words'
autoComplete='name'
title='Name can only contain letters, spaces, hyphens, and apostrophes'
value={name}
onChange={handleNameChange}
className={cn(
showNameValidationError &&
nameErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
/>
<div
className={cn(
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
showNameValidationError && nameErrors.length > 0
? 'grid-rows-[1fr]'
: 'grid-rows-[0fr]'
)}
aria-live={showNameValidationError && nameErrors.length > 0 ? 'polite' : 'off'}
>
<div className='overflow-hidden'>
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{nameErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
</div>
</div>
</div>
)}
</div>
</div>
<div className='space-y-2'>
<div className='flex items-center justify-between'>
<Label htmlFor='email'>Email</Label>
</div>
<Input
id='email'
name='email'
placeholder='Enter your email'
autoCapitalize='none'
autoComplete='email'
autoCorrect='off'
value={email}
onChange={handleEmailChange}
className={cn(
(emailError || (showEmailValidationError && emailErrors.length > 0)) &&
'border-red-500 focus:border-red-500'
)}
/>
{showEmailValidationError && emailErrors.length > 0 && (
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{emailErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
<div className='relative'>
<Input
id='email'
name='email'
placeholder='Enter your email'
autoCapitalize='none'
autoComplete='email'
autoCorrect='off'
value={email}
onChange={handleEmailChange}
className={cn(
(emailError || (showEmailValidationError && emailErrors.length > 0)) &&
'border-red-500 focus:border-red-500'
)}
/>
<div
className={cn(
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
(showEmailValidationError && emailErrors.length > 0) ||
(emailError && !showEmailValidationError)
? 'grid-rows-[1fr]'
: 'grid-rows-[0fr]'
)}
aria-live={
(showEmailValidationError && emailErrors.length > 0) ||
(emailError && !showEmailValidationError)
? 'polite'
: 'off'
}
>
<div className='overflow-hidden'>
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{showEmailValidationError && emailErrors.length > 0 ? (
emailErrors.map((error, index) => <p key={index}>{error}</p>)
) : emailError && !showEmailValidationError ? (
<p>{emailError}</p>
) : null}
</div>
</div>
</div>
)}
{emailError && !showEmailValidationError && (
<div className='mt-1 text-red-400 text-xs'>
<p>{emailError}</p>
</div>
)}
</div>
</div>
<div className='space-y-2'>
<div className='flex items-center justify-between'>
<Label htmlFor='password'>Password</Label>
</div>
<div className='relative'>
<Input
id='password'
name='password'
type={showPassword ? 'text' : 'password'}
autoCapitalize='none'
autoComplete='new-password'
placeholder='Enter your password'
autoCorrect='off'
value={password}
onChange={handlePasswordChange}
className={cn(
'pr-10',
showValidationError &&
passwordErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
/>
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] transition hover:text-[#ECECEC]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{showValidationError && passwordErrors.length > 0 && (
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{passwordErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
<div className='relative'>
<Input
id='password'
name='password'
type={showPassword ? 'text' : 'password'}
autoCapitalize='none'
autoComplete='new-password'
placeholder='Enter your password'
autoCorrect='off'
value={password}
onChange={handlePasswordChange}
className={cn(
'pr-10',
showValidationError &&
passwordErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
/>
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--landing-text-muted)] transition hover:text-[var(--landing-text)]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
)}
<div
className={cn(
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
showValidationError && passwordErrors.length > 0
? 'grid-rows-[1fr]'
: 'grid-rows-[0fr]'
)}
aria-live={showValidationError && passwordErrors.length > 0 ? 'polite' : 'off'}
>
<div className='overflow-hidden'>
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{passwordErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
</div>
</div>
</div>
</div>
</div>
</div>
@@ -514,6 +553,7 @@ function SignupFormContent({
disabled={isLoading}
loading={isLoading}
loadingText='Creating account'
className='!mt-6'
>
Create account
</BrandedButton>
@@ -532,10 +572,12 @@ function SignupFormContent({
})() && (
<div className='relative my-6 font-light'>
<div className='absolute inset-0 flex items-center'>
<div className='w-full border-[#2A2A2A] border-t' />
<div className='w-full border-[var(--landing-bg-elevated)] border-t' />
</div>
<div className='relative flex justify-center text-sm'>
<span className='bg-[#1C1C1C] px-4 font-[340] text-[#999]'>Or continue with</span>
<span className='bg-[var(--landing-bg)] px-4 font-[340] text-[var(--landing-text-muted)]'>
Or continue with
</span>
</div>
</div>
)}
@@ -570,23 +612,23 @@ function SignupFormContent({
</div>
)}
<div className='pt-6 text-center font-light text-[14px]'>
<div className='pt-6 text-center font-light text-sm'>
<span className='font-normal'>Already have an account? </span>
<Link
href={isInviteFlow ? `/login?invite_flow=true&callbackUrl=${redirectUrl}` : '/login'}
className='font-medium text-[#ECECEC] underline-offset-4 transition hover:text-white hover:underline'
className='font-medium text-[var(--landing-text)] underline-offset-4 transition hover:text-white hover:underline'
>
Sign in
</Link>
</div>
<div className='absolute right-0 bottom-0 left-0 px-8 pb-8 text-center font-[340] text-[#999] text-[13px] leading-relaxed sm:px-8 md:px-[44px]'>
<div className='absolute right-0 bottom-0 left-0 px-8 pb-8 text-center font-[340] text-[var(--landing-text-muted)] text-small leading-relaxed sm:px-8 md:px-11'>
By creating an account, you agree to our{' '}
<Link
href='/terms'
target='_blank'
rel='noopener noreferrer'
className='text-[#999] underline-offset-4 transition hover:text-[#ECECEC] hover:underline'
className='text-[var(--landing-text-muted)] underline-offset-4 transition hover:text-[var(--landing-text)] hover:underline'
>
Terms of Service
</Link>{' '}
@@ -595,7 +637,7 @@ function SignupFormContent({
href='/privacy'
target='_blank'
rel='noopener noreferrer'
className='text-[#999] underline-offset-4 transition hover:text-[#ECECEC] hover:underline'
className='text-[var(--landing-text-muted)] underline-offset-4 transition hover:text-[var(--landing-text)] hover:underline'
>
Privacy Policy
</Link>
+4 -4
View File
@@ -4,13 +4,13 @@ export default function SSOLoading() {
return (
<div className='flex flex-col items-center'>
<Skeleton className='h-[38px] w-[120px] rounded-[4px]' />
<Skeleton className='mt-[12px] h-[14px] w-[260px] rounded-[4px]' />
<div className='mt-[32px] w-full space-y-[8px]'>
<Skeleton className='mt-3 h-[14px] w-[260px] rounded-[4px]' />
<div className='mt-8 w-full space-y-2'>
<Skeleton className='h-[14px] w-[80px] rounded-[4px]' />
<Skeleton className='h-[44px] w-full rounded-[10px]' />
</div>
<Skeleton className='mt-[24px] h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-[24px] h-[14px] w-[120px] rounded-[4px]' />
<Skeleton className='mt-6 h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-6 h-[14px] w-[120px] rounded-[4px]' />
</div>
)
}
+3 -3
View File
@@ -4,9 +4,9 @@ export default function VerifyLoading() {
return (
<div className='flex flex-col items-center'>
<Skeleton className='h-[38px] w-[180px] rounded-[4px]' />
<Skeleton className='mt-[12px] h-[14px] w-[300px] rounded-[4px]' />
<Skeleton className='mt-[4px] h-[14px] w-[240px] rounded-[4px]' />
<Skeleton className='mt-[32px] h-[44px] w-full rounded-[10px]' />
<Skeleton className='mt-3 h-[14px] w-[300px] rounded-[4px]' />
<Skeleton className='mt-1 h-[14px] w-[240px] rounded-[4px]' />
<Skeleton className='mt-8 h-[44px] w-full rounded-[10px]' />
</div>
)
}
+11 -10
View File
@@ -59,10 +59,10 @@ function VerificationForm({
return (
<>
<div className='space-y-1 text-center'>
<h1 className='font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
<h1 className='text-balance font-[430] font-season text-[40px] text-white leading-[110%] tracking-[-0.02em]'>
{isVerified ? 'Email Verified!' : 'Verify Your Email'}
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[18px] leading-[125%] tracking-[0.02em]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-lg leading-[125%] tracking-[0.02em]'>
{isVerified
? 'Your email has been verified. Redirecting to dashboard...'
: !isEmailVerificationEnabled
@@ -78,7 +78,7 @@ function VerificationForm({
{!isVerified && isEmailVerificationEnabled && (
<div className='mt-8 space-y-8'>
<div className='space-y-6'>
<p className='text-center text-[#999] text-sm'>
<p className='text-center text-[var(--landing-text-muted)] text-sm'>
Enter the 6-digit code to verify your account.
{hasEmailService ? " If you don't see it in your inbox, check your spam folder." : ''}
</p>
@@ -122,15 +122,16 @@ function VerificationForm({
{hasEmailService && (
<div className='text-center'>
<p className='text-[#999] text-sm'>
<p className='text-[var(--landing-text-muted)] text-sm'>
Didn't receive a code?{' '}
{countdown > 0 ? (
<span>
Resend in <span className='font-medium text-[#ECECEC]'>{countdown}s</span>
Resend in{' '}
<span className='font-medium text-[var(--landing-text)]'>{countdown}s</span>
</span>
) : (
<button
className='font-medium text-[#ECECEC] underline-offset-4 transition hover:text-white hover:underline'
className='font-medium text-[var(--landing-text)] underline-offset-4 transition hover:text-white hover:underline'
onClick={handleResend}
disabled={isLoading || isResendDisabled}
>
@@ -141,7 +142,7 @@ function VerificationForm({
</div>
)}
<div className='text-center font-light text-[14px]'>
<div className='text-center font-light text-sm'>
<button
onClick={() => {
if (typeof window !== 'undefined') {
@@ -151,7 +152,7 @@ function VerificationForm({
}
router.push('/signup')
}}
className='font-medium text-[#ECECEC] underline-offset-4 transition hover:text-white hover:underline'
className='font-medium text-[var(--landing-text)] underline-offset-4 transition hover:text-white hover:underline'
>
Back to signup
</button>
@@ -166,8 +167,8 @@ function VerificationFormFallback() {
return (
<div className='text-center'>
<div className='animate-pulse'>
<div className='mx-auto mb-4 h-8 w-48 rounded bg-[#2A2A2A]' />
<div className='mx-auto h-4 w-64 rounded bg-[#2A2A2A]' />
<div className='mx-auto mb-4 h-8 w-48 rounded bg-[var(--surface-4)]' />
<div className='mx-auto h-4 w-64 rounded bg-[var(--surface-4)]' />
</div>
</div>
)
@@ -25,7 +25,7 @@ function DotGrid({ className, cols, rows, gap = 0 }: DotGridProps) {
}}
>
{Array.from({ length: cols * rows }, (_, i) => (
<div key={i} className='h-[1.5px] w-[1.5px] rounded-full bg-[#2A2A2A]' />
<div key={i} className='h-[1.5px] w-[1.5px] rounded-full bg-[var(--landing-bg-elevated)]' />
))}
</div>
)
@@ -89,7 +89,7 @@ function VikhyathCursor() {
<div className='absolute top-0 left-[56.02px]'>
<CursorArrow fill='#2ABBF8' />
</div>
<div className='-left-[4px] absolute top-[18px] flex items-center rounded bg-[#2ABBF8] px-[5px] py-[3px] font-[420] font-season text-[#202020] text-[14px] leading-[100%] tracking-[-0.02em]'>
<div className='-left-[4px] absolute top-4.5 flex items-center rounded bg-[#2ABBF8] px-[5px] py-[3px] font-[420] font-season text-[var(--landing-text-dark)] text-sm leading-[100%] tracking-[-0.02em]'>
Vikhyath
</div>
</div>
@@ -113,7 +113,7 @@ function AlexaCursor() {
<div className='absolute top-0 left-0'>
<CursorArrow fill='#FFCC02' />
</div>
<div className='absolute top-[16px] left-[23px] flex items-center rounded bg-[#FFCC02] px-[5px] py-[3px] font-[420] font-season text-[#202020] text-[14px] leading-[100%] tracking-[-0.02em]'>
<div className='absolute top-4 left-[23px] flex items-center rounded bg-[#FFCC02] px-[5px] py-[3px] font-[420] font-season text-[var(--landing-text-dark)] text-sm leading-[100%] tracking-[-0.02em]'>
Alexa
</div>
</div>
@@ -143,7 +143,7 @@ function YouCursor({ x, y, visible }: YouCursorProps) {
<svg width='23.15' height='21.1' viewBox='0 0 17.5 16.4' fill='none'>
<path d={CURSOR_ARROW_MIRRORED_PATH} fill='#33C482' />
</svg>
<div className='absolute top-[16px] left-[23px] flex items-center rounded bg-[#33C482] px-[5px] py-[3px] font-[420] font-season text-[#202020] text-[14px] leading-[100%] tracking-[-0.02em]'>
<div className='absolute top-4 left-[23px] flex items-center rounded bg-[var(--brand-accent)] px-[5px] py-[3px] font-[420] font-season text-[var(--landing-text-dark)] text-sm leading-[100%] tracking-[-0.02em]'>
You
</div>
</div>
@@ -212,7 +212,7 @@ export default function Collaboration() {
ref={sectionRef}
id='collaboration'
aria-labelledby='collaboration-heading'
className='bg-[#1C1C1C]'
className='bg-[var(--landing-bg)]'
style={{ cursor: isHovering ? 'none' : 'auto' }}
onMouseMove={handleMouseMove}
onMouseEnter={handleMouseEnter}
@@ -222,7 +222,7 @@ export default function Collaboration() {
<style dangerouslySetInnerHTML={{ __html: CURSOR_KEYFRAMES }} />
<DotGrid
className='overflow-hidden border-[#2A2A2A] border-y bg-[#1C1C1C] p-[6px]'
className='overflow-hidden border-[var(--landing-bg-elevated)] border-y bg-[var(--landing-bg)] p-1.5'
cols={120}
rows={1}
gap={6}
@@ -230,33 +230,33 @@ export default function Collaboration() {
<div className='relative overflow-hidden'>
<div className='grid grid-cols-1 md:grid-cols-[auto_1fr]'>
<div className='flex flex-col items-start gap-3 px-4 pt-[60px] pb-8 sm:gap-4 sm:px-8 md:gap-[20px] md:px-[80px] md:pt-[100px]'>
<div className='flex flex-col items-start gap-3 px-4 pt-[60px] pb-8 sm:gap-4 sm:px-8 md:gap-5 md:px-20 md:pt-[100px]'>
<Badge
variant='blue'
size='md'
dot
className='bg-[#33C482]/10 font-season text-[#33C482] uppercase tracking-[0.02em]'
className='bg-[color-mix(in_srgb,var(--brand-accent)_10%,transparent)] font-season text-[var(--brand-accent)] uppercase tracking-[0.02em]'
>
Teams
</Badge>
<h2
id='collaboration-heading'
className='font-[430] font-season text-[32px] text-white leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
className='text-balance font-[430] font-season text-[32px] text-white leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
>
Realtime
<br />
collaboration
</h2>
<p className='font-[430] font-season text-[#F6F6F0]/50 text-[15px] leading-[150%] tracking-[0.02em] md:text-[18px]'>
<p className='font-[430] font-season text-[#F6F6F0]/50 text-base leading-[150%] tracking-[0.02em] md:text-lg'>
Grab your team. Build agents together <br className='hidden md:block' />
in real-time inside your workspace.
</p>
<Link
href='/signup'
className='group/cta mt-[12px] inline-flex h-[32px] cursor-none items-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='group/cta mt-3 inline-flex h-[32px] cursor-none items-center gap-1.5 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Build together
<span className='relative h-[10px] w-[10px] shrink-0'>
@@ -306,16 +306,16 @@ export default function Collaboration() {
href='/blog/multiplayer'
target='_blank'
rel='noopener noreferrer'
className='relative mx-4 mb-6 flex cursor-none items-center gap-[14px] rounded-[5px] border border-[#2A2A2A] bg-[#1C1C1C] px-[12px] py-[10px] transition-colors hover:border-[#3d3d3d] hover:bg-[#232323] sm:mx-8 md:absolute md:bottom-10 md:left-[80px] md:z-20 md:mx-0 md:mb-0'
className='relative mx-4 mb-6 flex cursor-none items-center gap-3.5 rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg)] px-3 py-2.5 transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-card)] sm:mx-8 md:absolute md:bottom-10 md:left-20 md:z-20 md:mx-0 md:mb-0'
>
<div className='relative h-7 w-11 shrink-0'>
<Image src='/landing/multiplayer-cursors.svg' alt='' fill className='object-contain' />
</div>
<div className='flex flex-col gap-[2px]'>
<span className='font-[430] font-season text-[#F6F6F0]/50 text-[12px] uppercase leading-[100%] tracking-[0.08em]'>
<div className='flex flex-col gap-0.5'>
<span className='font-[430] font-season text-[#F6F6F0]/50 text-caption uppercase leading-[100%] tracking-[0.08em]'>
Blog
</span>
<span className='font-[430] font-season text-[#F6F6F0] text-[14px] leading-[125%] tracking-[0.02em]'>
<span className='font-[430] font-season text-[#F6F6F0] text-sm leading-[125%] tracking-[0.02em]'>
How we built realtime collaboration
</span>
</div>
@@ -323,7 +323,7 @@ export default function Collaboration() {
</div>
<DotGrid
className='overflow-hidden border-[#2A2A2A] border-y bg-[#1C1C1C] p-[6px]'
className='overflow-hidden border-[var(--landing-bg-elevated)] border-y bg-[var(--landing-bg)] p-1.5'
cols={120}
rows={1}
gap={6}
@@ -100,14 +100,14 @@ export function AccessControlPanel() {
<span className='font-[430] font-season text-[#F6F6F6]/30 text-[10px] uppercase leading-none tracking-[0.08em]'>
{category.label}
</span>
<div className='mt-[8px] grid grid-cols-2 gap-x-4 gap-y-[8px]'>
<div className='mt-2 grid grid-cols-2 gap-x-4 gap-y-2'>
{category.features.map((feature, featIdx) => {
const enabled = accessState[feature.key]
return (
<motion.div
key={feature.key}
className='flex cursor-pointer items-center gap-[8px] rounded-[4px] py-[2px]'
className='flex cursor-pointer items-center gap-2 rounded-[4px] py-0.5'
initial={{ opacity: 0, x: -6 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{
@@ -143,7 +143,7 @@ export function AccessControlPanel() {
<span className='font-[430] font-season text-[#F6F6F6]/30 text-[10px] uppercase leading-none tracking-[0.08em]'>
{category.label}
</span>
<div className='mt-[8px] grid grid-cols-2 gap-x-4 gap-y-[8px]'>
<div className='mt-2 grid grid-cols-2 gap-x-4 gap-y-2'>
{category.features.map((feature, featIdx) => {
const enabled = accessState[feature.key]
const currentIndex =
@@ -155,7 +155,7 @@ export function AccessControlPanel() {
return (
<motion.div
key={feature.key}
className='flex cursor-pointer items-center gap-[8px] rounded-[4px] py-[2px]'
className='flex cursor-pointer items-center gap-2 rounded-[4px] py-0.5'
initial={{ opacity: 0, x: -6 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{
@@ -125,7 +125,7 @@ function AuditRow({ entry, index }: AuditRowProps) {
const timeAgo = formatTimeAgo(entry.insertedAt)
return (
<div className='group relative overflow-hidden border-[#2A2A2A] border-b bg-[#1C1C1C] transition-colors duration-150 last:border-b-0 hover:bg-[#212121]'>
<div className='group relative overflow-hidden border-[var(--landing-border)] border-b bg-[var(--landing-bg)] transition-colors duration-150 last:border-b-0 hover:bg-[#212121]'>
{/* Left accent bar -- brightness encodes recency */}
<div
aria-hidden='true'
@@ -209,8 +209,8 @@ export function AuditLogPreview() {
transition={{
layout: {
type: 'spring',
stiffness: 380,
damping: 38,
stiffness: 350,
damping: 50,
mass: 0.8,
},
y: { duration: 0.32, ease: [0.25, 0.46, 0.45, 0.94] },
@@ -22,13 +22,27 @@ import { DemoRequestModal } from '@/app/(home)/components/demo-request/demo-requ
import { AccessControlPanel } from '@/app/(home)/components/enterprise/components/access-control-panel'
import { AuditLogPreview } from '@/app/(home)/components/enterprise/components/audit-log-preview'
const MARQUEE_KEYFRAMES = `
@keyframes marquee {
const ENTERPRISE_FEATURE_MARQUEE_STYLES = `
@keyframes enterprise-feature-marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-25%); }
}
.enterprise-feature-marquee-track {
animation: enterprise-feature-marquee 30s linear infinite;
}
.enterprise-feature-marquee:hover .enterprise-feature-marquee-track {
animation-play-state: paused;
}
.enterprise-feature-marquee-tag {
transition: background-color 0.3s ease, color 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
@keyframes marquee { 0%, 100% { transform: none; } }
.enterprise-feature-marquee-track {
animation: none;
}
.enterprise-feature-marquee-tag {
transition: none;
}
}
`
@@ -51,13 +65,13 @@ const FEATURE_TAGS = [
function TrustStrip() {
return (
<div className='mx-6 mt-4 grid grid-cols-1 overflow-hidden rounded-[8px] border border-[#2A2A2A] sm:grid-cols-3 md:mx-8'>
<div className='mx-6 mt-4 grid grid-cols-1 overflow-hidden rounded-lg border border-[var(--landing-bg-elevated)] sm:grid-cols-3 md:mx-8'>
{/* SOC 2 + HIPAA combined */}
<Link
href='https://app.vanta.com/sim.ai/trust/v35ia0jil4l7dteqjgaktn'
target='_blank'
rel='noopener noreferrer'
className='group flex items-center gap-3 border-[#2A2A2A] border-b px-4 py-[14px] transition-colors hover:bg-[#212121] sm:border-r sm:border-b-0'
className='group flex items-center gap-3 border-[var(--landing-bg-elevated)] border-b px-4 py-3.5 transition-colors hover:bg-[#212121] sm:border-r sm:border-b-0'
>
<Image
src='/footer/soc2.png'
@@ -68,10 +82,10 @@ function TrustStrip() {
unoptimized
/>
<div className='flex flex-col gap-[3px]'>
<strong className='font-[430] font-season text-[13px] text-white leading-none'>
<strong className='font-[430] font-season text-small text-white leading-none'>
SOC 2 & HIPAA
</strong>
<span className='font-[430] font-season text-[#F6F6F6]/30 text-[11px] leading-none tracking-[0.02em] transition-colors group-hover:text-[#F6F6F6]/55'>
<span className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_30%,transparent)] text-xs leading-none tracking-[0.02em] transition-colors group-hover:text-[color-mix(in_srgb,var(--landing-text-subtle)_55%,transparent)]'>
Type II · PHI protected →
</span>
</div>
@@ -82,31 +96,31 @@ function TrustStrip() {
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='group flex items-center gap-3 border-[#2A2A2A] border-b px-4 py-[14px] transition-colors hover:bg-[#212121] sm:border-r sm:border-b-0'
className='group flex items-center gap-3 border-[var(--landing-bg-elevated)] border-b px-4 py-3.5 transition-colors hover:bg-[#212121] sm:border-r sm:border-b-0'
>
<div className='flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-full bg-[#FFCC02]/10'>
<GithubIcon width={11} height={11} className='text-[#FFCC02]/75' />
</div>
<div className='flex flex-col gap-[3px]'>
<strong className='font-[430] font-season text-[13px] text-white leading-none'>
<strong className='font-[430] font-season text-small text-white leading-none'>
Open Source
</strong>
<span className='font-[430] font-season text-[#F6F6F6]/30 text-[11px] leading-none tracking-[0.02em] transition-colors group-hover:text-[#F6F6F6]/55'>
<span className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_30%,transparent)] text-xs leading-none tracking-[0.02em] transition-colors group-hover:text-[color-mix(in_srgb,var(--landing-text-subtle)_55%,transparent)]'>
View on GitHub →
</span>
</div>
</Link>
{/* SSO */}
<div className='flex items-center gap-3 px-4 py-[14px]'>
<div className='flex items-center gap-3 px-4 py-3.5'>
<div className='flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-full bg-[#2ABBF8]/10'>
<Lock className='h-[14px] w-[14px] text-[#2ABBF8]/75' />
</div>
<div className='flex flex-col gap-[3px]'>
<strong className='font-[430] font-season text-[13px] text-white leading-none'>
<strong className='font-[430] font-season text-small text-white leading-none'>
SSO & SCIM
</strong>
<span className='font-[430] font-season text-[#F6F6F6]/30 text-[11px] leading-none tracking-[0.02em]'>
<span className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_30%,transparent)] text-xs leading-none tracking-[0.02em]'>
Okta, Azure AD, Google
</span>
</div>
@@ -117,9 +131,13 @@ function TrustStrip() {
export default function Enterprise() {
return (
<section id='enterprise' aria-labelledby='enterprise-heading' className='bg-[#F6F6F6]'>
<div className='px-4 pt-[60px] pb-[40px] sm:px-8 sm:pt-[80px] sm:pb-0 md:px-[80px] md:pt-[100px]'>
<div className='flex flex-col items-start gap-3 sm:gap-4 md:gap-[20px]'>
<section
id='enterprise'
aria-labelledby='enterprise-heading'
className='bg-[var(--landing-bg-section)]'
>
<div className='px-4 pt-[60px] pb-10 sm:px-8 sm:pt-20 sm:pb-0 md:px-20 md:pt-[100px]'>
<div className='flex flex-col items-start gap-3 sm:gap-4 md:gap-5'>
<Badge
variant='blue'
size='md'
@@ -131,7 +149,7 @@ export default function Enterprise() {
<h2
id='enterprise-heading'
className='max-w-[600px] font-[430] font-season text-[#1C1C1C] text-[32px] leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
className='max-w-[600px] text-balance font-[430] font-season text-[32px] text-[var(--landing-text-dark)] leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
>
Enterprise features for
<br />
@@ -139,10 +157,10 @@ export default function Enterprise() {
</h2>
</div>
<div className='mt-8 overflow-hidden rounded-[12px] bg-[#1C1C1C] sm:mt-10 md:mt-12'>
<div className='grid grid-cols-1 border-[#2A2A2A] border-b lg:grid-cols-[1fr_420px]'>
<div className='mt-8 overflow-hidden rounded-[12px] bg-[var(--landing-bg)] sm:mt-10 md:mt-12'>
<div className='grid grid-cols-1 border-[var(--landing-border)] border-b lg:grid-cols-[1fr_420px]'>
{/* Audit Trail */}
<div className='border-[#2A2A2A] lg:border-r'>
<div className='border-[var(--landing-border)] lg:border-r'>
<div className='px-6 pt-6 md:px-8 md:pt-8'>
<h3 className='font-[430] font-season text-[16px] text-white leading-[120%] tracking-[-0.01em]'>
Audit Trail
@@ -156,12 +174,12 @@ export default function Enterprise() {
</div>
{/* Access Control */}
<div className='border-[#2A2A2A] border-t lg:border-t-0'>
<div className='border-[var(--landing-border)] border-t lg:border-t-0'>
<div className='px-6 pt-6 md:px-8 md:pt-8'>
<h3 className='font-[430] font-season text-[16px] text-white leading-[120%] tracking-[-0.01em]'>
Access Control
</h3>
<p className='mt-[6px] font-[430] font-season text-[#F6F6F6]/50 text-[14px] leading-[150%] tracking-[0.02em]'>
<p className='mt-1.5 font-[430] font-season text-[#F6F6F6]/50 text-[14px] leading-[150%] tracking-[0.02em]'>
Restrict providers, surfaces, and tools per group.
</p>
</div>
@@ -173,27 +191,27 @@ export default function Enterprise() {
<TrustStrip />
{/* Scrolling feature ticker */}
<div className='relative mt-6 overflow-hidden border-[#2A2A2A] border-t'>
<style dangerouslySetInnerHTML={{ __html: MARQUEE_KEYFRAMES }} />
{/* Scrolling feature ticker — keyframe loop; pause on hover. Tags use transitions for hover. */}
<div className='enterprise-feature-marquee relative mt-6 overflow-hidden border-[var(--landing-bg-elevated)] border-t'>
<style dangerouslySetInnerHTML={{ __html: ENTERPRISE_FEATURE_MARQUEE_STYLES }} />
{/* Fade edges */}
<div
aria-hidden='true'
className='pointer-events-none absolute top-0 bottom-0 left-0 z-10 w-24'
style={{ background: 'linear-gradient(to right, #1C1C1C, transparent)' }}
style={{ background: 'linear-gradient(to right, var(--landing-bg), transparent)' }}
/>
<div
aria-hidden='true'
className='pointer-events-none absolute top-0 right-0 bottom-0 z-10 w-24'
style={{ background: 'linear-gradient(to left, #1C1C1C, transparent)' }}
style={{ background: 'linear-gradient(to left, var(--landing-bg), transparent)' }}
/>
{/* Duplicate tags for seamless loop */}
<div className='flex w-max' style={{ animation: 'marquee 30s linear infinite' }}>
<div className='enterprise-feature-marquee-track flex w-max'>
{[...FEATURE_TAGS, ...FEATURE_TAGS, ...FEATURE_TAGS, ...FEATURE_TAGS].map(
(tag, i) => (
<span
key={i}
className='whitespace-nowrap border-[#2A2A2A] border-r px-5 py-4 font-[430] font-season text-[#F6F6F6]/40 text-[13px] leading-none tracking-[0.02em]'
className='enterprise-feature-marquee-tag whitespace-nowrap border-[var(--landing-bg-elevated)] border-r px-5 py-4 font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_40%,transparent)] text-small leading-none tracking-[0.02em] hover:bg-white/[0.04] hover:text-[color-mix(in_srgb,var(--landing-text-subtle)_55%,transparent)]'
>
{tag}
</span>
@@ -202,14 +220,14 @@ export default function Enterprise() {
</div>
</div>
<div className='flex items-center justify-between border-[#2A2A2A] border-t px-6 py-5 md:px-8 md:py-6'>
<p className='font-[430] font-season text-[#F6F6F6]/40 text-[15px] leading-[150%] tracking-[0.02em]'>
<div className='flex items-center justify-between border-[var(--landing-bg-elevated)] border-t px-6 py-5 md:px-8 md:py-6'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_40%,transparent)] text-base leading-[150%] tracking-[0.02em]'>
Ready for growth?
</p>
<DemoRequestModal>
<button
type='button'
className='group/cta inline-flex h-[32px] cursor-pointer items-center gap-[6px] rounded-[5px] border border-white bg-white px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='group/cta inline-flex h-[32px] cursor-pointer items-center gap-1.5 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Book a demo
<span className='relative h-[10px] w-[10px] shrink-0'>
@@ -304,7 +304,7 @@ function WorkspacePreview({ activeTab, isActive }: { activeTab: number; isActive
function MockUserInput({ text }: { text: string }) {
return (
<div className='flex w-[380px] items-center gap-[6px] rounded-[16px] border border-[#E0E0E0] bg-white px-[10px] py-[8px] shadow-[0_2px_8px_rgba(0,0,0,0.06)]'>
<div className='flex w-[380px] items-center gap-1.5 rounded-[16px] border border-[#E0E0E0] bg-white px-2.5 py-2 shadow-[0_2px_8px_rgba(0,0,0,0.06)]'>
<div className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-full border border-[#E8E8E8]'>
<svg width='12' height='12' viewBox='0 0 12 12' fill='none'>
<path d='M6 2.5v7M2.5 6h7' stroke='#999' strokeWidth='1.5' strokeLinecap='round' />
@@ -364,7 +364,7 @@ function MiniCardHeader({
color?: string
}) {
return (
<div className='flex items-center gap-[4px] border-[#F0F0F0] border-b px-[8px] py-[5px]'>
<div className='flex items-center gap-1 border-[#F0F0F0] border-b px-2 py-1.5'>
<MiniCardIcon variant={variant} color={color} />
<span className='truncate font-medium text-[#888] text-[7px] leading-none'>{label}</span>
</div>
@@ -419,7 +419,7 @@ function MiniCardBody({ variant, color }: { variant: CardVariant; color?: string
function PromptCardBody() {
return (
<div className='px-[8px] py-[6px]'>
<div className='px-2 py-1.5'>
<p className='break-words text-[#AAAAAA] text-[6.5px] leading-[10px]'>{TYPING_PROMPT}</p>
</div>
)
@@ -427,7 +427,7 @@ function PromptCardBody() {
function FileCardBody() {
return (
<div className='flex flex-col gap-[3px] px-[8px] py-[6px]'>
<div className='flex flex-col gap-[3px] px-2 py-1.5'>
<div className='h-[2px] w-[78%] rounded-full bg-[#E8E8E8]' />
<div className='h-[2px] w-[92%] rounded-full bg-[#E8E8E8]' />
<div className='h-[2px] w-[62%] rounded-full bg-[#E8E8E8]' />
@@ -450,7 +450,7 @@ const TABLE_ROW_WIDTHS = [
function TableCardBody() {
return (
<div className='flex flex-col'>
<div className='flex items-center gap-[4px] bg-[#FAFAFA] px-[6px] py-[3px]'>
<div className='flex items-center gap-1 bg-[#FAFAFA] px-1.5 py-[3px]'>
<div className='h-[2px] flex-1 rounded-full bg-[#D4D4D4]' />
<div className='h-[2px] flex-1 rounded-full bg-[#D4D4D4]' />
<div className='h-[2px] flex-1 rounded-full bg-[#D4D4D4]' />
@@ -458,7 +458,7 @@ function TableCardBody() {
{TABLE_ROW_WIDTHS.map((row, i) => (
<div
key={i}
className='flex items-center gap-[4px] border-[#F5F5F5] border-b px-[6px] py-[3.5px]'
className='flex items-center gap-1 border-[#F5F5F5] border-b px-1.5 py-[3.5px]'
>
<div className='h-[1.5px] rounded-full bg-[#EBEBEB]' style={{ width: `${row[0]}%` }} />
<div className='h-[1.5px] rounded-full bg-[#EBEBEB]' style={{ width: `${row[1]}%` }} />
@@ -472,17 +472,17 @@ function TableCardBody() {
function WorkflowCardBody({ color }: { color: string }) {
return (
<div className='relative h-full w-full'>
<div className='absolute top-[10px] left-[10px] h-[14px] w-[14px] rounded-[3px] border border-[#E0E0E0] bg-[#F8F8F8]' />
<div className='absolute top-2.5 left-[10px] h-[14px] w-[14px] rounded-[3px] border border-[#E0E0E0] bg-[#F8F8F8]' />
<div className='absolute top-[16px] left-[24px] h-[1px] w-[16px] bg-[#D8D8D8]' />
<div
className='absolute top-[10px] left-[40px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
className='absolute top-2.5 left-[40px] h-[14px] w-[14px] rounded-[3px] border-[2px]'
style={{
backgroundColor: color,
borderColor: `${color}60`,
backgroundClip: 'padding-box',
}}
/>
<div className='absolute top-[24px] left-[46px] h-[12px] w-[1px] bg-[#D8D8D8]' />
<div className='absolute top-6 left-[46px] h-[12px] w-[1px] bg-[#D8D8D8]' />
<div className='absolute top-[36px] left-[40px] h-[14px] w-[14px] rounded-[3px] border border-[#E0E0E0] bg-[#F8F8F8]' />
<div className='absolute top-[42px] left-[54px] h-[1px] w-[14px] bg-[#D8D8D8]' />
<div
@@ -502,9 +502,9 @@ const KB_WIDTHS = [70, 85, 55, 80, 48] as const
function KnowledgeCardBody() {
return (
<div className='flex flex-col gap-[5px] px-[8px] py-[6px]'>
<div className='flex flex-col gap-[5px] px-2 py-1.5'>
{KB_WIDTHS.map((w, i) => (
<div key={i} className='flex items-center gap-[4px]'>
<div key={i} className='flex items-center gap-1'>
<div className='h-[3px] w-[3px] flex-shrink-0 rounded-full bg-[#D4D4D4]' />
<div className='h-[1.5px] rounded-full bg-[#E8E8E8]' style={{ width: `${w}%` }} />
</div>
@@ -524,9 +524,9 @@ const LOG_ENTRIES = [
function LogsCardBody() {
return (
<div className='flex flex-col gap-[3px] px-[6px] py-[4px]'>
<div className='flex flex-col gap-[3px] px-1.5 py-1'>
{LOG_ENTRIES.map((entry, i) => (
<div key={i} className='flex items-center gap-[4px] py-[1px]'>
<div key={i} className='flex items-center gap-1 py-[1px]'>
<div
className='h-[3px] w-[3px] flex-shrink-0 rounded-full'
style={{ backgroundColor: entry.color }}
@@ -620,8 +620,8 @@ const MD_COMPONENTS: Components = {
{children}
</h2>
),
ul: ({ children }) => <ul className='mb-3 list-disc pl-[24px]'>{children}</ul>,
ol: ({ children }) => <ol className='mb-3 list-decimal pl-[24px]'>{children}</ol>,
ul: ({ children }) => <ul className='mb-3 list-disc pl-6'>{children}</ul>,
ol: ({ children }) => <ol className='mb-3 list-decimal pl-6'>{children}</ol>,
li: ({ children }) => (
<li className='mb-1 text-[#1C1C1C] text-[14px] leading-[1.6]'>{children}</li>
),
@@ -633,8 +633,8 @@ function MockFullFiles() {
return (
<div className='flex h-full flex-col'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<File className='h-[14px] w-[14px] text-[#999]' />
<span className='text-[#999] text-[13px]'>Files</span>
<span className='text-[#D4D4D4] text-[13px]'>/</span>
@@ -654,7 +654,7 @@ function MockFullFiles() {
onChange={(e) => setSource(e.target.value)}
spellCheck={false}
autoCorrect='off'
className='h-full w-full resize-none overflow-auto whitespace-pre-wrap bg-transparent p-[24px] font-[300] font-mono text-[#1C1C1C] text-[12px] leading-[1.7] outline-none'
className='h-full w-full resize-none overflow-auto whitespace-pre-wrap bg-transparent p-6 font-[300] font-mono text-[#1C1C1C] text-[12px] leading-[1.7] outline-none'
/>
</motion.div>
@@ -666,7 +666,7 @@ function MockFullFiles() {
animate={{ opacity: 1 }}
transition={{ duration: 0.4, delay: 0.5 }}
>
<div className='h-full overflow-auto p-[24px]'>
<div className='h-full overflow-auto p-6'>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={MD_COMPONENTS}>
{source}
</ReactMarkdown>
@@ -686,8 +686,8 @@ const KB_STATUS_STYLES: Record<string, { bg: string; text: string; label: string
function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
return (
<div className='flex h-full flex-col'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<Database className='h-[14px] w-[14px] text-[#999]' />
<span className='text-[#999] text-[13px]'>Knowledge Base</span>
<span className='text-[#D4D4D4] text-[13px]'>/</span>
@@ -695,12 +695,12 @@ function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
</div>
</div>
<div className='flex h-[36px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[24px] items-center gap-[4px] rounded-[6px] border border-[#E5E5E5] px-[8px] text-[#999] text-[12px]'>
<div className='flex h-[36px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<div className='flex h-[24px] items-center gap-1 rounded-[6px] border border-[#E5E5E5] px-2 text-[#999] text-[12px]'>
Sort
</div>
<div className='flex h-[24px] items-center gap-[4px] rounded-[6px] border border-[#E5E5E5] px-[8px] text-[#999] text-[12px]'>
<div className='flex h-[24px] items-center gap-1 rounded-[6px] border border-[#E5E5E5] px-2 text-[#999] text-[12px]'>
Filter
</div>
</div>
@@ -716,7 +716,7 @@ function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
</colgroup>
<thead>
<tr>
<th className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-[4px] py-[7px] text-center align-middle'>
<th className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-1 py-[7px] text-center align-middle'>
<div className='flex items-center justify-center'>
<div className='h-[13px] w-[13px] rounded-[2px] border border-[#D4D4D4]' />
</div>
@@ -724,7 +724,7 @@ function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
{MOCK_KB_COLUMNS.map((col) => (
<th
key={col}
className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-[8px] py-[7px] text-left align-middle'
className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-2 py-[7px] text-left align-middle'
>
<span className='font-base text-[#999] text-[13px]'>{col}</span>
</th>
@@ -742,11 +742,11 @@ function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2, ease: 'easeOut' }}
>
<td className='border-[#E5E5E5] border-r border-b px-[4px] py-[7px] text-center align-middle'>
<td className='border-[#E5E5E5] border-r border-b px-1 py-[7px] text-center align-middle'>
<span className='text-[#999] text-[11px] tabular-nums'>{i + 1}</span>
</td>
<td className='border-[#E5E5E5] border-r border-b px-[8px] py-[7px] align-middle'>
<span className='flex items-center gap-[8px] text-[#1C1C1C] text-[13px]'>
<td className='border-[#E5E5E5] border-r border-b px-2 py-[7px] align-middle'>
<span className='flex items-center gap-2 text-[#1C1C1C] text-[13px]'>
<DocIcon className='h-[14px] w-[14px] shrink-0' />
<span className='truncate'>{row[0]}</span>
</span>
@@ -754,14 +754,14 @@ function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
{row.slice(1, 4).map((cell, j) => (
<td
key={j}
className='border-[#E5E5E5] border-r border-b px-[8px] py-[7px] align-middle'
className='border-[#E5E5E5] border-r border-b px-2 py-[7px] align-middle'
>
<span className='text-[#999] text-[13px]'>{cell}</span>
</td>
))}
<td className='border-[#E5E5E5] border-r border-b px-[8px] py-[7px] align-middle'>
<td className='border-[#E5E5E5] border-r border-b px-2 py-[7px] align-middle'>
<span
className='inline-flex items-center rounded-full px-[8px] py-[2px] font-medium text-[11px]'
className='inline-flex items-center rounded-full px-2 py-0.5 font-medium text-[11px]'
style={{ backgroundColor: status.bg, color: status.text }}
>
{status.label}
@@ -885,8 +885,8 @@ function MockFullLogs({ revealedRows }: { revealedRows: number }) {
return (
<div className='relative flex h-full'>
<div className='flex min-w-0 flex-1 flex-col'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<Library className='h-[14px] w-[14px] text-[#999]' />
<span className='font-medium text-[#1C1C1C] text-[13px]'>Logs</span>
</div>
@@ -902,7 +902,7 @@ function MockFullLogs({ revealedRows }: { revealedRows: number }) {
<thead className='shadow-[inset_0_-1px_0_#E5E5E5]'>
<tr>
{['Workflow', 'Date', 'Status', 'Cost', 'Trigger', 'Duration'].map((col) => (
<th key={col} className='h-10 px-[24px] py-[10px] text-left align-middle'>
<th key={col} className='h-10 px-6 py-2.5 text-left align-middle'>
<span className='font-base text-[#999] text-[13px]'>{col}</span>
</th>
))}
@@ -924,8 +924,8 @@ function MockFullLogs({ revealedRows }: { revealedRows: number }) {
)}
onClick={() => setSelectedRow(i)}
>
<td className='px-[24px] py-[10px] align-middle'>
<span className='flex items-center gap-[12px] font-medium text-[#1C1C1C] text-[14px]'>
<td className='px-6 py-2.5 align-middle'>
<span className='flex items-center gap-3 font-medium text-[#1C1C1C] text-[14px]'>
<div
className='h-[10px] w-[10px] shrink-0 rounded-[3px] border-[1.5px]'
style={{
@@ -937,26 +937,26 @@ function MockFullLogs({ revealedRows }: { revealedRows: number }) {
<span className='truncate'>{row[0]}</span>
</span>
</td>
<td className='px-[24px] py-[10px] align-middle'>
<td className='px-6 py-2.5 align-middle'>
<span className='font-medium text-[#999] text-[14px]'>{row[1]}</span>
</td>
<td className='px-[24px] py-[10px] align-middle'>
<td className='px-6 py-2.5 align-middle'>
<span
className='inline-flex items-center rounded-full px-[8px] py-[2px] font-medium text-[11px]'
className='inline-flex items-center rounded-full px-2 py-0.5 font-medium text-[11px]'
style={{ backgroundColor: statusStyle.bg, color: statusStyle.text }}
>
{statusStyle.label}
</span>
</td>
<td className='px-[24px] py-[10px] align-middle'>
<td className='px-6 py-2.5 align-middle'>
<span className='font-medium text-[#999] text-[14px]'>{row[3]}</span>
</td>
<td className='px-[24px] py-[10px] align-middle'>
<span className='rounded-[4px] bg-[#F5F5F5] px-[6px] py-[2px] text-[#666] text-[11px]'>
<td className='px-6 py-2.5 align-middle'>
<span className='rounded-[4px] bg-[#F5F5F5] px-1.5 py-0.5 text-[#666] text-[11px]'>
{row[4]}
</span>
</td>
<td className='px-[24px] py-[10px] align-middle'>
<td className='px-6 py-2.5 align-middle'>
<span className='font-medium text-[#999] text-[14px]'>{row[5]}</span>
</td>
</motion.tr>
@@ -1001,7 +1001,7 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi
const isNextDisabled = selectedRow === MOCK_LOG_DATA.length - 1
return (
<div className='flex h-full flex-col overflow-y-auto px-[14px] pt-[12px]'>
<div className='flex h-full flex-col overflow-y-auto px-3.5 pt-3'>
<div className='flex items-center justify-between'>
<span className='font-medium text-[#1C1C1C] text-[14px]'>Log Details</span>
<div className='flex items-center gap-[1px]'>
@@ -1030,18 +1030,18 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi
</div>
</div>
<div className='mt-[20px] flex flex-col gap-[10px]'>
<div className='flex items-center gap-[16px] px-[1px]'>
<div className='flex w-[120px] shrink-0 flex-col gap-[8px]'>
<div className='mt-5 flex flex-col gap-2.5'>
<div className='flex items-center gap-4 px-[1px]'>
<div className='flex w-[120px] shrink-0 flex-col gap-2'>
<span className='font-medium text-[#999] text-[12px]'>Timestamp</span>
<div className='flex items-center gap-[6px]'>
<div className='flex items-center gap-1.5'>
<span className='font-medium text-[#666] text-[13px]'>{date}</span>
<span className='font-medium text-[#666] text-[13px]'>{time}</span>
</div>
</div>
<div className='flex min-w-0 flex-1 flex-col gap-[8px]'>
<div className='flex min-w-0 flex-1 flex-col gap-2'>
<span className='font-medium text-[#999] text-[12px]'>Workflow</span>
<div className='flex items-center gap-[8px]'>
<div className='flex items-center gap-2'>
<div
className='h-[10px] w-[10px] shrink-0 rounded-[3px] border-[1.5px]'
style={{
@@ -1056,42 +1056,39 @@ function MockLogDetailsSidebar({ selectedRow, onPrev, onNext }: MockLogDetailsSi
</div>
<div className='flex flex-col'>
<div className='flex h-[42px] items-center justify-between border-[#E5E5E5] border-b px-[8px]'>
<div className='flex h-[42px] items-center justify-between border-[#E5E5E5] border-b px-2'>
<span className='font-medium text-[#999] text-[12px]'>Level</span>
<span
className='inline-flex items-center rounded-full px-[8px] py-[2px] font-medium text-[11px]'
className='inline-flex items-center rounded-full px-2 py-0.5 font-medium text-[11px]'
style={{ backgroundColor: statusStyle.bg, color: statusStyle.text }}
>
{statusStyle.label}
</span>
</div>
<div className='flex h-[42px] items-center justify-between border-[#E5E5E5] border-b px-[8px]'>
<div className='flex h-[42px] items-center justify-between border-[#E5E5E5] border-b px-2'>
<span className='font-medium text-[#999] text-[12px]'>Trigger</span>
<span className='rounded-[4px] bg-[#F5F5F5] px-[6px] py-[2px] text-[#666] text-[11px]'>
<span className='rounded-[4px] bg-[#F5F5F5] px-1.5 py-0.5 text-[#666] text-[11px]'>
{row[4]}
</span>
</div>
<div className='flex h-[42px] items-center justify-between px-[8px]'>
<div className='flex h-[42px] items-center justify-between px-2'>
<span className='font-medium text-[#999] text-[12px]'>Duration</span>
<span className='font-medium text-[#666] text-[13px]'>{row[5]}</span>
</div>
</div>
<div className='flex flex-col gap-[6px] rounded-[6px] border border-[#E5E5E5] bg-[#FAFAFA] px-[10px] py-[8px]'>
<div className='flex flex-col gap-1.5 rounded-[6px] border border-[#E5E5E5] bg-[#FAFAFA] px-2.5 py-2'>
<span className='font-medium text-[#999] text-[12px]'>Workflow Output</span>
<div className='rounded-[6px] bg-[#F0F0F0] p-[10px] font-mono text-[#555] text-[11px] leading-[1.5]'>
<div className='rounded-[6px] bg-[#F0F0F0] p-2.5 font-mono text-[#555] text-[11px] leading-[1.5]'>
{detail.output}
</div>
</div>
<div className='flex flex-col gap-[6px] rounded-[6px] border border-[#E5E5E5] bg-[#FAFAFA] px-[10px] py-[8px]'>
<div className='flex flex-col gap-1.5 rounded-[6px] border border-[#E5E5E5] bg-[#FAFAFA] px-2.5 py-2'>
<span className='font-medium text-[#999] text-[12px]'>Trace Spans</span>
<div className='flex flex-col gap-[6px]'>
<div className='flex flex-col gap-1.5'>
{detail.spans.map((span, i) => (
<div
key={i}
className={cn('flex flex-col gap-[3px]', span.depth === 1 && 'ml-[12px]')}
>
<div key={i} className={cn('flex flex-col gap-[3px]', span.depth === 1 && 'ml-3')}>
<div className='flex items-center justify-between'>
<span className='font-mono text-[#555] text-[11px]'>{span.name}</span>
<span className='font-medium text-[#999] text-[11px]'>{span.ms}ms</span>
@@ -1116,8 +1113,8 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
return (
<div className='flex h-full flex-col'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[44px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<Table className='h-[14px] w-[14px] text-[#999]' />
<span className='text-[#999] text-[13px]'>Tables</span>
<span className='text-[#D4D4D4] text-[13px]'>/</span>
@@ -1125,12 +1122,12 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
</div>
</div>
<div className='flex h-[36px] shrink-0 items-center border-[#E5E5E5] border-b px-[24px]'>
<div className='flex items-center gap-[6px]'>
<div className='flex h-[24px] items-center gap-[4px] rounded-[6px] border border-[#E5E5E5] px-[8px] text-[#999] text-[12px]'>
<div className='flex h-[36px] shrink-0 items-center border-[#E5E5E5] border-b px-6'>
<div className='flex items-center gap-1.5'>
<div className='flex h-[24px] items-center gap-1 rounded-[6px] border border-[#E5E5E5] px-2 text-[#999] text-[12px]'>
Sort
</div>
<div className='flex h-[24px] items-center gap-[4px] rounded-[6px] border border-[#E5E5E5] px-[8px] text-[#999] text-[12px]'>
<div className='flex h-[24px] items-center gap-1 rounded-[6px] border border-[#E5E5E5] px-2 text-[#999] text-[12px]'>
Filter
</div>
</div>
@@ -1146,7 +1143,7 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
</colgroup>
<thead>
<tr>
<th className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-[4px] py-[7px] text-center align-middle'>
<th className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-1 py-[7px] text-center align-middle'>
<div className='flex items-center justify-center'>
<div className='h-[13px] w-[13px] rounded-[2px] border border-[#D4D4D4]' />
</div>
@@ -1154,9 +1151,9 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
{MOCK_TABLE_COLUMNS.map((col) => (
<th
key={col}
className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-[8px] py-[7px] text-left align-middle'
className='border-[#E5E5E5] border-r border-b bg-[#FAFAFA] px-2 py-[7px] text-left align-middle'
>
<div className='flex items-center gap-[6px]'>
<div className='flex items-center gap-1.5'>
<ColumnTypeIcon />
<span className='font-medium text-[#1C1C1C] text-[13px]'>{col}</span>
<ChevronDown className='ml-auto h-[7px] w-[9px] shrink-0 text-[#CCC]' />
@@ -1179,7 +1176,7 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
>
<td
className={cn(
'border-[#E5E5E5] border-r border-b px-[4px] py-[7px] text-center align-middle',
'border-[#E5E5E5] border-r border-b px-1 py-[7px] text-center align-middle',
isSelected ? 'bg-[rgba(37,99,235,0.06)]' : 'hover:bg-[#FAFAFA]'
)}
>
@@ -1189,7 +1186,7 @@ function MockFullTable({ revealedRows }: { revealedRows: number }) {
<td
key={j}
className={cn(
'relative border-[#E5E5E5] border-r border-b px-[8px] py-[7px] align-middle',
'relative border-[#E5E5E5] border-r border-b px-2 py-[7px] align-middle',
isSelected ? 'bg-[rgba(37,99,235,0.06)]' : 'hover:bg-[#FAFAFA]'
)}
>
@@ -1311,13 +1308,13 @@ function DefaultPreview() {
return (
<motion.div
key={key}
className='absolute flex items-center justify-center rounded-xl border border-[#E5E5E5] bg-white p-[10px] shadow-[0_2px_4px_0_rgba(0,0,0,0.06)]'
className='absolute flex items-center justify-center rounded-xl border border-[#E5E5E5] bg-white p-2.5 shadow-[0_2px_4px_0_rgba(0,0,0,0.06)]'
initial={{ top: '50%', left: '50%', opacity: 0, scale: 0, x: '-50%', y: '-50%' }}
animate={inView ? { top, left, opacity: 1, scale: 1, x: '-50%', y: '-50%' } : undefined}
transition={{
type: 'spring',
stiffness: 50,
damping: 12,
stiffness: 80,
damping: 20,
delay: explodeDelay,
}}
style={{ color }}
@@ -1334,7 +1331,7 @@ function DefaultPreview() {
animate={inView ? { opacity: 1, x: '-50%', y: '-50%' } : undefined}
transition={{ duration: 0.4, ease: 'easeOut', delay: 0 }}
>
<div className='flex h-[36px] items-center gap-[8px] rounded-[8px] border border-[#E5E5E5] bg-white px-[10px] shadow-[0_2px_6px_0_rgba(0,0,0,0.08)]'>
<div className='flex h-[36px] items-center gap-2 rounded-[8px] border border-[#E5E5E5] bg-white px-2.5 shadow-[0_2px_6px_0_rgba(0,0,0,0.08)]'>
<div className='flex h-[22px] w-[22px] flex-shrink-0 items-center justify-center rounded-[5px] bg-[#1e1e1e]'>
<svg width='11' height='11' viewBox='0 0 10 10' fill='none'>
<path
@@ -152,7 +152,7 @@ function DotGrid({
return (
<div
aria-hidden='true'
className={`h-full shrink-0 bg-[#F6F6F6] p-[6px] ${borderLeft ? 'border-[#E9E9E9] border-l' : ''}`}
className={`h-full shrink-0 bg-[var(--landing-bg-section)] p-1.5 ${borderLeft ? 'border-[var(--divider)] border-l' : ''}`}
style={{
width: width ? `${width}px` : undefined,
display: 'grid',
@@ -181,7 +181,7 @@ export default function Features() {
<section
id='features'
aria-labelledby='features-heading'
className='relative overflow-hidden bg-[#F6F6F6]'
className='relative overflow-hidden bg-[var(--landing-bg-section)]'
>
<div aria-hidden='true' className='absolute top-0 left-0 w-full'>
<Image
@@ -195,10 +195,7 @@ export default function Features() {
</div>
<div className='relative z-10 pt-[60px] lg:pt-[100px]'>
<div
ref={sectionRef}
className='flex flex-col items-start gap-[20px] px-[24px] lg:px-[80px]'
>
<div ref={sectionRef} className='flex flex-col items-start gap-5 px-6 lg:px-20'>
<Badge
variant='blue'
size='md'
@@ -216,31 +213,31 @@ export default function Features() {
</Badge>
<h2
id='features-heading'
className='max-w-[900px] font-[430] font-season text-[#1C1C1C] text-[28px] leading-[110%] tracking-[-0.02em] md:text-[40px]'
className='max-w-[900px] text-balance font-[430] font-season text-[28px] text-[var(--landing-text-dark)] leading-[110%] tracking-[-0.02em] md:text-[40px]'
>
{HEADING_LETTERS.map((char, i) => (
<ScrollLetter key={i} scrollYProgress={scrollYProgress} charIndex={i}>
{char}
</ScrollLetter>
))}
<span className='text-[#1C1C1C]/40'>
<span className='text-[color-mix(in_srgb,var(--landing-text-dark)_40%,transparent)]'>
Design powerful workflows, connect your data, and monitor every run — all in one
platform.
</span>
</h2>
</div>
<div className='relative mt-[40px] pb-[40px] lg:mt-[73px] lg:pb-[80px]'>
<div className='relative mt-10 pb-10 lg:mt-[73px] lg:pb-20'>
<div
aria-hidden='true'
className='absolute top-0 bottom-0 left-[80px] z-20 hidden w-px bg-[#E9E9E9] lg:block'
className='absolute top-0 bottom-0 left-[80px] z-20 hidden w-px bg-[var(--divider)] lg:block'
/>
<div
aria-hidden='true'
className='absolute top-0 right-[80px] bottom-0 z-20 hidden w-px bg-[#E9E9E9] lg:block'
className='absolute top-0 right-[80px] bottom-0 z-20 hidden w-px bg-[var(--divider)] lg:block'
/>
<div className='flex h-[68px] border border-[#E9E9E9] lg:overflow-hidden'>
<div className='flex h-[68px] border border-[var(--divider)] lg:overflow-hidden'>
<div className='h-full shrink-0'>
<div className='h-full lg:hidden'>
<DotGrid cols={3} rows={8} width={24} />
@@ -258,7 +255,7 @@ export default function Features() {
role='tab'
aria-selected={index === activeTab}
onClick={() => setActiveTab(index)}
className={`relative h-full flex-1 items-center justify-center whitespace-nowrap px-[12px] font-medium font-season text-[#212121] text-[12px] uppercase lg:px-0 lg:text-[14px]${tab.hideOnMobile ? ' hidden lg:flex' : ' flex'}${index > 0 ? ' border-[#E9E9E9] border-l' : ''}`}
className={`relative h-full flex-1 items-center justify-center whitespace-nowrap px-3 font-medium font-season text-[var(--landing-text-dark)] text-caption uppercase lg:px-0 lg:text-sm${tab.hideOnMobile ? ' hidden lg:flex' : ' flex'}${index > 0 ? ' border-[var(--divider)] border-l' : ''}`}
style={{ backgroundColor: index === activeTab ? '#FDFDFD' : '#F6F6F6' }}
>
{tab.mobileLabel ? (
@@ -298,19 +295,19 @@ export default function Features() {
</div>
</div>
<div className='mt-[32px] flex flex-col gap-[24px] px-[24px] lg:mt-[60px] lg:grid lg:grid-cols-[1fr_2.8fr] lg:gap-[60px] lg:px-[120px]'>
<div className='flex flex-col items-start justify-between gap-[24px] pt-[20px] lg:h-[560px] lg:gap-0'>
<div className='flex flex-col items-start gap-[16px]'>
<h3 className='font-[430] font-season text-[#1C1C1C] text-[24px] leading-[120%] tracking-[-0.02em] lg:text-[28px]'>
<div className='mt-8 flex flex-col gap-6 px-6 lg:mt-[60px] lg:grid lg:grid-cols-[1fr_2.8fr] lg:gap-[60px] lg:px-[120px]'>
<div className='flex flex-col items-start justify-between gap-6 pt-5 lg:h-[560px] lg:gap-0'>
<div className='flex flex-col items-start gap-4'>
<h3 className='font-[430] font-season text-[24px] text-[var(--landing-text-dark)] leading-[120%] tracking-[-0.02em] lg:text-[28px]'>
{FEATURE_TABS[activeTab].title}
</h3>
<p className='font-[430] font-season text-[#1C1C1C]/50 text-[16px] leading-[150%] tracking-[0.02em] lg:text-[18px]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-dark)_50%,transparent)] text-md leading-[150%] tracking-[0.02em] lg:text-lg'>
{FEATURE_TABS[activeTab].description}
</p>
</div>
<Link
href='/signup'
className='group/cta inline-flex h-[32px] items-center gap-[6px] rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-[10px] font-[430] font-season text-[14px] text-white transition-colors hover:border-[#2A2A2A] hover:bg-[#2A2A2A]'
className='group/cta inline-flex h-[32px] items-center gap-1.5 rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-2.5 font-[430] font-season text-sm text-white transition-colors hover:border-[var(--landing-bg-elevated)] hover:bg-[var(--landing-bg-elevated)]'
>
{FEATURE_TABS[activeTab].cta}
<span className='relative h-[10px] w-[10px] shrink-0'>
@@ -336,7 +333,7 @@ export default function Features() {
<FeaturesPreview activeTab={activeTab} />
</div>
<div aria-hidden='true' className='mt-[60px] hidden h-px bg-[#E9E9E9] lg:block' />
<div aria-hidden='true' className='mt-[60px] hidden h-px bg-[var(--divider)] lg:block' />
</div>
</div>
</section>
@@ -9,7 +9,7 @@ import { useAnimatedPlaceholder } from '@/app/workspace/[workspaceId]/home/hooks
const MAX_HEIGHT = 120
const CTA_BUTTON =
'inline-flex items-center h-[32px] rounded-[5px] border px-[10px] font-[430] font-season text-[14px]'
'inline-flex items-center h-[32px] rounded-[5px] border px-2.5 font-[430] font-season text-sm'
export function FooterCTA() {
const landingSubmit = useLandingSubmit()
@@ -41,14 +41,14 @@ export function FooterCTA() {
}, [])
return (
<div className='flex flex-col items-center px-4 pt-[120px] pb-[100px] sm:px-8 md:px-[80px]'>
<h2 className='text-center font-[430] font-season text-[#1C1C1C] text-[28px] leading-[100%] tracking-[-0.02em] sm:text-[32px] md:text-[36px]'>
<div className='flex flex-col items-center px-4 pt-[120px] pb-[100px] sm:px-8 md:px-20'>
<h2 className='text-balance text-center font-[430] font-season text-[28px] text-[var(--landing-text-dark)] leading-[100%] tracking-[-0.02em] sm:text-[32px] md:text-[36px]'>
What should we get done?
</h2>
<div className='mt-8 w-full max-w-[42rem]'>
<div
className='cursor-text rounded-[20px] border border-[#E5E5E5] bg-white px-[10px] py-[8px] shadow-sm'
className='cursor-text rounded-[20px] border border-[var(--landing-bg-skeleton)] bg-white px-2.5 py-2 shadow-sm'
onClick={() => textareaRef.current?.focus()}
>
<textarea
@@ -59,7 +59,7 @@ export function FooterCTA() {
onInput={handleInput}
placeholder={animatedPlaceholder}
rows={2}
className='m-0 box-border min-h-[48px] w-full resize-none border-0 bg-transparent px-[4px] py-[4px] font-body text-[#1C1C1C] text-[15px] leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[#999] focus-visible:ring-0'
className='m-0 box-border min-h-[48px] w-full resize-none border-0 bg-transparent px-1 py-1 font-body text-[var(--landing-text-dark)] text-base leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[var(--landing-text-muted)] focus-visible:ring-0'
style={{ caretColor: '#1C1C1C', maxHeight: `${MAX_HEIGHT}px` }}
/>
<div className='flex items-center justify-end'>
@@ -79,18 +79,18 @@ export function FooterCTA() {
</div>
</div>
<div className='mt-8 flex gap-[8px]'>
<div className='mt-8 flex gap-2'>
<a
href='https://docs.sim.ai'
target='_blank'
rel='noopener noreferrer'
className={`${CTA_BUTTON} border-[#D4D4D4] text-[#1C1C1C] transition-colors hover:bg-[#E8E8E8]`}
className={`${CTA_BUTTON} border-[var(--landing-border-subtle)] text-[var(--landing-text-dark)] transition-colors hover:bg-[var(--landing-bg-skeleton)]`}
>
Docs
</a>
<Link
href='/signup'
className={`${CTA_BUTTON} gap-[8px] border-[#1C1C1C] bg-[#1C1C1C] text-white transition-colors hover:border-[#333] hover:bg-[#333]`}
className={`${CTA_BUTTON} gap-2 border-[var(--landing-bg)] bg-[var(--landing-bg)] text-white transition-colors hover:border-[var(--landing-bg-elevated)] hover:bg-[var(--landing-bg-elevated)]`}
>
Get started
</Link>
@@ -2,7 +2,8 @@ import Image from 'next/image'
import Link from 'next/link'
import { FooterCTA } from '@/app/(home)/components/footer/footer-cta'
const LINK_CLASS = 'text-[14px] text-[#999] transition-colors hover:text-[#ECECEC]'
const LINK_CLASS =
'text-sm text-[var(--landing-text-muted)] transition-colors hover:text-[var(--landing-text)]'
interface FooterItem {
label: string
@@ -81,8 +82,8 @@ const LEGAL_LINKS: FooterItem[] = [
function FooterColumn({ title, items }: { title: string; items: FooterItem[] }) {
return (
<div>
<h3 className='mb-[16px] font-medium text-[#ECECEC] text-[14px]'>{title}</h3>
<div className='flex flex-col gap-[10px]'>
<h3 className='mb-4 font-medium text-[var(--landing-text)] text-sm'>{title}</h3>
<div className='flex flex-col gap-2.5'>
{items.map(({ label, href, external }) =>
external ? (
<a
@@ -113,16 +114,16 @@ export default function Footer({ hideCTA }: FooterProps) {
return (
<footer
role='contentinfo'
className={`bg-[#F6F6F6] pb-[40px] font-[430] font-season text-[14px]${hideCTA ? ' pt-[40px]' : ''}`}
className={`bg-[var(--landing-bg-section)] pb-10 font-[430] font-season text-sm${hideCTA ? ' pt-10' : ''}`}
>
{!hideCTA && <FooterCTA />}
<div className='px-4 sm:px-8 md:px-[80px]'>
<div className='relative overflow-hidden rounded-lg bg-[#1C1C1C] px-6 pt-[40px] pb-[32px] sm:px-10 sm:pt-[48px] sm:pb-[40px]'>
<div className='px-4 sm:px-8 md:px-20'>
<div className='relative overflow-hidden rounded-lg bg-[var(--landing-bg)] px-6 pt-10 pb-8 sm:px-10 sm:pt-12 sm:pb-10'>
<nav
aria-label='Footer navigation'
className='relative z-[1] grid grid-cols-2 gap-x-8 gap-y-10 sm:grid-cols-3 lg:grid-cols-7'
>
<div className='col-span-2 flex flex-col gap-[24px] sm:col-span-1'>
<div className='col-span-2 flex flex-col gap-6 sm:col-span-1'>
<Link href='/' aria-label='Sim home'>
<Image
src='/logo/sim-landing.svg'
+10 -10
View File
@@ -20,13 +20,13 @@ const LandingPreview = dynamic(
),
{
ssr: false,
loading: () => <div className='aspect-[1116/549] w-full rounded bg-[#1b1b1b]' />,
loading: () => <div className='aspect-[1116/549] w-full rounded bg-[var(--landing-bg)]' />,
}
)
/** Shared base classes for CTA link buttons — matches Deploy/Run button styling in the preview panel. */
const CTA_BASE =
'inline-flex items-center h-[32px] rounded-[5px] border px-[10px] font-[430] font-season text-[14px]'
'inline-flex items-center h-[32px] rounded-[5px] border px-2.5 font-[430] font-season text-sm'
export default function Hero() {
const blockStates = useBlockCycle()
@@ -35,7 +35,7 @@ export default function Hero() {
<section
id='hero'
aria-labelledby='hero-heading'
className='relative flex flex-col items-center overflow-hidden bg-[#1C1C1C] pt-[60px] pb-[12px] lg:pt-[100px]'
className='relative flex flex-col items-center overflow-hidden bg-[var(--landing-bg)] pt-[60px] pb-3 lg:pt-[100px]'
>
<p className='sr-only'>
Sim is the open-source platform to build AI agents and run your agentic workforce. Connect
@@ -59,22 +59,22 @@ export default function Hero() {
<Image src='/landing/card-right.svg' alt='' fill className='object-contain' />
</div>
<div className='relative z-10 flex flex-col items-center gap-[12px]'>
<div className='relative z-10 flex flex-col items-center gap-3'>
<h1
id='hero-heading'
className='font-[430] font-season text-[36px] text-white leading-[100%] tracking-[-0.02em] sm:text-[48px] lg:text-[72px]'
className='text-balance font-[430] font-season text-[36px] text-white leading-[100%] tracking-[-0.02em] sm:text-[48px] lg:text-[72px]'
>
Build AI Agents
</h1>
<p className='font-[430] font-season text-[#F6F6F6]/60 text-[15px] leading-[125%] tracking-[0.02em] lg:text-[18px]'>
<p className='font-[430] font-season text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)] text-base leading-[125%] tracking-[0.02em] lg:text-lg'>
Sim is the AI Workspace for Agent Builders.
</p>
<div className='mt-[12px] flex items-center gap-[8px]'>
<div className='mt-3 flex items-center gap-2'>
<DemoRequestModal>
<button
type='button'
className={`${CTA_BASE} border-[#3d3d3d] bg-transparent text-[#ECECEC] transition-colors hover:bg-[#2A2A2A]`}
className={`${CTA_BASE} border-[var(--landing-border-strong)] bg-transparent text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]`}
aria-label='Get a demo'
>
Get a demo
@@ -82,7 +82,7 @@ export default function Hero() {
</DemoRequestModal>
<Link
href='/signup'
className={`${CTA_BASE} gap-[8px] border-[#FFFFFF] bg-[#FFFFFF] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
className={`${CTA_BASE} gap-2 border-white bg-white text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
aria-label='Get started with Sim'
>
Get started
@@ -119,7 +119,7 @@ export default function Hero() {
<BlocksRightSideAnimated animState={blockStates.rightSide} />
</div>
<div className='relative z-10 overflow-hidden rounded border border-[#2A2A2A]'>
<div className='relative z-10 overflow-hidden rounded border border-[var(--landing-bg-elevated)]'>
<LandingPreview />
</div>
</div>
@@ -48,10 +48,10 @@ export const LandingPreviewHome = memo(function LandingPreviewHome() {
}, [])
return (
<div className='flex min-w-0 flex-1 flex-col items-center justify-center px-[24px] pb-[2vh]'>
<div className='flex min-w-0 flex-1 flex-col items-center justify-center px-6 pb-[2vh]'>
<p
role='presentation'
className='mb-[24px] max-w-[42rem] font-[430] font-season text-[32px] tracking-[-0.02em]'
className='mb-6 max-w-[42rem] font-[430] font-season text-[32px] tracking-[-0.02em]'
style={{ color: C.TEXT_PRIMARY }}
>
What should we get done?
@@ -59,7 +59,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome() {
<div className='w-full max-w-[32rem]'>
<div
className='cursor-text rounded-[20px] border px-[10px] py-[8px]'
className='cursor-text rounded-[20px] border px-2.5 py-2'
style={{ borderColor: C.BORDER, backgroundColor: C.SURFACE }}
onClick={() => textareaRef.current?.focus()}
>
@@ -71,7 +71,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome() {
onInput={handleInput}
placeholder={animatedPlaceholder}
rows={1}
className='m-0 box-border min-h-[24px] w-full resize-none overflow-y-auto border-0 bg-transparent px-[4px] py-[4px] font-body text-[15px] leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[#787878] focus-visible:ring-0'
className='m-0 box-border min-h-[24px] w-full resize-none overflow-y-auto border-0 bg-transparent px-1 py-1 font-body text-[15px] leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[#787878] focus-visible:ring-0'
style={{
color: C.TEXT_PRIMARY,
caretColor: C.TEXT_PRIMARY,
@@ -59,10 +59,10 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
return (
<div className='flex h-full w-[280px] flex-shrink-0 flex-col bg-[#1e1e1e]'>
<div className='flex h-full flex-col border-[#2c2c2c] border-l pt-[14px]'>
<div className='flex h-full flex-col border-[#2c2c2c] border-l pt-3.5'>
{/* Header — More + Chat | Deploy + Run */}
<div className='flex flex-shrink-0 items-center justify-between px-[8px]'>
<div className='pointer-events-none flex gap-[6px]'>
<div className='flex flex-shrink-0 items-center justify-between px-2'>
<div className='pointer-events-none flex gap-1.5'>
<div className='flex h-[30px] w-[30px] items-center justify-center rounded-[5px] border border-[#3d3d3d] bg-[#363636]'>
<MoreHorizontal className='h-[14px] w-[14px] text-[#e6e6e6]' />
</div>
@@ -72,14 +72,14 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
</div>
<Link
href='/signup'
className='flex gap-[6px]'
className='flex gap-1.5'
onMouseMove={(e) => setCursorPos({ x: e.clientX, y: e.clientY })}
onMouseLeave={() => setCursorPos(null)}
>
<div className='flex h-[30px] items-center rounded-[5px] bg-[#33C482] px-[10px] transition-colors hover:bg-[#2DAC72]'>
<div className='flex h-[30px] items-center rounded-[5px] bg-[#33C482] px-2.5 transition-colors hover:bg-[#2DAC72]'>
<span className='font-medium text-[#1b1b1b] text-[12px]'>Deploy</span>
</div>
<div className='flex h-[30px] items-center gap-[8px] rounded-[5px] bg-[#33C482] px-[10px] transition-colors hover:bg-[#2DAC72]'>
<div className='flex h-[30px] items-center gap-2 rounded-[5px] bg-[#33C482] px-2.5 transition-colors hover:bg-[#2DAC72]'>
<Play className='h-[11.5px] w-[11.5px] text-[#1b1b1b]' />
<span className='font-medium text-[#1b1b1b] text-[12px]'>Run</span>
</div>
@@ -101,7 +101,7 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
<div className='h-full w-[8px] bg-[#FA4EDF]' />
<div className='h-full w-[14px] bg-[#FA4EDF] opacity-60' />
</div>
<div className='flex items-center gap-[5px] bg-white px-[6px] py-[4px] font-medium text-[#1C1C1C] text-[11px]'>
<div className='flex items-center gap-[5px] bg-white px-1.5 py-1 font-medium text-[#1C1C1C] text-[11px]'>
Get started
<ChevronDown className='-rotate-90 h-[7px] w-[7px] text-[#1C1C1C]' />
</div>
@@ -111,31 +111,31 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
</div>
{/* Tabs */}
<div className='flex flex-shrink-0 items-center px-[8px] pt-[14px]'>
<div className='pointer-events-none flex gap-[4px]'>
<div className='flex h-[28px] items-center rounded-[6px] border border-[#3d3d3d] bg-[#363636] px-[8px] py-[5px]'>
<div className='flex flex-shrink-0 items-center px-2 pt-3.5'>
<div className='pointer-events-none flex gap-1'>
<div className='flex h-[28px] items-center rounded-[6px] border border-[#3d3d3d] bg-[#363636] px-2 py-[5px]'>
<span className='font-medium text-[#e6e6e6] text-[12.5px]'>Copilot</span>
</div>
<div className='flex h-[28px] items-center rounded-[6px] border border-transparent px-[8px] py-[5px]'>
<div className='flex h-[28px] items-center rounded-[6px] border border-transparent px-2 py-[5px]'>
<span className='font-medium text-[#787878] text-[12.5px]'>Toolbar</span>
</div>
<div className='flex h-[28px] items-center rounded-[6px] border border-transparent px-[8px] py-[5px]'>
<div className='flex h-[28px] items-center rounded-[6px] border border-transparent px-2 py-[5px]'>
<span className='font-medium text-[#787878] text-[12.5px]'>Editor</span>
</div>
</div>
</div>
{/* Tab content — copilot */}
<div className='flex flex-1 flex-col overflow-hidden pt-[12px]'>
<div className='flex flex-1 flex-col overflow-hidden pt-3'>
<div className='flex h-full flex-col'>
{/* Copilot header bar — matches mx-[-1px] in real copilot */}
<div className='pointer-events-none mx-[-1px] flex flex-shrink-0 items-center rounded-[4px] border border-[#2c2c2c] bg-[#292929] px-[12px] py-[6px]'>
<div className='pointer-events-none mx-[-1px] flex flex-shrink-0 items-center rounded-[4px] border border-[#2c2c2c] bg-[#292929] px-3 py-1.5'>
<span className='truncate font-medium text-[#e6e6e6] text-[14px]'>New Chat</span>
</div>
{/* User input — matches real UserInput at p-[8px] inside copilot welcome state */}
<div className='px-[8px] pt-[12px] pb-[8px]'>
<div className='rounded-[4px] border border-[#3d3d3d] bg-[#292929] px-[6px] py-[6px]'>
<div className='px-2 pt-3 pb-2'>
<div className='rounded-[4px] border border-[#3d3d3d] bg-[#292929] px-1.5 py-1.5'>
<textarea
ref={textareaRef}
value={inputValue}
@@ -143,7 +143,7 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
onKeyDown={handleKeyDown}
placeholder='Build an AI agent...'
rows={2}
className='mb-[6px] min-h-[48px] w-full cursor-text resize-none border-0 bg-transparent px-[2px] py-1 font-base text-[#e6e6e6] text-sm leading-[1.25rem] placeholder-[#787878] caret-[#e6e6e6] outline-none'
className='mb-1.5 min-h-[48px] w-full cursor-text resize-none border-0 bg-transparent px-0.5 py-1 font-base text-[#e6e6e6] text-sm leading-[1.25rem] placeholder-[#787878] caret-[#e6e6e6] outline-none'
/>
<div className='flex items-center justify-end'>
<button
@@ -57,7 +57,7 @@ function StaticNavItem({
label: string
}) {
return (
<div className='pointer-events-none mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px]'>
<div className='pointer-events-none mx-0.5 flex h-[28px] items-center gap-2 rounded-[8px] px-2'>
<Icon className='h-[14px] w-[14px] flex-shrink-0' style={{ color: C.TEXT_ICON }} />
<span className='truncate text-[13px]' style={{ color: C.TEXT_BODY, fontWeight: 450 }}>
{label}
@@ -82,13 +82,13 @@ export function LandingPreviewSidebar({
return (
<div
className='flex h-full w-[248px] flex-shrink-0 flex-col pt-[12px]'
className='flex h-full w-[248px] flex-shrink-0 flex-col pt-3'
style={{ backgroundColor: C.SURFACE_1 }}
>
{/* Workspace Header */}
<div className='flex-shrink-0 px-[10px]'>
<div className='flex-shrink-0 px-2.5'>
<div
className='pointer-events-none flex h-[32px] w-full items-center gap-[8px] rounded-[8px] border pr-[8px] pl-[5px]'
className='pointer-events-none flex h-[32px] w-full items-center gap-2 rounded-[8px] border pr-2 pl-[5px]'
style={{ borderColor: C.BORDER, backgroundColor: C.SURFACE_2 }}
>
<div className='flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center rounded-[4px] bg-white'>
@@ -112,11 +112,11 @@ export function LandingPreviewSidebar({
</div>
{/* Top Navigation: Home (interactive), Search (static) */}
<div className='mt-[10px] flex flex-shrink-0 flex-col gap-[2px] px-[8px]'>
<div className='mt-2.5 flex flex-shrink-0 flex-col gap-0.5 px-2'>
<button
type='button'
onClick={onSelectHome}
className='mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px] transition-colors'
className='mx-0.5 flex h-[28px] items-center gap-2 rounded-[8px] px-2 transition-colors'
style={{ backgroundColor: isHomeActive ? C.SURFACE_ACTIVE : 'transparent' }}
onMouseEnter={(e) => {
if (!isHomeActive) e.currentTarget.style.backgroundColor = C.SURFACE_ACTIVE
@@ -134,13 +134,13 @@ export function LandingPreviewSidebar({
</div>
{/* Workspace */}
<div className='mt-[14px] flex flex-shrink-0 flex-col'>
<div className='px-[16px] pb-[6px]'>
<div className='mt-3.5 flex flex-shrink-0 flex-col'>
<div className='px-4 pb-1.5'>
<div className='font-base text-[12px]' style={{ color: C.TEXT_ICON }}>
Workspace
</div>
</div>
<div className='flex flex-col gap-[2px] px-[8px]'>
<div className='flex flex-col gap-0.5 px-2'>
{WORKSPACE_NAV.map((item) => (
<StaticNavItem key={item.id} icon={item.icon} label={item.label} />
))}
@@ -148,15 +148,15 @@ export function LandingPreviewSidebar({
</div>
{/* Scrollable Tasks + Workflows */}
<div className='flex flex-1 flex-col overflow-y-auto overflow-x-hidden pt-[14px]'>
<div className='flex flex-1 flex-col overflow-y-auto overflow-x-hidden pt-3.5'>
{/* Workflows */}
<div className='flex flex-col'>
<div className='px-[16px]'>
<div className='px-4'>
<div className='font-base text-[12px]' style={{ color: C.TEXT_ICON }}>
Workflows
</div>
</div>
<div className='mt-[6px] flex flex-col gap-[2px] px-[8px]'>
<div className='mt-1.5 flex flex-col gap-0.5 px-2'>
{workflows.map((workflow) => {
const isActive = activeView === 'workflow' && workflow.id === activeWorkflowId
return (
@@ -164,7 +164,7 @@ export function LandingPreviewSidebar({
key={workflow.id}
type='button'
onClick={() => onSelectWorkflow(workflow.id)}
className='group mx-[2px] flex h-[28px] w-full items-center gap-[8px] rounded-[8px] px-[8px] transition-colors'
className='group mx-0.5 flex h-[28px] w-full items-center gap-2 rounded-[8px] px-2 transition-colors'
style={{ backgroundColor: isActive ? C.SURFACE_ACTIVE : 'transparent' }}
onMouseEnter={(e) => {
if (!isActive) e.currentTarget.style.backgroundColor = C.SURFACE_ACTIVE
@@ -195,7 +195,7 @@ export function LandingPreviewSidebar({
</div>
{/* Footer */}
<div className='flex flex-shrink-0 flex-col gap-[2px] px-[8px] pt-[9px] pb-[8px]'>
<div className='flex flex-shrink-0 flex-col gap-0.5 px-2 pt-[9px] pb-2'>
{FOOTER_NAV.map((item) => (
<StaticNavItem key={item.id} icon={item.icon} label={item.label} />
))}
@@ -137,7 +137,7 @@ function PreviewFlow({ workflow, animate = false, fitViewOptions }: LandingPrevi
proOptions={PRO_OPTIONS}
fitView
fitViewOptions={resolvedFitViewOptions}
className='h-full w-full bg-[#1b1b1b]'
className='h-full w-full bg-[var(--landing-bg)]'
/>
)
}
@@ -150,10 +150,10 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({
transition={{ duration: 0.45, delay, ease: EASE_OUT }}
>
<div className='w-[280px] select-none rounded-[8px] border border-[#3d3d3d] bg-[#232323]'>
<div className='border-[#3d3d3d] border-b p-[8px]'>
<div className='border-[#3d3d3d] border-b p-2'>
<span className='font-medium text-[#e6e6e6] text-[16px]'>Note</span>
</div>
<div className='p-[10px]'>
<div className='p-2.5'>
<NoteMarkdown content={markdown} />
</div>
</div>
@@ -186,9 +186,9 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({
{/* Header */}
<div
className={`flex items-center justify-between p-[8px] ${hasContent ? 'border-[#3d3d3d] border-b' : ''}`}
className={`flex items-center justify-between p-2 ${hasContent ? 'border-[#3d3d3d] border-b' : ''}`}
>
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-[10px]'>
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-2.5'>
<div
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
style={{ background: bgColor }}
@@ -201,17 +201,17 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({
{/* Sub-block rows + tools */}
{hasContent && (
<div className='flex flex-col gap-[8px] p-[8px]'>
<div className='flex flex-col gap-2 p-2'>
{rows.map((row) => {
const modelEntry = row.title === 'Model' ? getModelIconEntry(row.value) : null
const ModelIcon = modelEntry?.icon
return (
<div key={row.title} className='flex items-center gap-[8px]'>
<div key={row.title} className='flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[#b3b3b3] text-[14px] capitalize'>
{row.title}
</span>
{row.value && (
<span className='flex min-w-0 flex-1 items-center justify-end gap-[5px] font-normal text-[#e6e6e6] text-[14px]'>
<span className='flex min-w-0 flex-1 items-center justify-end gap-2 font-normal text-[#e6e6e6] text-[14px]'>
{ModelIcon && (
<ModelIcon
className={`inline-block flex-shrink-0 text-[#e6e6e6] ${modelEntry.size ?? 'h-[14px] w-[14px]'}`}
@@ -226,15 +226,15 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({
{/* Tool chips — inline with label */}
{tools && tools.length > 0 && (
<div className='flex items-center gap-[8px]'>
<div className='flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[#b3b3b3] text-[14px]'>Tools</span>
<div className='flex flex-1 flex-wrap items-center justify-end gap-[5px]'>
<div className='flex flex-1 flex-wrap items-center justify-end gap-2'>
{tools.map((tool) => {
const ToolIcon = BLOCK_ICONS[tool.type]
return (
<div
key={tool.type}
className='flex items-center gap-[5px] rounded-[5px] border border-[#3d3d3d] bg-[#2a2a2a] px-[6px] py-[3px]'
className='flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] bg-[#2a2a2a] px-2 py-1'
>
<div
className='flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[4px]'
@@ -277,13 +277,13 @@ function NoteMarkdown({ content }: { content: string }) {
const lines = content.split('\n')
return (
<div className='flex flex-col gap-[4px]'>
<div className='flex flex-col gap-1'>
{lines.map((line, i) => {
const trimmed = line.trim()
if (!trimmed) return <div key={i} className='h-[4px]' />
if (trimmed === '---') {
return <hr key={i} className='my-[4px] border-[#3d3d3d] border-t' />
return <hr key={i} className='my-1 border-[#3d3d3d] border-t' />
}
if (trimmed.startsWith('### ')) {
@@ -81,7 +81,7 @@ export function LandingPreview() {
return (
<motion.div
className='dark flex aspect-[1116/549] w-full overflow-hidden rounded bg-[#1e1e1e] antialiased'
className='dark flex aspect-[1116/549] w-full overflow-hidden rounded bg-[var(--landing-bg-surface)] antialiased'
initial='hidden'
animate='visible'
variants={containerVariants}
@@ -95,8 +95,8 @@ export function LandingPreview() {
onSelectHome={handleSelectHome}
/>
</motion.div>
<div className='flex min-w-0 flex-1 flex-col py-[8px] pr-[8px] pl-[8px] lg:pl-0'>
<div className='flex flex-1 overflow-hidden rounded-[8px] border border-[#2c2c2c] bg-[#1b1b1b]'>
<div className='flex min-w-0 flex-1 flex-col py-2 pr-2 pl-2 lg:pl-0'>
<div className='flex flex-1 overflow-hidden rounded-[8px] border border-[#2c2c2c] bg-[var(--landing-bg)]'>
<div
className={
isWorkflowView
@@ -29,7 +29,7 @@ function BlogCard({
<Link
href={`/blog/${slug}`}
className={cn(
'group/card flex flex-col overflow-hidden rounded-[5px] border border-[#2A2A2A] bg-[#1C1C1C] transition-colors hover:border-[#3D3D3D] hover:bg-[#2A2A2A]',
'group/card flex flex-col overflow-hidden rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg)] transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)]',
className
)}
prefetch={false}
@@ -44,9 +44,9 @@ function BlogCard({
unoptimized
/>
</div>
<div className='flex-shrink-0 px-[10px] py-[6px]'>
<div className='flex-shrink-0 px-2.5 py-1.5'>
<span
className='font-[430] font-season text-[#cdcdcd] leading-[140%]'
className='font-[430] font-season text-[var(--landing-text-body)] leading-[140%]'
style={{ fontSize: titleSize }}
>
{title}
@@ -66,8 +66,8 @@ export function BlogDropdown({ posts }: BlogDropdownProps) {
if (!featured) return null
return (
<div className='w-[560px] rounded-[5px] border border-[#2A2A2A] bg-[#1C1C1C] p-[16px] shadow-[0_16px_48px_rgba(0,0,0,0.4)]'>
<div className='grid grid-cols-3 gap-[8px]'>
<div className='w-[560px] rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg)] p-4 shadow-overlay'>
<div className='grid grid-cols-3 gap-2'>
<BlogCard
slug={featured.slug}
image={featured.ogImage}
@@ -37,15 +37,15 @@ const RESOURCE_CARDS = [
export function DocsDropdown() {
return (
<div className='w-[480px] rounded-[5px] border border-[#2A2A2A] bg-[#1C1C1C] p-[16px] shadow-[0_16px_48px_rgba(0,0,0,0.4)]'>
<div className='grid grid-cols-2 gap-[10px]'>
<div className='w-[480px] rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg)] p-4 shadow-overlay'>
<div className='grid grid-cols-2 gap-2.5'>
{PREVIEW_CARDS.map((card) => (
<a
key={card.title}
href={card.href}
target='_blank'
rel='noopener noreferrer'
className='group/card overflow-hidden rounded-[5px] border border-[#2A2A2A] bg-[#1C1C1C] transition-colors hover:border-[#3D3D3D] hover:bg-[#2A2A2A]'
className='group/card overflow-hidden rounded-[5px] border border-[var(--landing-bg-elevated)] bg-[var(--landing-bg)] transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)]'
>
<div className='relative h-[120px] w-full overflow-hidden bg-[#141414]'>
<Image
@@ -57,8 +57,8 @@ export function DocsDropdown() {
unoptimized
/>
</div>
<div className='px-[10px] py-[8px]'>
<span className='font-[430] font-season text-[#cdcdcd] text-[13px]'>
<div className='px-2.5 py-2'>
<span className='font-[430] font-season text-[var(--landing-text-body)] text-small'>
{card.title}
</span>
</div>
@@ -66,7 +66,7 @@ export function DocsDropdown() {
))}
</div>
<div className='mt-[8px] grid grid-cols-3 gap-[8px]'>
<div className='mt-2 grid grid-cols-3 gap-2'>
{RESOURCE_CARDS.map((card) => {
const Icon = card.icon
return (
@@ -75,15 +75,15 @@ export function DocsDropdown() {
href={card.href}
target='_blank'
rel='noopener noreferrer'
className='flex flex-col gap-[4px] rounded-[5px] border border-[#2A2A2A] px-[10px] py-[8px] transition-colors hover:border-[#3D3D3D] hover:bg-[#232323]'
className='flex flex-col gap-1 rounded-[5px] border border-[var(--landing-bg-elevated)] px-2.5 py-2 transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-card)]'
>
<div className='flex items-center gap-[6px]'>
<Icon className='h-[13px] w-[13px] flex-shrink-0 text-[#939393]' />
<span className='font-[430] font-season text-[#cdcdcd] text-[12px]'>
<div className='flex items-center gap-1.5'>
<Icon className='h-[13px] w-[13px] flex-shrink-0 text-[var(--landing-text-icon)]' />
<span className='font-[430] font-season text-[var(--landing-text-body)] text-caption'>
{card.title}
</span>
</div>
<span className='font-season text-[#939393] text-[11px] leading-[130%]'>
<span className='font-season text-[var(--landing-text-icon)] text-xs leading-[130%]'>
{card.description}
</span>
</a>
@@ -31,7 +31,7 @@ export function GitHubStars() {
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='flex items-center gap-[8px] px-[12px]'
className='flex items-center gap-2 px-3'
aria-label={`GitHub repository — ${stars} stars`}
>
<GithubOutlineIcon className='h-[14px] w-[14px]' />
@@ -32,8 +32,8 @@ const NAV_LINKS: NavLink[] = [
{ label: 'Enterprise', href: 'https://form.typeform.com/to/jqCO12pF', external: true },
]
const LOGO_CELL = 'flex items-center pl-[20px] lg:pl-[80px] pr-[20px]'
const LINK_CELL = 'flex items-center px-[14px]'
const LOGO_CELL = 'flex items-center pl-5 lg:pl-20 pr-5'
const LINK_CELL = 'flex items-center px-3.5'
interface NavbarProps {
logoOnly?: boolean
@@ -96,7 +96,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
return (
<nav
aria-label='Primary navigation'
className='relative flex h-[52px] border-[#2A2A2A] border-b-[1px] bg-[#1C1C1C] font-[430] font-season text-[#ECECEC] text-[14px]'
className='relative flex h-[52px] border-[var(--landing-bg-elevated)] border-b-[1px] bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)] text-sm'
itemScope
itemType='https://schema.org/SiteNavigationElement'
>
@@ -138,9 +138,9 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
const isHighlighted = isActive || isThisHovered
const isDimmed = anyHighlighted && !isHighlighted
const linkClass = cn(
icon ? `${LINK_CELL} gap-[8px]` : LINK_CELL,
icon ? `${LINK_CELL} gap-2` : LINK_CELL,
'transition-colors duration-200',
isDimmed && 'text-[#F6F6F6]/60'
isDimmed && 'text-[color-mix(in_srgb,var(--landing-text-subtle)_60%,transparent)]'
)
const chevron = icon === 'chevron' && <NavChevron open={isActive} />
@@ -171,7 +171,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
<div
className={cn(
'-mt-[2px] absolute top-full left-0 z-50',
'-mt-0.5 absolute top-full left-0 z-50',
isActive
? 'pointer-events-auto opacity-100'
: 'pointer-events-none opacity-0'
@@ -225,14 +225,14 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
<div
className={cn(
'hidden items-center gap-[8px] pr-[80px] pl-[20px] lg:flex',
'hidden items-center gap-2 pr-20 pl-5 lg:flex',
isSessionPending && 'invisible'
)}
>
{isAuthenticated ? (
<Link
href='/workspace'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
aria-label='Go to app'
>
Go to App
@@ -241,14 +241,14 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
<>
<Link
href='/login'
className='inline-flex h-[30px] items-center rounded-[5px] border border-[#3d3d3d] px-[9px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
className='inline-flex h-[30px] items-center rounded-[5px] border border-[var(--landing-border-strong)] px-[9px] text-[13.5px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
aria-label='Log in'
>
Log in
</Link>
<Link
href='/signup'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-2.5 text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
aria-label='Get started with Sim'
>
Get started
@@ -257,10 +257,10 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
)}
</div>
<div className='flex flex-1 items-center justify-end pr-[20px] lg:hidden'>
<div className='flex flex-1 items-center justify-end pr-5 lg:hidden'>
<button
type='button'
className='flex h-[32px] w-[32px] items-center justify-center rounded-[5px] transition-colors hover:bg-[#2A2A2A]'
className='flex h-[32px] w-[32px] items-center justify-center rounded-[5px] transition-colors hover:bg-[var(--landing-bg-elevated)]'
onClick={() => setMobileMenuOpen((prev) => !prev)}
aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
aria-expanded={mobileMenuOpen}
@@ -271,7 +271,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
<div
className={cn(
'fixed inset-x-0 top-[52px] bottom-0 z-50 flex flex-col overflow-y-auto bg-[#1C1C1C] font-[430] font-season text-[14px] transition-all duration-200 lg:hidden',
'fixed inset-x-0 top-[52px] bottom-0 z-50 flex flex-col overflow-y-auto bg-[var(--landing-bg)] font-[430] font-season text-sm transition-all duration-200 lg:hidden',
mobileMenuOpen ? 'visible opacity-100' : 'invisible opacity-0'
)}
>
@@ -280,13 +280,13 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
const href =
useHomeLinks && rawHref.startsWith('/#') ? `/?home${rawHref.slice(1)}` : rawHref
return (
<li key={label} className='border-[#2A2A2A] border-b'>
<li key={label} className='border-[var(--landing-border)] border-b'>
{external ? (
<a
href={href}
target='_blank'
rel='noopener noreferrer'
className='flex items-center justify-between px-[20px] py-[14px] text-[#ECECEC] transition-colors active:bg-[#2A2A2A]'
className='flex items-center justify-between px-5 py-3.5 text-[var(--landing-text)] transition-colors active:bg-[var(--landing-bg-elevated)]'
onClick={() => setMobileMenuOpen(false)}
>
{label}
@@ -295,7 +295,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
) : (
<Link
href={href}
className='flex items-center px-[20px] py-[14px] text-[#ECECEC] transition-colors active:bg-[#2A2A2A]'
className='flex items-center px-5 py-3.5 text-[var(--landing-text)] transition-colors active:bg-[var(--landing-bg-elevated)]'
onClick={() => setMobileMenuOpen(false)}
>
{label}
@@ -304,12 +304,12 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
</li>
)
})}
<li className='border-[#2A2A2A] border-b'>
<li className='border-[var(--landing-border)] border-b'>
<a
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='flex items-center gap-[8px] px-[20px] py-[14px] text-[#ECECEC] transition-colors active:bg-[#2A2A2A]'
className='flex items-center gap-2 px-5 py-3.5 text-[var(--landing-text)] transition-colors active:bg-[var(--landing-bg-elevated)]'
onClick={() => setMobileMenuOpen(false)}
>
<GithubOutlineIcon className='h-[14px] w-[14px]' />
@@ -319,15 +319,12 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
</ul>
<div
className={cn(
'mt-auto flex flex-col gap-[10px] p-[20px]',
isSessionPending && 'invisible'
)}
className={cn('mt-auto flex flex-col gap-2.5 p-5', isSessionPending && 'invisible')}
>
{isAuthenticated ? (
<Link
href='/workspace'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
onClick={() => setMobileMenuOpen(false)}
aria-label='Go to app'
>
@@ -337,7 +334,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
<>
<Link
href='/login'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#3d3d3d] text-[#ECECEC] text-[14px] transition-colors active:bg-[#2A2A2A]'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--landing-border-strong)] text-[14px] text-[var(--landing-text)] transition-colors active:bg-[var(--landing-bg-elevated)]'
onClick={() => setMobileMenuOpen(false)}
aria-label='Log in'
>
@@ -345,7 +342,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
</Link>
<Link
href='/signup'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
onClick={() => setMobileMenuOpen(false)}
aria-label='Get started with Sim'
>
@@ -432,7 +429,13 @@ function MobileMenuIcon({ open }: { open: boolean }) {
function ExternalArrowIcon() {
return (
<svg width='12' height='12' viewBox='0 0 12 12' fill='none' className='text-[#666]'>
<svg
width='12'
height='12'
viewBox='0 0 12 12'
fill='none'
className='text-[var(--landing-text-secondary)]'
>
<path
d='M3.5 2.5H9.5V8.5M9 3L3 9'
stroke='currentColor'
@@ -107,21 +107,21 @@ function PricingCard({ tier }: PricingCardProps) {
return (
<article className='flex flex-1 flex-col' aria-labelledby={`${tier.id}-heading`}>
<div className='flex flex-1 flex-col gap-6 rounded-t-lg border border-[#E5E5E5] border-b-0 bg-white p-5'>
<div className='flex flex-1 flex-col gap-6 rounded-t-lg border border-[var(--landing-border-light)] border-b-0 bg-white p-5'>
<div className='flex flex-col'>
<h3
id={`${tier.id}-heading`}
className='font-[430] font-season text-[#1C1C1C] text-[24px] leading-[100%] tracking-[-0.02em]'
className='font-[430] font-season text-[24px] text-[var(--landing-text-dark)] leading-[100%] tracking-[-0.02em]'
>
{tier.name}
</h3>
<p className='mt-2 min-h-[44px] font-[430] font-season text-[#5c5c5c] text-[14px] leading-[125%] tracking-[0.02em]'>
<p className='mt-2 min-h-[44px] font-[430] font-season text-[#5c5c5c] text-sm leading-[125%] tracking-[0.02em]'>
{tier.description}
</p>
<p className='mt-4 flex items-center gap-1.5 font-[430] font-season text-[#1C1C1C] text-[20px] leading-[100%] tracking-[-0.02em]'>
<p className='mt-4 flex items-center gap-1.5 font-[430] font-season text-[20px] text-[var(--landing-text-dark)] leading-[100%] tracking-[-0.02em]'>
{tier.price}
{tier.billingPeriod && (
<span className='text-[#737373] text-[16px]'>{tier.billingPeriod}</span>
<span className='text-[#737373] text-md'>{tier.billingPeriod}</span>
)}
</p>
<div className='mt-4'>
@@ -129,7 +129,7 @@ function PricingCard({ tier }: PricingCardProps) {
<DemoRequestModal theme='light'>
<button
type='button'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#E5E5E5] bg-transparent px-[10px] font-[430] font-season text-[#1C1C1C] text-[14px] transition-colors hover:bg-[#F0F0F0]'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[var(--landing-border-light)] bg-transparent px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:bg-[var(--landing-bg-hover)]'
>
{tier.cta.label}
</button>
@@ -137,14 +137,14 @@ function PricingCard({ tier }: PricingCardProps) {
) : isPro ? (
<Link
href={tier.cta.href || '/signup'}
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-[10px] font-[430] font-season text-[14px] text-white transition-colors hover:border-[#2A2A2A] hover:bg-[#2A2A2A]'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-2.5 font-[430] font-season text-[14px] text-white transition-colors hover:border-[var(--landing-border)] hover:bg-[var(--landing-bg-elevated)]'
>
{tier.cta.label}
</Link>
) : (
<Link
href={tier.cta.href || '/signup'}
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#E5E5E5] px-[10px] font-[430] font-season text-[#1C1C1C] text-[14px] transition-colors hover:bg-[#F0F0F0]'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[var(--landing-border-light)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:bg-[var(--landing-bg-hover)]'
>
{tier.cta.label}
</Link>
@@ -156,7 +156,7 @@ function PricingCard({ tier }: PricingCardProps) {
{tier.features.map((feature) => (
<li key={feature} className='flex items-center gap-2'>
<CheckIcon color='#404040' />
<span className='font-[400] font-season text-[#5c5c5c] text-[14px] leading-[125%] tracking-[0.02em]'>
<span className='font-[400] font-season text-[#5c5c5c] text-sm leading-[125%] tracking-[0.02em]'>
{feature}
</span>
</li>
@@ -187,9 +187,13 @@ function PricingCard({ tier }: PricingCardProps) {
*/
export default function Pricing() {
return (
<section id='pricing' aria-labelledby='pricing-heading' className='bg-[#F6F6F6]'>
<div className='px-4 pt-[60px] pb-[40px] sm:px-8 sm:pt-[80px] sm:pb-0 md:px-[80px] md:pt-[100px]'>
<div className='flex flex-col items-start gap-3 sm:gap-4 md:gap-[20px]'>
<section
id='pricing'
aria-labelledby='pricing-heading'
className='bg-[var(--landing-bg-section)]'
>
<div className='px-4 pt-[60px] pb-10 sm:px-8 sm:pt-20 sm:pb-0 md:px-20 md:pt-[100px]'>
<div className='flex flex-col items-start gap-3 sm:gap-4 md:gap-5'>
<Badge
variant='blue'
size='md'
@@ -201,7 +205,7 @@ export default function Pricing() {
<h2
id='pricing-heading'
className='font-[430] font-season text-[#1C1C1C] text-[32px] leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
className='text-balance font-[430] font-season text-[32px] text-[var(--landing-text-dark)] leading-[100%] tracking-[-0.02em] sm:text-[36px] md:text-[40px]'
>
Pricing
</h2>
@@ -19,7 +19,7 @@ const LandingPreviewWorkflow = dynamic(
).then((mod) => mod.LandingPreviewWorkflow),
{
ssr: false,
loading: () => <div className='h-full w-full bg-[#1b1b1b]' />,
loading: () => <div className='h-full w-full bg-[var(--landing-bg)]' />,
}
)
@@ -337,7 +337,7 @@ function DotGrid({ className, cols, rows, gap = 0 }: DotGridProps) {
}}
>
{Array.from({ length: cols * rows }, (_, i) => (
<div key={i} className='h-[1.5px] w-[1.5px] rounded-full bg-[#2A2A2A]' />
<div key={i} className='h-[1.5px] w-[1.5px] rounded-full bg-[var(--landing-bg-elevated)]' />
))}
</div>
)
@@ -412,7 +412,7 @@ export default function Templates() {
ref={sectionRef}
id='templates'
aria-labelledby='templates-heading'
className='mt-[40px] mb-[80px]'
className='mt-10 mb-20'
>
<p className='sr-only'>
Sim includes {TEMPLATE_WORKFLOWS.length} pre-built workflow templates covering OCR
@@ -422,9 +422,9 @@ export default function Templates() {
customise it, and deploy in minutes.
</p>
<div className='bg-[#1C1C1C]'>
<div className='bg-[var(--landing-bg)]'>
<DotGrid
className='overflow-hidden border-[#2A2A2A] border-y bg-[#1C1C1C] p-[6px]'
className='overflow-hidden border-[var(--landing-bg-elevated)] border-y bg-[var(--landing-bg)] p-1.5'
cols={160}
rows={1}
gap={6}
@@ -449,8 +449,8 @@ export default function Templates() {
</svg>
</div>
<div className='px-[20px] pt-[60px] lg:px-[80px] lg:pt-[100px]'>
<div className='flex flex-col items-start gap-[20px]'>
<div className='px-5 pt-[60px] lg:px-20 lg:pt-[100px]'>
<div className='flex flex-col items-start gap-5'>
<Badge
variant='blue'
size='md'
@@ -466,12 +466,12 @@ export default function Templates() {
<h2
id='templates-heading'
className='font-[430] font-season text-[28px] text-white leading-[100%] tracking-[-0.02em] lg:text-[40px]'
className='text-balance font-[430] font-season text-[28px] text-white leading-[100%] tracking-[-0.02em] lg:text-[40px]'
>
Ship your agent in minutes
</h2>
<p className='font-[430] font-season text-[#F6F6F0]/50 text-[15px] leading-[150%] tracking-[0.02em] lg:text-[18px]'>
<p className='font-[430] font-season text-[#F6F6F0]/50 text-base leading-[150%] tracking-[0.02em] lg:text-lg'>
Pre-built templates for every use case—pick one, swap{' '}
<br className='hidden lg:inline' />
models and tools to fit your stack, and deploy.
@@ -479,11 +479,11 @@ export default function Templates() {
</div>
</div>
<div className='mt-[40px] flex border-[#2A2A2A] border-y lg:mt-[73px]'>
<div className='mt-10 flex border-[var(--landing-bg-elevated)] border-y lg:mt-[73px]'>
<div className='shrink-0'>
<div className='h-full lg:hidden'>
<DotGrid
className='h-full w-[24px] overflow-hidden border-[#2A2A2A] border-r p-[4px]'
className='h-full w-[24px] overflow-hidden border-[var(--landing-bg-elevated)] border-r p-1'
cols={2}
rows={55}
gap={4}
@@ -491,7 +491,7 @@ export default function Templates() {
</div>
<div className='hidden h-full lg:block'>
<DotGrid
className='h-full w-[80px] overflow-hidden border-[#2A2A2A] border-r p-[6px]'
className='h-full w-[80px] overflow-hidden border-[var(--landing-bg-elevated)] border-r p-1.5'
cols={8}
rows={55}
gap={6}
@@ -503,7 +503,7 @@ export default function Templates() {
<div
role='tablist'
aria-label='Workflow templates'
className='flex w-full shrink-0 flex-col border-[#2A2A2A] lg:w-[300px] lg:border-r'
className='flex w-full shrink-0 flex-col border-[var(--landing-bg-elevated)] lg:w-[300px] lg:border-r'
>
{TEMPLATE_WORKFLOWS.map((workflow, index) => {
const isActive = index === activeIndex
@@ -521,7 +521,7 @@ export default function Templates() {
isActive
? 'z-10'
: cn(
'flex items-center px-[12px] py-[10px] hover:bg-[#232323]/50',
'flex items-center px-3 py-2.5 hover:bg-[color-mix(in_srgb,var(--landing-bg-card)_50%,transparent)]',
index < TEMPLATE_WORKFLOWS.length - 1 &&
'shadow-[inset_0_-1px_0_0_#2A2A2A]'
)
@@ -543,8 +543,8 @@ export default function Templates() {
className='absolute right-[-8px] bottom-0 left-2 h-2'
style={buildBottomWallStyle(depth)}
/>
<div className='-translate-y-2 relative flex translate-x-2 items-center bg-[#242424] px-[12px] py-[10px] shadow-[inset_0_0_0_1.5px_#3E3E3E]'>
<span className='flex-1 font-[430] font-season text-[16px] text-white'>
<div className='-translate-y-2 relative flex translate-x-2 items-center bg-[var(--landing-bg-card)] px-3 py-2.5 shadow-[inset_0_0_0_1.5px_#3E3E3E]'>
<span className='flex-1 font-[430] font-season text-md text-white'>
{workflow.name}
</span>
<ChevronDown
@@ -556,7 +556,7 @@ export default function Templates() {
)
})()
) : (
<span className='font-[430] font-season text-[#F6F6F0]/50 text-[16px]'>
<span className='font-[430] font-season text-[#F6F6F0]/50 text-md'>
{workflow.name}
</span>
)}
@@ -571,19 +571,19 @@ export default function Templates() {
transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
className='overflow-hidden'
>
<div className='aspect-[16/10] w-full border-[#2A2A2A] border-y bg-[#1b1b1b]'>
<div className='aspect-[16/10] w-full border-[var(--landing-bg-elevated)] border-y bg-[var(--landing-bg)]'>
<LandingPreviewWorkflow
workflow={workflow}
animate
fitViewOptions={{ padding: 0.15, maxZoom: 1.3 }}
/>
</div>
<div className='p-[12px]'>
<div className='p-3'>
<button
type='button'
onClick={handleUseTemplate}
disabled={isPreparingTemplate}
className='inline-flex h-[32px] w-full cursor-pointer items-center justify-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] font-[430] font-season text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
className='inline-flex h-[32px] w-full cursor-pointer items-center justify-center gap-1.5 rounded-[5px] border border-white bg-white font-[430] font-season text-black text-sm transition-colors active:bg-[#E0E0E0]'
>
{isPreparingTemplate ? 'Preparing...' : 'Use template'}
</button>
@@ -614,7 +614,7 @@ export default function Templates() {
type='button'
onClick={handleUseTemplate}
disabled={isPreparingTemplate}
className='group/cta absolute top-[16px] right-[16px] z-10 inline-flex h-[32px] cursor-pointer items-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='group/cta absolute top-4 right-[16px] z-10 inline-flex h-[32px] cursor-pointer items-center gap-1.5 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
{isPreparingTemplate ? 'Preparing...' : 'Use template'}
<span className='relative h-[10px] w-[10px] shrink-0'>
@@ -642,7 +642,7 @@ export default function Templates() {
<div className='shrink-0'>
<div className='h-full lg:hidden'>
<DotGrid
className='h-full w-[24px] overflow-hidden border-[#2A2A2A] border-l p-[4px]'
className='h-full w-[24px] overflow-hidden border-[var(--landing-bg-elevated)] border-l p-1'
cols={2}
rows={55}
gap={4}
@@ -650,7 +650,7 @@ export default function Templates() {
</div>
<div className='hidden h-full lg:block'>
<DotGrid
className='h-full w-[80px] overflow-hidden border-[#2A2A2A] border-l p-[6px]'
className='h-full w-[80px] overflow-hidden border-[var(--landing-bg-elevated)] border-l p-1.5'
cols={8}
rows={55}
gap={6}
+3 -1
View File
@@ -36,7 +36,9 @@ export default async function Landing() {
const blogPosts = await getNavBlogPosts()
return (
<div className={`${season.variable} ${martianMono.variable} min-h-screen bg-[#1C1C1C]`}>
<div
className={`${season.variable} ${martianMono.variable} min-h-screen bg-[var(--landing-bg)]`}
>
<a
href='#main-content'
className='sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[100] focus:rounded-md focus:bg-white focus:px-4 focus:py-2 focus:font-medium focus:text-black focus:text-sm'
@@ -10,7 +10,7 @@ export function BackLink() {
return (
<Link
href='/blog'
className='group flex items-center gap-1 text-[#999] text-sm hover:text-[#ECECEC]'
className='group flex items-center gap-1 text-[var(--landing-text-muted)] text-sm hover:text-[var(--landing-text)]'
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
+21 -21
View File
@@ -7,51 +7,51 @@ export default function BlogPostLoading() {
<div className='mx-auto max-w-[1450px] px-6 pt-8 sm:px-8 sm:pt-12 md:px-12 md:pt-16'>
{/* Back link */}
<div className='mb-6'>
<Skeleton className='h-[16px] w-[60px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[60px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
{/* Image + title row */}
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
{/* Image */}
<div className='w-full flex-shrink-0 md:w-[450px]'>
<Skeleton className='aspect-[450/360] w-full rounded-lg bg-[#2A2A2A]' />
<Skeleton className='aspect-[450/360] w-full rounded-lg bg-[var(--landing-bg-elevated)]' />
</div>
{/* Title + author */}
<div className='flex flex-1 flex-col justify-between'>
<div>
<Skeleton className='h-[48px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-[8px] h-[48px] w-[80%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[48px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-2 h-[48px] w-[80%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-4 flex items-center justify-between'>
<div className='flex items-center gap-2'>
<Skeleton className='h-[24px] w-[24px] rounded-full bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[24px] w-[24px] rounded-full bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<Skeleton className='h-[32px] w-[32px] rounded-[6px] bg-[#2A2A2A]' />
<Skeleton className='h-[32px] w-[32px] rounded-[6px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
{/* Divider */}
<Skeleton className='mt-8 h-[1px] w-full bg-[#2A2A2A] sm:mt-12' />
<Skeleton className='mt-8 h-[1px] w-full bg-[var(--landing-bg-elevated)] sm:mt-12' />
{/* Date + description */}
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
<Skeleton className='h-[16px] w-[120px] flex-shrink-0 rounded-[4px] bg-[#2A2A2A]' />
<div className='flex-1 space-y-[8px]'>
<Skeleton className='h-[20px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[20px] w-[70%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[120px] flex-shrink-0 rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='flex-1 space-y-2'>
<Skeleton className='h-[20px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[20px] w-[70%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
{/* Article body */}
<div className='mx-auto max-w-[900px] px-6 pb-20 sm:px-8 md:px-12'>
<div className='space-y-[16px]'>
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[95%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[88%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-[24px] h-[24px] w-[200px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[92%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[85%] rounded-[4px] bg-[#2A2A2A]' />
<div className='space-y-4'>
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-6 h-[24px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[92%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</article>
+12 -10
View File
@@ -66,7 +66,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
</div>
<div className='flex flex-1 flex-col justify-between'>
<h1
className='font-[500] text-[#ECECEC] text-[36px] leading-tight tracking-tight sm:text-[48px] md:text-[56px] lg:text-[64px]'
className='text-balance font-[500] text-[36px] text-[var(--landing-text)] leading-tight tracking-tight sm:text-[48px] md:text-[56px] lg:text-[64px]'
itemProp='headline'
>
{post.title}
@@ -85,7 +85,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
href={a?.url || '#'}
target='_blank'
rel='noopener noreferrer author'
className='text-[#999] text-[14px] leading-[1.5] hover:text-[#ECECEC] sm:text-[16px]'
className='text-[var(--landing-text-muted)] text-sm leading-[1.5] hover:text-[var(--landing-text)] sm:text-md'
itemProp='author'
itemScope
itemType='https://schema.org/Person'
@@ -99,11 +99,11 @@ export default async function Page({ params }: { params: Promise<{ slug: string
</div>
</div>
</div>
<hr className='mt-8 border-[#2A2A2A] border-t sm:mt-12' />
<hr className='mt-8 border-[var(--landing-bg-elevated)] border-t sm:mt-12' />
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
<div className='flex flex-shrink-0 items-center gap-4'>
<time
className='block text-[#999] text-[14px] leading-[1.5] sm:text-[16px]'
className='block text-[var(--landing-text-muted)] text-sm leading-[1.5] sm:text-md'
dateTime={post.date}
itemProp='datePublished'
>
@@ -116,7 +116,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
<meta itemProp='dateModified' content={post.updated ?? post.date} />
</div>
<div className='flex-1'>
<p className='m-0 block translate-y-[-4px] font-[400] text-[#999] text-[18px] leading-[1.5] sm:text-[20px] md:text-[26px]'>
<p className='m-0 block translate-y-[-4px] font-[400] text-[var(--landing-text-muted)] text-lg leading-[1.5] sm:text-[20px] md:text-[26px]'>
{post.description}
</p>
</div>
@@ -124,18 +124,18 @@ export default async function Page({ params }: { params: Promise<{ slug: string
</header>
<div className='mx-auto max-w-[900px] px-6 pb-20 sm:px-8 md:px-12' itemProp='articleBody'>
<div className='prose prose-lg prose-invert max-w-none prose-blockquote:border-[#3d3d3d] prose-hr:border-[#2A2A2A] prose-a:text-[#ECECEC] prose-blockquote:text-[#999] prose-code:text-[#ECECEC] prose-headings:text-[#ECECEC] prose-li:text-[#999] prose-p:text-[#999] prose-strong:text-[#ECECEC]'>
<div className='prose prose-lg prose-invert max-w-none prose-blockquote:border-[var(--landing-border-strong)] prose-hr:border-[var(--landing-bg-elevated)] prose-a:text-[var(--landing-text)] prose-blockquote:text-[var(--landing-text-muted)] prose-code:text-[var(--landing-text)] prose-headings:text-[var(--landing-text)] prose-li:text-[var(--landing-text-muted)] prose-p:text-[var(--landing-text-muted)] prose-strong:text-[var(--landing-text)]'>
<Article />
{post.faq && post.faq.length > 0 ? <FAQ items={post.faq} /> : null}
</div>
</div>
{related.length > 0 && (
<div className='mx-auto max-w-[900px] px-6 pb-24 sm:px-8 md:px-12'>
<h2 className='mb-4 font-[500] text-[#ECECEC] text-[24px]'>Related posts</h2>
<h2 className='mb-4 font-[500] text-[24px] text-[var(--landing-text)]'>Related posts</h2>
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3'>
{related.map((p) => (
<Link key={p.slug} href={`/blog/${p.slug}`} className='group'>
<div className='overflow-hidden rounded-lg border border-[#2A2A2A]'>
<div className='overflow-hidden rounded-lg border border-[var(--landing-bg-elevated)]'>
<Image
src={p.ogImage}
alt={p.title}
@@ -147,14 +147,16 @@ export default async function Page({ params }: { params: Promise<{ slug: string
unoptimized
/>
<div className='p-3'>
<div className='mb-1 text-[#999] text-xs'>
<div className='mb-1 text-[var(--landing-text-muted)] text-xs'>
{new Date(p.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</div>
<div className='font-[500] text-[#ECECEC] text-sm leading-tight'>{p.title}</div>
<div className='font-[500] text-[var(--landing-text)] text-sm leading-tight'>
{p.title}
</div>
</div>
</div>
</Link>
@@ -41,7 +41,7 @@ export function ShareButton({ url, title }: ShareButtonProps) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className='flex items-center gap-1.5 text-[#999] text-sm hover:text-[#ECECEC]'
className='flex items-center gap-1.5 text-[var(--landing-text-muted)] text-sm hover:text-[var(--landing-text)]'
aria-label='Share this post'
>
<Share2 className='h-4 w-4' />
@@ -6,16 +6,16 @@ export default function AuthorLoading() {
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<div className='mb-6 flex items-center gap-3'>
<Skeleton className='h-[40px] w-[40px] rounded-full bg-[#2A2A2A]' />
<Skeleton className='h-[32px] w-[160px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[40px] w-[40px] rounded-full bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[32px] w-[160px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2'>
{Array.from({ length: SKELETON_POST_COUNT }).map((_, i) => (
<div key={i} className='overflow-hidden rounded-lg border border-[#2A2A2A]'>
<Skeleton className='h-[160px] w-full rounded-none bg-[#2A2A2A]' />
<div key={i} className='overflow-hidden rounded-lg border border-[var(--landing-border)]'>
<Skeleton className='h-[160px] w-full rounded-none bg-[var(--landing-bg-elevated)]' />
<div className='p-3'>
<Skeleton className='mb-1 h-[12px] w-[80px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[14px] w-[200px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mb-1 h-[12px] w-[80px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[14px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
))}
@@ -23,7 +23,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
if (!author) {
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<h1 className='font-[500] text-[#ECECEC] text-[32px]'>Author not found</h1>
<h1 className='font-[500] text-[32px] text-[var(--landing-text)]'>Author not found</h1>
</main>
)
}
@@ -52,12 +52,14 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
unoptimized
/>
) : null}
<h1 className='font-[500] text-[#ECECEC] text-[32px] leading-tight'>{author.name}</h1>
<h1 className='font-[500] text-[32px] text-[var(--landing-text)] leading-tight'>
{author.name}
</h1>
</div>
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2'>
{posts.map((p) => (
<Link key={p.slug} href={`/blog/${p.slug}`} className='group'>
<div className='overflow-hidden rounded-lg border border-[#2A2A2A]'>
<div className='overflow-hidden rounded-lg border border-[var(--landing-bg-elevated)]'>
<Image
src={p.ogImage}
alt={p.title}
@@ -67,14 +69,16 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
unoptimized
/>
<div className='p-3'>
<div className='mb-1 text-[#999] text-xs'>
<div className='mb-1 text-[var(--landing-text-muted)] text-xs'>
{new Date(p.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</div>
<div className='font-[500] text-[#ECECEC] text-sm leading-tight'>{p.title}</div>
<div className='font-[500] text-[var(--landing-text)] text-sm leading-tight'>
{p.title}
</div>
</div>
</div>
</Link>
+1 -1
View File
@@ -26,7 +26,7 @@ export default async function StudioLayout({ children }: { children: React.React
}
return (
<div className='flex min-h-screen flex-col bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]'>
<div className='flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
+13 -10
View File
@@ -5,20 +5,23 @@ const SKELETON_CARD_COUNT = 6
export default function BlogLoading() {
return (
<main className='mx-auto max-w-[1200px] px-6 py-12 sm:px-8 md:px-12'>
<Skeleton className='h-[48px] w-[100px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-3 h-[18px] w-[420px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[48px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-3 h-[18px] w-[420px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-10 grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
{Array.from({ length: SKELETON_CARD_COUNT }).map((_, i) => (
<div key={i} className='flex flex-col overflow-hidden rounded-xl border border-[#2A2A2A]'>
<Skeleton className='aspect-video w-full rounded-none bg-[#2A2A2A]' />
<div
key={i}
className='flex flex-col overflow-hidden rounded-xl border border-[var(--landing-border)]'
>
<Skeleton className='aspect-video w-full rounded-none bg-[var(--landing-bg-elevated)]' />
<div className='flex flex-1 flex-col p-4'>
<Skeleton className='mb-2 h-[12px] w-[80px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mb-1 h-[20px] w-[85%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mb-3 h-[14px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[14px] w-[70%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mb-2 h-[12px] w-[80px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mb-1 h-[20px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mb-3 h-[14px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[14px] w-[70%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-3 flex items-center gap-2'>
<Skeleton className='h-[16px] w-[16px] rounded-full bg-[#2A2A2A]' />
<Skeleton className='h-[12px] w-[80px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[16px] w-[16px] rounded-full bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[12px] w-[80px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
+5 -5
View File
@@ -50,10 +50,10 @@ export default async function BlogIndex({
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(blogJsonLd) }}
/>
<h1 className='mb-3 font-[500] text-[#ECECEC] text-[40px] leading-tight sm:text-[56px]'>
<h1 className='mb-3 text-balance font-[500] text-[40px] text-[var(--landing-text)] leading-tight sm:text-[56px]'>
Blog
</h1>
<p className='mb-10 text-[#999] text-[18px]'>
<p className='mb-10 text-[var(--landing-text-muted)] text-lg'>
Announcements, insights, and guides for building AI agent workflows.
</p>
@@ -75,18 +75,18 @@ export default async function BlogIndex({
{pageNum > 1 && (
<Link
href={`/blog?page=${pageNum - 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
className='rounded-[5px] border border-[#3d3d3d] px-3 py-1 text-[#ECECEC] text-sm transition-colors hover:bg-[#2A2A2A]'
className='rounded-[5px] border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
Previous
</Link>
)}
<span className='text-[#999] text-sm'>
<span className='text-[var(--landing-text-muted)] text-sm'>
Page {pageNum} of {totalPages}
</span>
{pageNum < totalPages && (
<Link
href={`/blog?page=${pageNum + 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
className='rounded-[5px] border border-[#3d3d3d] px-3 py-1 text-[#ECECEC] text-sm transition-colors hover:bg-[#2A2A2A]'
className='rounded-[5px] border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
Next
</Link>
+11 -7
View File
@@ -25,7 +25,7 @@ export function PostGrid({ posts }: { posts: Post[] }) {
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
{posts.map((p, index) => (
<Link key={p.slug} href={`/blog/${p.slug}`} className='group flex flex-col'>
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-[#2A2A2A] transition-colors duration-300 hover:border-[#3d3d3d]'>
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-[var(--landing-bg-elevated)] transition-colors duration-300 hover:border-[var(--landing-border-strong)]'>
{/* Image container with fixed aspect ratio to prevent layout shift */}
<div className='relative aspect-video w-full overflow-hidden'>
<Image
@@ -40,29 +40,33 @@ export function PostGrid({ posts }: { posts: Post[] }) {
/>
</div>
<div className='flex flex-1 flex-col p-4'>
<div className='mb-2 text-[#999] text-xs'>
<div className='mb-2 text-[var(--landing-text-muted)] text-xs'>
{new Date(p.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</div>
<h3 className='mb-1 font-[500] text-[#ECECEC] text-lg leading-tight'>{p.title}</h3>
<p className='mb-3 line-clamp-3 flex-1 text-[#999] text-sm'>{p.description}</p>
<h3 className='mb-1 font-[500] text-[var(--landing-text)] text-lg leading-tight'>
{p.title}
</h3>
<p className='mb-3 line-clamp-3 flex-1 text-[var(--landing-text-muted)] text-sm'>
{p.description}
</p>
<div className='flex items-center gap-2'>
<div className='-space-x-1.5 flex'>
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
.slice(0, 3)
.map((author, idx) => (
<Avatar key={idx} className='size-4 border border-[#1C1C1C]'>
<Avatar key={idx} className='size-4 border border-[var(--landing-text)]'>
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
<AvatarFallback className='border border-[#1C1C1C] bg-[#2A2A2A] text-[#999] text-[10px]'>
<AvatarFallback className='border border-[var(--landing-text)] bg-[var(--landing-bg-elevated)] text-[var(--landing-text-muted)] text-micro'>
{author?.name.slice(0, 2)}
</AvatarFallback>
</Avatar>
))}
</div>
<span className='text-[#999] text-xs'>
<span className='text-[var(--landing-text-muted)] text-xs'>
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
.slice(0, 2)
.map((a) => a?.name)
+2 -2
View File
@@ -5,12 +5,12 @@ const SKELETON_TAG_COUNT = 12
export default function TagsLoading() {
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<Skeleton className='mb-6 h-[32px] w-[200px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mb-6 h-[32px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='flex flex-wrap gap-3'>
{Array.from({ length: SKELETON_TAG_COUNT }).map((_, i) => (
<Skeleton
key={i}
className='h-[30px] rounded-full bg-[#2A2A2A]'
className='h-[30px] rounded-full bg-[var(--landing-bg-elevated)]'
style={{ width: `${60 + (i % 4) * 24}px` }}
/>
))}
+5 -3
View File
@@ -10,11 +10,13 @@ export default async function TagsIndex() {
const tags = await getAllTags()
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<h1 className='mb-6 font-[500] text-[#ECECEC] text-[32px] leading-tight'>Browse by tag</h1>
<h1 className='mb-6 font-[500] text-[32px] text-[var(--landing-text)] leading-tight'>
Browse by tag
</h1>
<div className='flex flex-wrap gap-3'>
<Link
href='/blog'
className='rounded-full border border-[#3d3d3d] px-3 py-1 text-[#ECECEC] text-sm transition-colors hover:bg-[#2A2A2A]'
className='rounded-full border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
All
</Link>
@@ -22,7 +24,7 @@ export default async function TagsIndex() {
<Link
key={t.tag}
href={`/blog?tag=${encodeURIComponent(t.tag)}`}
className='rounded-full border border-[#3d3d3d] px-3 py-1 text-[#ECECEC] text-sm transition-colors hover:bg-[#2A2A2A]'
className='rounded-full border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
{t.tag} ({t.count})
</Link>
@@ -4,7 +4,7 @@ import { HIPAABadgeIcon } from '@/components/icons'
export default function ComplianceBadges() {
return (
<div className='mt-[6px] flex items-center gap-[12px]'>
<div className='mt-1.5 flex items-center gap-3'>
{/* SOC2 badge */}
<Link
href='https://app.vanta.com/sim.ai/trust/v35ia0jil4l7dteqjgaktn'
@@ -2,12 +2,12 @@ import { DiscordIcon, GithubIcon, LinkedInIcon, xIcon as XIcon } from '@/compone
export default function SocialLinks() {
return (
<div className='flex items-center gap-[12px]'>
<div className='flex items-center gap-3'>
<a
href='https://discord.gg/Hr4UWYEcTT'
target='_blank'
rel='noopener noreferrer'
className='flex items-center text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label='Discord'
>
<DiscordIcon className='h-[20px] w-[20px]' aria-hidden='true' />
@@ -16,7 +16,7 @@ export default function SocialLinks() {
href='https://x.com/simdotai'
target='_blank'
rel='noopener noreferrer'
className='flex items-center text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label='X (Twitter)'
>
<XIcon className='h-[18px] w-[18px]' aria-hidden='true' />
@@ -25,7 +25,7 @@ export default function SocialLinks() {
href='https://www.linkedin.com/company/simstudioai/'
target='_blank'
rel='noopener noreferrer'
className='flex items-center text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label='LinkedIn'
>
<LinkedInIcon className='h-[18px] w-[18px]' aria-hidden='true' />
@@ -34,7 +34,7 @@ export default function SocialLinks() {
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='flex items-center text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label='GitHub'
>
<GithubIcon className='h-[20px] w-[20px]' aria-hidden='true' />
@@ -36,8 +36,8 @@ export function StatusDotIcon({ status, className, ...props }: StatusDotIconProp
const STATUS_COLORS: Record<StatusType, string> = {
operational: 'text-[#10B981] hover:text-[#059669]',
degraded: 'text-[#F59E0B] hover:text-[#D97706]',
outage: 'text-[#EF4444] hover:text-[#DC2626]',
degraded: 'text-[var(--caution)] hover:text-[#D97706]',
outage: 'text-[var(--text-error)] hover:text-[var(--error)]',
maintenance: 'text-[#3B82F6] hover:text-[#2563EB]',
loading: 'text-muted-foreground hover:text-foreground',
error: 'text-muted-foreground hover:text-foreground',
@@ -59,7 +59,7 @@ export default function StatusIndicator() {
href={statusUrl}
target='_blank'
rel='noopener noreferrer'
className={`flex min-w-[165px] items-center gap-[6px] whitespace-nowrap text-[12px] transition-colors ${STATUS_COLORS[status]}`}
className={`flex min-w-[165px] items-center gap-1.5 whitespace-nowrap text-caption transition-colors ${STATUS_COLORS[status]}`}
aria-label={`System status: ${message}`}
>
<StatusDotIcon status={status} className='h-[6px] w-[6px]' aria-hidden='true' />
@@ -17,13 +17,13 @@ export default function Footer({ fullWidth = false }: FooterProps) {
<div
className={
fullWidth
? 'px-4 pt-[40px] pb-[40px] sm:px-4 sm:pt-[34px] sm:pb-[340px]'
: 'px-4 pt-[40px] pb-[40px] sm:px-[50px] sm:pt-[34px] sm:pb-[340px]'
? 'px-4 pt-10 pb-10 sm:px-4 sm:pt-[34px] sm:pb-[340px]'
: 'px-4 pt-10 pb-10 sm:px-[50px] sm:pt-[34px] sm:pb-[340px]'
}
>
<div className={`flex gap-[80px] ${fullWidth ? 'justify-center' : ''}`}>
<div className={`flex gap-20 ${fullWidth ? 'justify-center' : ''}`}>
{/* Logo and social links */}
<div className='flex flex-col gap-[24px]'>
<div className='flex flex-col gap-6'>
<Logo />
<SocialLinks />
<ComplianceBadges />
@@ -32,19 +32,19 @@ export default function Footer({ fullWidth = false }: FooterProps) {
{/* Links section */}
<div>
<h2 className='mb-[16px] font-medium text-[14px] text-foreground'>More Sim</h2>
<div className='flex flex-col gap-[12px]'>
<h2 className='mb-4 font-medium text-foreground text-sm'>More Sim</h2>
<div className='flex flex-col gap-3'>
<Link
href='https://docs.sim.ai'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Docs
</Link>
<Link
href='#pricing'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Pricing
</Link>
@@ -52,19 +52,19 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href='https://form.typeform.com/to/jqCO12pF'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Enterprise
</Link>
<Link
href='/blog'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Blog
</Link>
<Link
href='/changelog'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Changelog
</Link>
@@ -72,7 +72,7 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href='https://status.sim.ai'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Status
</Link>
@@ -80,7 +80,7 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href='https://jobs.ashbyhq.com/sim'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Careers
</a>
@@ -88,7 +88,7 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href='/privacy'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Privacy Policy
</Link>
@@ -96,7 +96,7 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href='/terms'
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
Terms of Service
</Link>
@@ -105,15 +105,15 @@ export default function Footer({ fullWidth = false }: FooterProps) {
{/* Blocks section */}
<div className='hidden sm:block'>
<h2 className='mb-[16px] font-medium text-[14px] text-foreground'>Blocks</h2>
<div className='flex flex-col gap-[12px]'>
<h2 className='mb-4 font-medium text-foreground text-sm'>Blocks</h2>
<div className='flex flex-col gap-3'>
{FOOTER_BLOCKS.map((block) => (
<Link
key={block}
href={`https://docs.sim.ai/blocks/${block.toLowerCase().replaceAll(' ', '-')}`}
target='_blank'
rel='noopener noreferrer'
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='text-muted-foreground text-sm transition-colors hover:text-foreground'
>
{block}
</Link>
@@ -123,24 +123,24 @@ export default function Footer({ fullWidth = false }: FooterProps) {
{/* Tools section - split into columns */}
<div className='hidden sm:block'>
<h2 className='mb-[16px] font-medium text-[14px] text-foreground'>Tools</h2>
<div className='flex gap-[80px]'>
<h2 className='mb-4 font-medium text-foreground text-sm'>Tools</h2>
<div className='flex gap-20'>
{/* First column */}
<div className='flex flex-col gap-[12px]'>
<div className='flex flex-col gap-3'>
{FOOTER_TOOLS.slice(0, Math.ceil(FOOTER_TOOLS.length / 4)).map((tool) => (
<Link
key={tool}
href={`https://docs.sim.ai/tools/${tool.toLowerCase().replace(/\s+/g, '_')}`}
target='_blank'
rel='noopener noreferrer'
className='whitespace-nowrap text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='whitespace-nowrap text-muted-foreground text-sm transition-colors hover:text-foreground'
>
{tool}
</Link>
))}
</div>
{/* Second column */}
<div className='flex flex-col gap-[12px]'>
<div className='flex flex-col gap-3'>
{FOOTER_TOOLS.slice(
Math.ceil(FOOTER_TOOLS.length / 4),
Math.ceil((FOOTER_TOOLS.length * 2) / 4)
@@ -150,14 +150,14 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href={`https://docs.sim.ai/tools/${tool.toLowerCase().replace(/\s+/g, '_')}`}
target='_blank'
rel='noopener noreferrer'
className='whitespace-nowrap text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='whitespace-nowrap text-muted-foreground text-sm transition-colors hover:text-foreground'
>
{tool}
</Link>
))}
</div>
{/* Third column */}
<div className='flex flex-col gap-[12px]'>
<div className='flex flex-col gap-3'>
{FOOTER_TOOLS.slice(
Math.ceil((FOOTER_TOOLS.length * 2) / 4),
Math.ceil((FOOTER_TOOLS.length * 3) / 4)
@@ -167,21 +167,21 @@ export default function Footer({ fullWidth = false }: FooterProps) {
href={`https://docs.sim.ai/tools/${tool.toLowerCase().replace(/\s+/g, '_')}`}
target='_blank'
rel='noopener noreferrer'
className='whitespace-nowrap text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='whitespace-nowrap text-muted-foreground text-sm transition-colors hover:text-foreground'
>
{tool}
</Link>
))}
</div>
{/* Fourth column */}
<div className='flex flex-col gap-[12px]'>
<div className='flex flex-col gap-3'>
{FOOTER_TOOLS.slice(Math.ceil((FOOTER_TOOLS.length * 3) / 4)).map((tool) => (
<Link
key={tool}
href={`https://docs.sim.ai/tools/${tool.toLowerCase().replace(/\s+/g, '_')}`}
target='_blank'
rel='noopener noreferrer'
className='whitespace-nowrap text-[14px] text-muted-foreground transition-colors hover:text-foreground'
className='whitespace-nowrap text-muted-foreground text-sm transition-colors hover:text-foreground'
>
{tool}
</Link>
@@ -27,8 +27,8 @@ export function IconButton({
onMouseEnter={onMouseEnter}
className={`flex items-center justify-center rounded-xl border p-2 outline-none transition-all duration-300 ${
isAutoHovered
? 'border-[#E5E5E5] shadow-[0_2px_4px_0_rgba(0,0,0,0.08)]'
: 'border-transparent hover:border-[#E5E5E5] hover:shadow-[0_2px_4px_0_rgba(0,0,0,0.08)]'
? 'border-[#454545] shadow-subtle'
: 'border-transparent hover:border-[#454545] hover:shadow-subtle'
}`}
style={style}
>
@@ -44,20 +44,20 @@ export const LandingBlock = React.memo(function LandingBlock({
return (
<div
className={`z-10 flex w-[250px] flex-col rounded-[8px] border border-[#E5E5E5] bg-white ${className ?? ''}`}
className={`z-10 flex w-[250px] flex-col rounded-lg border border-[var(--landing-border-light)] bg-white ${className ?? ''}`}
>
{/* Header - matches workflow-block.tsx header styling */}
<div
className={`flex items-center justify-between p-[8px] ${hasContentBelowHeader ? 'border-[#E5E5E5] border-b' : ''}`}
className={`flex items-center justify-between p-2 ${hasContentBelowHeader ? 'border-[var(--landing-border-light)] border-b' : ''}`}
>
<div className='flex min-w-0 flex-1 items-center gap-[10px]'>
<div className='flex min-w-0 flex-1 items-center gap-2.5'>
<div
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-md'
style={{ background: color as string }}
>
{icon}
</div>
<span className='truncate font-medium text-[#171717] text-[16px]' title={name}>
<span className='truncate font-medium text-[#171717] text-md' title={name}>
{name}
</span>
</div>
@@ -65,7 +65,7 @@ export const LandingBlock = React.memo(function LandingBlock({
{/* Content - SubBlock Rows matching workflow-block.tsx */}
{hasContentBelowHeader && (
<div className='flex flex-col gap-[8px] p-[8px]'>
<div className='flex flex-col gap-2 p-2'>
{tags.map((tag) => (
<SubBlockRow key={tag.label} icon={tag.icon} label={tag.label} />
))}
@@ -30,15 +30,12 @@ export const SubBlockRow = React.memo(function SubBlockRow({ label, value }: Sub
: [label, value]
return (
<div className='flex items-center gap-[8px]'>
<span className='min-w-0 truncate text-[#888888] text-[14px] capitalize' title={title}>
<div className='flex items-center gap-2'>
<span className='min-w-0 truncate text-[#888888] text-sm capitalize' title={title}>
{title}
</span>
{displayValue && (
<span
className='flex-1 truncate text-right text-[#171717] text-[14px]'
title={displayValue}
>
<span className='flex-1 truncate text-right text-[#171717] text-sm' title={displayValue}>
{displayValue}
</span>
)}
@@ -128,7 +128,7 @@ export function LandingCanvas({
{/* <button
type='button'
aria-label='Use template'
className='absolute top-[24px] left-[50px] z-20 inline-flex items-center justify-center rounded-[10px] border border-[#343434] bg-gradient-to-b from-[#060606] to-[#323232] px-3 py-1.5 text-sm text-white shadow-[inset_0_1.25px_2.5px_0_#9B77FF] transition-all duration-200'
className='absolute top-6 left-[50px] z-20 inline-flex items-center justify-center rounded-[10px] border border-[#343434] bg-gradient-to-b from-[#060606] to-[#323232] px-3 py-1.5 text-sm text-white shadow-brand-inset transition-all duration-200'
onClick={() => {
// Template usage logic will be implemented here
}}
@@ -363,20 +363,20 @@ export default function Hero() {
return (
<section
id='hero'
className='flex w-full flex-col items-center justify-center pt-[36px] sm:pt-[80px]'
className='flex w-full flex-col items-center justify-center pt-9 sm:pt-20'
aria-labelledby='hero-heading'
>
<h1
id='hero-heading'
className='px-4 text-center font-medium text-[36px] leading-none tracking-tight sm:px-0 sm:text-[74px]'
className='text-balance px-4 text-center font-medium text-[36px] leading-none tracking-tight sm:px-0 sm:text-[74px]'
>
Workflows for LLMs
</h1>
<p className='px-4 pt-[6px] text-center text-[18px] opacity-70 sm:px-0 sm:pt-[10px] sm:text-[22px]'>
<p className='px-4 pt-1.5 text-center text-lg opacity-70 sm:px-0 sm:pt-2.5 sm:text-[22px]'>
Build and deploy AI agent workflows
</p>
<div
className='flex items-center justify-center gap-[2px] pt-[18px] sm:pt-[32px]'
className='flex items-center justify-center gap-0.5 pt-4.5 sm:pt-8'
onMouseEnter={handleIconContainerMouseEnter}
onMouseLeave={handleIconContainerMouseLeave}
>
@@ -397,7 +397,7 @@ export default function Hero() {
)
})}
</div>
<div className='flex w-full items-center justify-center px-4 pt-[8px] sm:px-8 sm:pt-[12px] md:px-[50px]'>
<div className='flex w-full items-center justify-center px-4 pt-2 sm:px-8 sm:pt-3 md:px-[50px]'>
<div className='relative w-full sm:w-[640px]'>
<label htmlFor='agent-description' className='sr-only'>
Describe the AI agent you want to build
@@ -424,7 +424,7 @@ export default function Hero() {
key={isEmpty ? 'empty' : 'filled'}
type='button'
aria-label='Submit description'
className='absolute right-2.5 bottom-4 flex h-[30px] w-[30px] items-center justify-center transition-all duration-200 sm:right-[11px] sm:bottom-[16px] sm:h-[34px] sm:w-[34px]'
className='absolute right-2.5 bottom-4 flex h-[30px] w-[30px] items-center justify-center transition-all duration-200 sm:right-[11px] sm:bottom-4 sm:h-[34px] sm:w-[34px]'
disabled={isEmpty}
onClick={handleSubmit}
style={{
@@ -93,7 +93,7 @@ function TickerRow({ direction, offset, showOdd, icons }: TickerRowProps) {
return (
<div className='relative h-[88px] w-full overflow-hidden'>
<div
className={`absolute flex items-center gap-[16px] ${
className={`absolute flex items-center gap-4 ${
direction === 'left' ? 'animate-slide-left' : 'animate-slide-right'
}`}
style={{
@@ -121,21 +121,21 @@ export default function Integrations() {
return (
<section
id='integrations'
className='flex flex-col pt-[40px] pb-[27px] sm:pt-[24px]'
className='flex flex-col pt-10 pb-[27px] sm:pt-6'
aria-labelledby='integrations-heading'
>
<h2
id='integrations-heading'
className='mb-[4px] px-4 font-medium text-[28px] text-foreground tracking-tight sm:pl-[50px]'
className='mb-1 px-4 font-medium text-[28px] text-foreground tracking-tight sm:pl-[50px]'
>
Integrations
</h2>
<p className='mb-[24px] px-4 text-[#515151] text-[18px] sm:pl-[50px]'>
<p className='mb-6 px-4 text-[#515151] text-lg sm:pl-[50px]'>
Immediately connect to 100+ models and apps
</p>
{/* Sliding tickers */}
<div className='flex w-full flex-col sm:px-[12px]'>
<div className='flex w-full flex-col sm:px-3'>
<TickerRow direction='left' offset={0} showOdd={false} icons={modelProviderIcons} />
<TickerRow direction='right' offset={0.5} showOdd={true} icons={communicationIcons} />
<TickerRow direction='left' offset={1} showOdd={false} icons={dataStorageIcons} />
@@ -159,7 +159,7 @@ function PricingCard({
</span>
</div>
<ul className='mb-[2px] space-y-3'>
<ul className='mb-0.5 space-y-3'>
{tier.features.map((feature, idx) => (
<li key={idx} className='flex items-start gap-2'>
<feature.icon
@@ -182,7 +182,7 @@ function PricingCard({
onClick={handleCtaClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#E8E8E8] bg-gradient-to-b from-[#F8F8F8] to-white px-3 py-[6px] font-medium text-[#6F3DFA] text-[14px] shadow-[inset_0_2px_4px_0_rgba(255,255,255,0.9)] transition-all'
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#E8E8E8] bg-gradient-to-b from-[#F8F8F8] to-white px-3 py-1.5 font-medium text-[#6F3DFA] text-[14px] shadow-[inset_0_2px_4px_0_rgba(255,255,255,0.9)] transition-all'
>
<span className='flex items-center gap-1'>
{tier.ctaText}
@@ -200,7 +200,7 @@ function PricingCard({
onClick={handleCtaClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#343434] bg-gradient-to-b from-[#060606] to-[#323232] px-3 py-[6px] font-medium text-[14px] text-white shadow-[inset_0_1.25px_2.5px_0_#9B77FF] transition-all'
className='group inline-flex w-full items-center justify-center gap-2 rounded-[10px] border border-[#343434] bg-gradient-to-b from-[#060606] to-[#323232] px-3 py-1.5 font-medium text-[14px] text-white shadow-[inset_0_1.25px_2.5px_0_#9B77FF] transition-all'
>
<span className='flex items-center gap-1'>
{tier.ctaText}
@@ -5,7 +5,7 @@ import { PricingGrid } from '@/app/(landing)/components/landing-pricing/componen
*/
export default function LandingPricing() {
return (
<section id='pricing' className='px-4 pt-[23px] sm:px-0 sm:pt-[4px]' aria-label='Pricing plans'>
<section id='pricing' className='px-4 pt-[23px] sm:px-0 sm:pt-1' aria-label='Pricing plans'>
<h2 className='sr-only'>Pricing Plans</h2>
<div className='relative mx-auto w-full max-w-[1289px]'>
<PricingGrid />
@@ -79,12 +79,12 @@ export default function LandingTemplates() {
return (
<section
id='templates'
className='flex flex-col px-4 pt-[40px] sm:px-[50px] sm:pt-[34px]'
className='flex flex-col px-4 pt-10 sm:px-[50px] sm:pt-[34px]'
aria-labelledby='templates-heading'
>
<h2
id='templates-heading'
className='mb-[16px] font-medium text-[28px] text-foreground tracking-tight sm:mb-[24px]'
className='mb-4 font-medium text-[28px] text-foreground tracking-tight sm:mb-6'
>
Templates
</h2>
@@ -12,16 +12,16 @@ export default async function LegalLayout({ title, children }: LegalLayoutProps)
const blogPosts = await getNavBlogPosts()
return (
<main className='min-h-screen bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]'>
<main className='min-h-screen bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
<header>
<Navbar blogPosts={blogPosts} />
</header>
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-[80px] sm:px-12'>
<h1 className='mb-12 text-center font-[500] text-4xl text-[#ECECEC] md:text-5xl'>
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-20 sm:px-12'>
<h1 className='mb-12 text-balance text-center font-[500] text-4xl text-[var(--landing-text)] md:text-5xl'>
{title}
</h1>
<div className='space-y-8 text-[#999] text-[15px] leading-[1.7] [&_h2]:mt-12 [&_h2]:mb-6 [&_h2]:text-[#ECECEC] [&_h3]:mt-8 [&_h3]:mb-4 [&_h3]:text-[#ECECEC] [&_li]:text-[#999] [&_strong]:text-[#ECECEC]'>
<div className='space-y-8 text-[var(--landing-text-muted)] text-base leading-[1.7] [&_h2]:mt-12 [&_h2]:mb-6 [&_h2]:text-[var(--landing-text)] [&_h3]:mt-8 [&_h3]:mb-4 [&_h3]:text-[var(--landing-text)] [&_li]:text-[var(--landing-text-muted)] [&_strong]:text-[var(--landing-text)]'>
{children}
</div>
</div>
+11 -11
View File
@@ -71,7 +71,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
href='https://docs.sim.ai'
target='_blank'
rel='noopener noreferrer'
className='text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='text-md text-muted-foreground transition-colors hover:text-foreground'
prefetch={false}
>
Docs
@@ -80,7 +80,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
<li>
<Link
href={useHomeLinks ? '/?home#pricing' : '/#pricing'}
className='text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='text-md text-muted-foreground transition-colors hover:text-foreground'
scroll={true}
>
Pricing
@@ -89,7 +89,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
<li>
<button
onClick={handleEnterpriseClick}
className='text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='text-md text-muted-foreground transition-colors hover:text-foreground'
type='button'
aria-label='Contact for Enterprise pricing'
>
@@ -101,7 +101,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
href='https://jobs.ashbyhq.com/sim'
target='_blank'
rel='noopener noreferrer'
className='text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='text-md text-muted-foreground transition-colors hover:text-foreground'
>
Careers
</a>
@@ -111,7 +111,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='flex items-center gap-2 text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center gap-2 text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label={`GitHub repository - ${githubStars} stars`}
>
<GithubIcon className='h-[16px] w-[16px]' aria-hidden='true' />
@@ -125,8 +125,8 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
<nav
aria-label='Primary navigation'
className={`flex w-full items-center justify-between px-4 ${
variant === 'auth' ? 'pt-[20px] sm:pt-[16.5px]' : 'pt-[12px] sm:pt-[8.5px]'
} pb-[21px] sm:px-8 md:px-[44px]`}
variant === 'auth' ? 'pt-5 sm:pt-[16.5px]' : 'pt-3 sm:pt-[8.5px]'
} pb-[21px] sm:px-8 md:px-11`}
itemScope
itemType='https://schema.org/SiteNavigationElement'
>
@@ -161,7 +161,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
</Link>
{/* Desktop Navigation Links - only show on landing and if hosted */}
{variant === 'landing' && isHosted && (
<ul className='hidden items-center justify-center gap-[20px] pt-[4px] md:flex'>
<ul className='hidden items-center justify-center gap-5 pt-1 md:flex'>
<NavLinks />
</ul>
)}
@@ -170,14 +170,14 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
{/* Auth Buttons - show only when hosted, regardless of variant */}
{!hideAuthButtons && isHosted && (
<div
className={`flex items-center justify-center gap-[16px] pt-[1.5px]${isSessionPending ? ' invisible' : ''}`}
className={`flex items-center justify-center gap-4 pt-[1.5px]${isSessionPending ? ' invisible' : ''}`}
>
{isAuthenticated ? (
<Link
href='/workspace'
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-1.5 pr-2.5 pl-3 text-[15px] text-white transition-all`}
aria-label='Go to app'
>
<span className='flex items-center gap-1'>
@@ -216,7 +216,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
href='/signup'
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-1.5 pr-2.5 pl-3 text-[15px] text-white transition-all`}
aria-label='Get started with Sim - Sign up for free'
prefetch={true}
>
@@ -125,7 +125,7 @@ export default function Testimonials() {
className='flex hidden h-[150px] items-center sm:block'
aria-label='Social proof testimonials'
>
<div className='relative mx-auto h-full w-full max-w-[1289px] pl-[2px]'>
<div className='relative mx-auto h-full w-full max-w-[1289px] pl-0.5'>
<div
className='relative h-full w-full overflow-hidden'
onMouseEnter={() => setIsPaused(true)}
@@ -146,7 +146,7 @@ export default function Testimonials() {
<div key={`${absoluteIndex}`} className='flex h-full w-1/4 flex-shrink-0'>
{/* Tweet container */}
<div
className='group flex h-full w-full cursor-pointer flex-col px-[12px] py-[12px] transition-all duration-100 hover:bg-[#0A0A0A] sm:px-[14px]'
className='group flex h-full w-full cursor-pointer flex-col px-3 py-3 transition-all duration-100 hover:bg-[#0A0A0A] sm:px-3.5'
onClick={() => window.open(tweet.tweetUrl, '_blank', 'noopener,noreferrer')}
>
{/* Top section with profile info */}
@@ -179,7 +179,7 @@ export default function Testimonials() {
</div>
{/* Tweet content below with padding */}
<p className='mt-2 line-clamp-4 font-[380] text-[#0A0A0A] text-[13px] leading-[1.3] transition-colors duration-300 group-hover:text-white'>
<p className='mt-2 line-clamp-4 font-[380] text-[#0A0A0A] text-small leading-[1.3] transition-colors duration-300 group-hover:text-white'>
{tweet.text}
</p>
</div>
@@ -13,7 +13,7 @@ export function IntegrationFAQ({ faqs }: IntegrationFAQProps) {
const [openIndex, setOpenIndex] = useState<number | null>(0)
return (
<div className='divide-y divide-[#2A2A2A]'>
<div className='divide-y divide-[var(--landing-border)]'>
{faqs.map(({ question, answer }, index) => {
const isOpen = openIndex === index
return (
@@ -27,7 +27,9 @@ export function IntegrationFAQ({ faqs }: IntegrationFAQProps) {
<span
className={cn(
'font-[500] text-[15px] leading-snug transition-colors',
isOpen ? 'text-[#ECECEC]' : 'text-[#999] hover:text-[#ECECEC]'
isOpen
? 'text-[var(--landing-text)]'
: 'text-[var(--landing-text-muted)] hover:text-[var(--landing-text)]'
)}
>
{question}
@@ -43,7 +45,9 @@ export function IntegrationFAQ({ faqs }: IntegrationFAQProps) {
{isOpen && (
<div className='pb-5'>
<p className='text-[#999] text-[14px] leading-[1.75]'>{answer}</p>
<p className='text-[14px] text-[var(--landing-text-muted)] leading-[1.75]'>
{answer}
</p>
</div>
)}
</div>
@@ -20,7 +20,7 @@ export function TemplateCardButton({ prompt, children }: TemplateCardButtonProps
<button
type='button'
onClick={handleClick}
className='group flex w-full flex-col items-start rounded-lg border border-[#2A2A2A] bg-[#242424] p-5 text-left transition-colors hover:border-[#3d3d3d] hover:bg-[#2A2A2A]'
className='group flex w-full flex-col items-start rounded-lg border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-5 text-left transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)]'
>
{children}
</button>
@@ -307,15 +307,18 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
aria-label='Breadcrumb'
className='mb-10 flex items-center gap-2 text-[#555] text-[13px]'
>
<Link href='/' className='transition-colors hover:text-[#999]'>
<Link href='/' className='transition-colors hover:text-[var(--landing-text-muted)]'>
Home
</Link>
<span aria-hidden='true'>/</span>
<Link href='/integrations' className='transition-colors hover:text-[#999]'>
<Link
href='/integrations'
className='transition-colors hover:text-[var(--landing-text-muted)]'
>
Integrations
</Link>
<span aria-hidden='true'>/</span>
<span className='text-[#999]'>{name}</span>
<span className='text-[var(--landing-text-muted)]'>{name}</span>
</nav>
{/* Hero */}
@@ -334,20 +337,22 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
<p className='mb-0.5 text-[#555] text-[12px]'>Integration</p>
<h1
id='integration-heading'
className='font-[500] text-[#ECECEC] text-[36px] leading-tight sm:text-[44px]'
className='font-[500] text-[36px] text-[var(--landing-text)] leading-tight sm:text-[44px]'
>
{name}
</h1>
</div>
</div>
<p className='mb-8 max-w-[700px] text-[#999] text-[17px] leading-[1.7]'>{description}</p>
<p className='mb-8 max-w-[700px] text-[17px] text-[var(--landing-text-muted)] leading-[1.7]'>
{description}
</p>
{/* CTAs */}
<div className='flex flex-wrap gap-[8px]'>
<div className='flex flex-wrap gap-2'>
<a
href='https://sim.ai'
className='inline-flex h-[32px] items-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[#1C1C1C] text-[14px] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='inline-flex h-[32px] items-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Start building free
</a>
@@ -355,7 +360,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
href={docsUrl}
target='_blank'
rel='noopener noreferrer'
className='inline-flex h-[32px] items-center gap-[6px] rounded-[5px] border border-[#3d3d3d] px-[10px] font-[430] font-season text-[#ECECEC] text-[14px] transition-colors hover:bg-[#2A2A2A]'
className='inline-flex h-[32px] items-center gap-1.5 rounded-[5px] border border-[var(--landing-border-strong)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
View docs
<svg
@@ -381,16 +386,24 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Overview */}
{longDescription && (
<section aria-labelledby='overview-heading'>
<h2 id='overview-heading' className='mb-4 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='overview-heading'
className='mb-4 font-[500] text-[20px] text-[var(--landing-text)]'
>
Overview
</h2>
<p className='text-[#999] text-[15px] leading-[1.8]'>{longDescription}</p>
<p className='text-[15px] text-[var(--landing-text-muted)] leading-[1.8]'>
{longDescription}
</p>
</section>
)}
{/* How to automate — targets "how to connect X" queries */}
<section aria-labelledby='how-it-works-heading'>
<h2 id='how-it-works-heading' className='mb-6 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='how-it-works-heading'
className='mb-6 font-[500] text-[20px] text-[var(--landing-text)]'
>
How to automate {name} with Sim
</h2>
<ol className='space-y-4' aria-label='Steps to set up automation'>
@@ -418,14 +431,18 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
].map(({ step, title, body }) => (
<li key={step} className='flex gap-4'>
<span
className='mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-[#3d3d3d] font-[500] text-[#555] text-[11px]'
className='mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-[var(--landing-border-strong)] font-[500] text-[#555] text-[11px]'
aria-hidden='true'
>
{step}
</span>
<div>
<h3 className='mb-1 font-[500] text-[#ECECEC] text-[15px]'>{title}</h3>
<p className='text-[#999] text-[14px] leading-relaxed'>{body}</p>
<h3 className='mb-1 font-[500] text-[15px] text-[var(--landing-text)]'>
{title}
</h3>
<p className='text-[14px] text-[var(--landing-text-muted)] leading-relaxed'>
{body}
</p>
</div>
</li>
))}
@@ -435,10 +452,13 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Triggers */}
{triggers.length > 0 && (
<section aria-labelledby='triggers-heading'>
<h2 id='triggers-heading' className='mb-2 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='triggers-heading'
className='mb-2 font-[500] text-[20px] text-[var(--landing-text)]'
>
Real-time triggers
</h2>
<p className='mb-4 text-[#999] text-[14px] leading-relaxed'>
<p className='mb-4 text-[14px] text-[var(--landing-text-muted)] leading-relaxed'>
Connect a {name} webhook to Sim and your workflow fires the instant an event
happens — no polling, no delay. Sim receives the full event payload and makes
every field available as a variable inside your workflow.
@@ -452,10 +472,10 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{triggers.map((trigger) => (
<li
key={trigger.id}
className='rounded-lg border border-[#2A2A2A] bg-[#242424] p-4'
className='rounded-lg border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-4'
>
<div className='mb-2 flex items-center gap-2'>
<span className='inline-flex items-center gap-1 rounded-[4px] bg-[#2A2A2A] px-1.5 py-0.5 font-[500] text-[#ECECEC] text-[11px]'>
<span className='inline-flex items-center gap-1 rounded-[4px] bg-[var(--landing-bg-elevated)] px-1.5 py-0.5 font-[500] text-[11px] text-[var(--landing-text)]'>
<svg
aria-hidden='true'
className='h-2.5 w-2.5'
@@ -469,9 +489,11 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
Event
</span>
</div>
<p className='font-[500] text-[#ECECEC] text-[13px]'>{trigger.name}</p>
<p className='font-[500] text-[13px] text-[var(--landing-text)]'>
{trigger.name}
</p>
{trigger.description && (
<p className='mt-1 text-[#999] text-[12px] leading-relaxed'>
<p className='mt-1 text-[12px] text-[var(--landing-text-muted)] leading-relaxed'>
{trigger.description}
</p>
)}
@@ -484,10 +506,13 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Workflow templates */}
{matchingTemplates.length > 0 && (
<section aria-labelledby='templates-heading'>
<h2 id='templates-heading' className='mb-2 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='templates-heading'
className='mb-2 font-[500] text-[20px] text-[var(--landing-text)]'
>
Workflow templates
</h2>
<p className='mb-6 text-[#999] text-[14px]'>
<p className='mb-6 text-[14px] text-[var(--landing-text-muted)]'>
Ready-to-use workflows featuring {name}. Click any to build it instantly.
</p>
<ul
@@ -522,7 +547,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
→
</span>
)}
<span className='inline-flex items-center gap-1 rounded-[3px] bg-[#2A2A2A] px-1.5 py-0.5 font-[500] text-[#ECECEC]'>
<span className='inline-flex items-center gap-1 rounded-[3px] bg-[var(--landing-bg-elevated)] px-1.5 py-0.5 font-[500] text-[var(--landing-text)]'>
<IntegrationIcon
bgColor={int?.bgColor ?? '#6B7280'}
name={intName}
@@ -539,11 +564,11 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
})}
</div>
<p className='mb-1 font-[500] text-[#ECECEC] text-[14px]'>
<p className='mb-1 font-[500] text-[14px] text-[var(--landing-text)]'>
{template.title}
</p>
<p className='mt-3 text-[#555] text-[13px] transition-colors group-hover:text-[#999]'>
<p className='mt-3 text-[#555] text-[13px] transition-colors group-hover:text-[var(--landing-text-muted)]'>
Try this workflow →
</p>
</TemplateCardButton>
@@ -557,10 +582,13 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Tools */}
{operations.length > 0 && (
<section aria-labelledby='tools-heading'>
<h2 id='tools-heading' className='mb-2 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='tools-heading'
className='mb-2 font-[500] text-[20px] text-[var(--landing-text)]'
>
Supported tools
</h2>
<p className='mb-6 text-[#999] text-[14px]'>
<p className='mb-6 text-[14px] text-[var(--landing-text-muted)]'>
{operations.length} {name} tool{operations.length === 1 ? '' : 's'} available in
Sim
</p>
@@ -571,9 +599,9 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{operations.map((op) => (
<li
key={op.name}
className='rounded-[6px] border border-[#2A2A2A] bg-[#242424] px-3.5 py-3'
className='rounded-[6px] border border-[var(--landing-border)] bg-[var(--landing-bg-card)] px-3.5 py-3'
>
<p className='font-[500] text-[#ECECEC] text-[13px]'>{op.name}</p>
<p className='font-[500] text-[13px] text-[var(--landing-text)]'>{op.name}</p>
{op.description && (
<p className='mt-0.5 text-[#555] text-[12px] leading-relaxed'>
{op.description}
@@ -587,7 +615,10 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* FAQ */}
<section aria-labelledby='faq-heading'>
<h2 id='faq-heading' className='mb-8 font-[500] text-[#ECECEC] text-[20px]'>
<h2
id='faq-heading'
className='mb-8 font-[500] text-[20px] text-[var(--landing-text)]'
>
Frequently asked questions
</h2>
<IntegrationFAQ faqs={faqs} />
@@ -597,24 +628,24 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Sidebar */}
<aside className='space-y-5' aria-label='Integration details'>
{/* Quick details */}
<div className='rounded-lg border border-[#2A2A2A] bg-[#242424] p-5'>
<h3 className='mb-4 font-[500] text-[#ECECEC] text-[14px]'>Details</h3>
<div className='rounded-lg border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-5'>
<h3 className='mb-4 font-[500] text-[14px] text-[var(--landing-text)]'>Details</h3>
<dl className='space-y-3 text-[13px]'>
{operations.length > 0 && (
<div>
<dt className='text-[#555]'>Tools</dt>
<dd className='text-[#ECECEC]'>{operations.length} supported</dd>
<dd className='text-[var(--landing-text)]'>{operations.length} supported</dd>
</div>
)}
{triggers.length > 0 && (
<div>
<dt className='text-[#555]'>Triggers</dt>
<dd className='text-[#ECECEC]'>{triggers.length} available</dd>
<dd className='text-[var(--landing-text)]'>{triggers.length} available</dd>
</div>
)}
<div>
<dt className='text-[#555]'>Auth</dt>
<dd className='text-[#ECECEC]'>
<dd className='text-[var(--landing-text)]'>
{authType === 'oauth'
? 'One-click OAuth'
: authType === 'api-key'
@@ -624,13 +655,13 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
</div>
<div>
<dt className='text-[#555]'>Pricing</dt>
<dd className='text-[#ECECEC]'>Free to start</dd>
<dd className='text-[var(--landing-text)]'>Free to start</dd>
</div>
</dl>
<div className='mt-5 flex flex-col gap-2'>
<a
href='https://sim.ai'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] font-[430] font-season text-[#1C1C1C] text-[13px] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] font-[430] font-season text-[13px] text-[var(--landing-text-dark)] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Get started free
</a>
@@ -638,7 +669,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
href={docsUrl}
target='_blank'
rel='noopener noreferrer'
className='flex h-[32px] w-full items-center justify-center gap-1.5 rounded-[5px] border border-[#3d3d3d] font-[430] font-season text-[#ECECEC] text-[13px] transition-colors hover:bg-[#2A2A2A]'
className='flex h-[32px] w-full items-center justify-center gap-1.5 rounded-[5px] border border-[var(--landing-border-strong)] font-[430] font-season text-[13px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
View docs
<svg
@@ -658,10 +689,10 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
</div>
{/* Related integrations — internal linking for SEO */}
<div className='rounded-lg border border-[#2A2A2A] bg-[#242424] p-5'>
<div className='rounded-lg border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-5'>
{relatedIntegrations.length > 0 && (
<>
<h3 className='mb-4 font-[500] text-[#ECECEC] text-[14px]'>
<h3 className='mb-4 font-[500] text-[14px] text-[var(--landing-text)]'>
Related integrations
</h3>
<ul className='space-y-2'>
@@ -669,7 +700,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
<li key={rel.slug}>
<Link
href={`/integrations/${rel.slug}`}
className='flex items-center gap-2.5 rounded-[6px] p-1.5 text-[#999] text-[13px] transition-colors hover:bg-[#2A2A2A] hover:text-[#ECECEC]'
className='flex items-center gap-2.5 rounded-[6px] p-1.5 text-[13px] text-[var(--landing-text-muted)] transition-colors hover:bg-[var(--landing-bg-elevated)] hover:text-[var(--landing-text)]'
>
<IntegrationIcon
bgColor={rel.bgColor}
@@ -690,7 +721,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
)}
<Link
href='/integrations'
className={`block text-[#555] text-[12px] transition-colors hover:text-[#999]${relatedIntegrations.length > 0 ? ' mt-4' : ''}`}
className={`block text-[#555] text-[12px] transition-colors hover:text-[var(--landing-text-muted)]${relatedIntegrations.length > 0 ? ' mt-4' : ''}`}
>
All integrations →
</Link>
@@ -701,7 +732,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
{/* Bottom CTA */}
<section
aria-labelledby='cta-heading'
className='mt-20 rounded-xl border border-[#2A2A2A] bg-[#242424] p-8 text-center sm:p-12'
className='mt-20 rounded-xl border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-8 text-center sm:p-12'
>
{/* Logo pair: Sim × Integration */}
<div className='mx-auto mb-6 flex items-center justify-center gap-3'>
@@ -716,11 +747,11 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
<div className='flex items-center gap-2'>
<span className='h-px w-5 bg-[#3d3d3d]' aria-hidden='true' />
<span
className='flex h-7 w-7 items-center justify-center rounded-full border border-[#3d3d3d]'
className='flex h-7 w-7 items-center justify-center rounded-full border border-[var(--landing-border-strong)]'
aria-hidden='true'
>
<svg
className='h-3.5 w-3.5 text-[#666]'
className='h-3.5 w-3.5 text-[var(--landing-text-secondary)]'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
@@ -745,17 +776,17 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
</div>
<h2
id='cta-heading'
className='mb-3 font-[500] text-[#ECECEC] text-[28px] sm:text-[34px]'
className='mb-3 font-[500] text-[28px] text-[var(--landing-text)] sm:text-[34px]'
>
Start automating {name} today
</h2>
<p className='mx-auto mb-8 max-w-[480px] text-[#999] text-[16px] leading-relaxed'>
<p className='mx-auto mb-8 max-w-[480px] text-[16px] text-[var(--landing-text-muted)] leading-relaxed'>
Build your first AI workflow with {name} in minutes. Connect to every tool your team
uses. Free to start — no credit card required.
</p>
<a
href='https://sim.ai'
className='inline-flex h-[32px] items-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[#1C1C1C] text-[14px] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='inline-flex h-[32px] items-center rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text-dark)] transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Build for free →
</a>
@@ -15,7 +15,7 @@ export function IntegrationCard({ integration, IconComponent }: IntegrationCardP
return (
<Link
href={`/integrations/${slug}`}
className='group flex flex-col rounded-lg border border-[#2A2A2A] bg-[#242424] p-4 transition-colors hover:border-[#3d3d3d] hover:bg-[#2A2A2A]'
className='group flex flex-col rounded-lg border border-[var(--landing-border)] bg-[var(--landing-bg-card)] p-4 transition-colors hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)]'
aria-label={`${name} integration`}
>
<IntegrationIcon
@@ -27,26 +27,28 @@ export function IntegrationCard({ integration, IconComponent }: IntegrationCardP
/>
{/* Name */}
<h3 className='mb-1 font-[500] text-[#ECECEC] text-[14px] leading-snug'>{name}</h3>
<h3 className='mb-1 font-[500] text-[14px] text-[var(--landing-text)] leading-snug'>
{name}
</h3>
{/* Description — clamped to 2 lines */}
<p className='mb-3 line-clamp-2 flex-1 text-[#999] text-[12px] leading-relaxed'>
<p className='mb-3 line-clamp-2 flex-1 text-[12px] text-[var(--landing-text-muted)] leading-relaxed'>
{description}
</p>
{/* Footer row */}
<div className='flex flex-wrap items-center gap-1.5'>
{operationCount > 0 && (
<Badge className='border-0 bg-[#333] text-[#999] text-[11px]'>
<Badge className='border-0 bg-[#333] text-[11px] text-[var(--landing-text-muted)]'>
{operationCount} {operationCount === 1 ? 'tool' : 'tools'}
</Badge>
)}
{triggerCount > 0 && (
<Badge className='border-0 bg-[#333] text-[#999] text-[11px]'>
<Badge className='border-0 bg-[#333] text-[11px] text-[var(--landing-text-muted)]'>
{triggerCount} {triggerCount === 1 ? 'trigger' : 'triggers'}
</Badge>
)}
<span className='ml-auto text-[#555] text-[12px] transition-colors group-hover:text-[#999]'>
<span className='ml-auto text-[#555] text-[12px] transition-colors group-hover:text-[var(--landing-text-muted)]'>
Learn more →
</span>
</div>
@@ -105,8 +105,8 @@ export function IntegrationGrid({ integrations }: IntegrationGridProps) {
onClick={() => setActiveCategory(null)}
className={`rounded-md border px-3 py-1 text-[12px] transition-colors ${
activeCategory === null
? 'border-[#555] bg-[#333] text-[#ECECEC]'
: 'border-[#2A2A2A] bg-transparent text-[#999] hover:border-[#3d3d3d] hover:text-[#ECECEC]'
? 'border-[#555] bg-[#333] text-[var(--landing-text)]'
: 'border-[var(--landing-border)] bg-transparent text-[var(--landing-text-muted)] hover:border-[var(--landing-border-strong)] hover:text-[var(--landing-text)]'
}`}
>
All
@@ -118,8 +118,8 @@ export function IntegrationGrid({ integrations }: IntegrationGridProps) {
onClick={() => setActiveCategory(activeCategory === cat ? null : cat)}
className={`rounded-md border px-3 py-1 text-[12px] transition-colors ${
activeCategory === cat
? 'border-[#555] bg-[#333] text-[#ECECEC]'
: 'border-[#2A2A2A] bg-transparent text-[#999] hover:border-[#3d3d3d] hover:text-[#ECECEC]'
? 'border-[#555] bg-[#333] text-[var(--landing-text)]'
: 'border-[var(--landing-border)] bg-transparent text-[var(--landing-text-muted)] hover:border-[var(--landing-border-strong)] hover:text-[var(--landing-text)]'
}`}
>
{CATEGORY_LABELS[cat] || cat}
@@ -74,7 +74,7 @@ export function RequestIntegrationModal() {
<button
type='button'
onClick={() => setOpen(true)}
className='inline-flex h-[32px] shrink-0 items-center gap-[6px] rounded-[5px] border border-[#3d3d3d] px-[10px] font-[430] font-season text-[#ECECEC] text-[14px] transition-colors hover:bg-[#2A2A2A]'
className='inline-flex h-[32px] shrink-0 items-center gap-1.5 rounded-[5px] border border-[var(--landing-border-strong)] px-2.5 font-[430] font-season text-[14px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
Request an integration
</button>
@@ -88,7 +88,7 @@ export function RequestIntegrationModal() {
<div className='flex flex-col items-center gap-3 py-6 text-center'>
<div className='flex h-10 w-10 items-center justify-center rounded-full bg-[#33C482]/10'>
<svg
className='h-5 w-5 text-[#33C482]'
className='h-5 w-5 text-[var(--brand-accent)]'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
@@ -99,7 +99,7 @@ export function RequestIntegrationModal() {
<polyline points='20 6 9 17 4 12' />
</svg>
</div>
<p className='text-[14px] text-[var(--text-primary)]'>
<p className='text-[14px] text-[var(--landing-text)]'>
Request submitted — we&apos;ll follow up at{' '}
<span className='font-medium'>{email}</span>.
</p>
@@ -108,8 +108,8 @@ export function RequestIntegrationModal() {
) : (
<form onSubmit={handleSubmit} className='flex min-h-0 flex-1 flex-col'>
<ModalBody>
<div className='space-y-[12px]'>
<div className='flex flex-col gap-[4px]'>
<div className='space-y-3'>
<div className='flex flex-col gap-1'>
<Label htmlFor='integration-name'>Integration name</Label>
<Input
id='integration-name'
@@ -122,7 +122,7 @@ export function RequestIntegrationModal() {
/>
</div>
<div className='flex flex-col gap-[4px]'>
<div className='flex flex-col gap-1'>
<Label htmlFor='requester-email'>Your email</Label>
<Input
id='requester-email'
@@ -135,7 +135,7 @@ export function RequestIntegrationModal() {
/>
</div>
<div className='flex flex-col gap-[4px]'>
<div className='flex flex-col gap-1'>
<Label htmlFor='use-case'>
Use case <span className='text-[var(--text-tertiary)]'>(optional)</span>
</Label>
@@ -28,7 +28,7 @@ export default async function IntegrationsLayout({ children }: { children: React
}
return (
<div className='dark flex min-h-screen flex-col bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]'>
<div className='dark flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
+8 -5
View File
@@ -105,11 +105,11 @@ export default function IntegrationsPage() {
<section aria-labelledby='integrations-heading' className='mb-16'>
<h1
id='integrations-heading'
className='mb-4 font-[500] text-[#ECECEC] text-[40px] leading-tight sm:text-[56px]'
className='mb-4 text-balance font-[500] text-[40px] text-[var(--landing-text)] leading-tight sm:text-[56px]'
>
Integrations
</h1>
<p className='max-w-[640px] text-[#999] text-[18px] leading-relaxed'>
<p className='max-w-[640px] text-[18px] text-[var(--landing-text-muted)] leading-relaxed'>
Connect every tool your team uses. Build AI-powered workflows that automate tasks across{' '}
{TOP_NAMES.slice(0, 4).map((name, i, arr) => {
const integration = allIntegrations.find((int) => int.name === name)
@@ -136,16 +136,19 @@ export default function IntegrationsPage() {
{/* Searchable grid — client component */}
<section aria-labelledby='all-integrations-heading'>
<h2 id='all-integrations-heading' className='mb-8 font-[500] text-[#ECECEC] text-[24px]'>
<h2
id='all-integrations-heading'
className='mb-8 font-[500] text-[24px] text-[var(--landing-text)]'
>
All Integrations
</h2>
<IntegrationGrid integrations={allIntegrations} />
</section>
{/* Integration request */}
<div className='mt-16 flex flex-col items-start gap-3 border-[#2A2A2A] border-t pt-10 sm:flex-row sm:items-center sm:justify-between'>
<div className='mt-16 flex flex-col items-start gap-3 border-[var(--landing-border)] border-t pt-10 sm:flex-row sm:items-center sm:justify-between'>
<div>
<p className='font-[500] text-[#ECECEC] text-[15px]'>
<p className='font-[500] text-[15px] text-[var(--landing-text)]'>
Don&apos;t see the integration you need?
</p>
<p className='mt-0.5 text-[#555] text-[13px]'>
+1 -1
View File
@@ -2,7 +2,7 @@ import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://sim.ai'),
manifest: '/manifest.json',
manifest: '/manifest.webmanifest',
icons: {
icon: [{ url: '/icon.svg', type: 'image/svg+xml', sizes: 'any' }],
apple: '/favicon/apple-touch-icon.png',
+40 -40
View File
@@ -2,55 +2,55 @@ import { Skeleton } from '@/components/emcn'
export default function PrivacyLoading() {
return (
<main className='min-h-screen bg-[#1C1C1C] text-[#ECECEC]'>
<div className='flex h-[52px] items-center border-[#2A2A2A] border-b px-6'>
<Skeleton className='h-[22px] w-[60px] rounded-[4px] bg-[#2A2A2A]' />
<div className='ml-auto flex items-center gap-[12px]'>
<Skeleton className='h-[30px] w-[64px] rounded-[5px] bg-[#2A2A2A]' />
<Skeleton className='h-[30px] w-[80px] rounded-[5px] bg-[#2A2A2A]' />
<main className='min-h-screen bg-[var(--landing-bg)] text-[var(--landing-text)]'>
<div className='flex h-[52px] items-center border-[var(--landing-border)] border-b px-6'>
<Skeleton className='h-[22px] w-[60px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='ml-auto flex items-center gap-3'>
<Skeleton className='h-[30px] w-[64px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[30px] w-[80px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-[80px] sm:px-12'>
<Skeleton className='mx-auto h-[48px] w-[280px] rounded-[4px] bg-[#2A2A2A]' />
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-20 sm:px-12'>
<Skeleton className='mx-auto h-[48px] w-[280px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-12 space-y-8'>
<div className='space-y-[10px]'>
<Skeleton className='h-[15px] w-[180px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[#2A2A2A]' />
<div className='space-y-2.5'>
<Skeleton className='h-[15px] w-[180px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[320px] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-4 space-y-[10px]'>
<Skeleton className='h-[20px] w-[160px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[92%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[85%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[320px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-4 space-y-2.5'>
<Skeleton className='h-[20px] w-[160px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[92%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[260px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[90%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-4 space-y-[8px] pl-6'>
<Skeleton className='h-[15px] w-[70%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[60%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[75%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[65%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[260px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[90%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-4 space-y-2 pl-6'>
<Skeleton className='h-[15px] w-[70%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[60%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[75%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[65%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[300px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[300px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[220px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[93%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[80%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[220px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[93%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[80%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
+22 -5
View File
@@ -1,7 +1,15 @@
import type { Metadata } from 'next'
import Link from 'next/link'
import { getEnv } from '@/lib/core/config/env'
import { ExternalRedirect, LegalLayout } from '@/app/(landing)/components'
export const metadata: Metadata = {
title: 'Privacy Policy',
description:
'Learn how Sim collects, uses, and protects your personal data, including data obtained from Google APIs.',
alternates: { canonical: '/privacy' },
}
export default function PrivacyPolicy() {
return (
<LegalLayout title='Privacy Policy'>
@@ -574,7 +582,7 @@ export default function PrivacyPolicy() {
Please note that we may ask you to verify your identity before responding to such
requests.
</p>
<p className='mb-4 border-[#3d3d3d] border-l-4 bg-[#2A2A2A] p-3 text-[#ECECEC]'>
<p className='mb-4 border-[var(--landing-border-strong)] border-l-4 bg-[var(--landing-bg-elevated)] p-3 text-[var(--landing-text)]'>
You have the right to complain to a Data Protection Authority about our collection and use
of your Personal Information. For more information, please contact your local data
protection authority in the European Economic Area (EEA).
@@ -596,7 +604,10 @@ export default function PrivacyPolicy() {
sharing practices (such as analytics or advertising services) may be considered a "sale"
or "share" under CCPA/CPRA. You have the right to opt-out of such data sharing. To
exercise this right, contact us at{' '}
<Link href='mailto:privacy@sim.ai' className='text-[#ECECEC] underline hover:text-white'>
<Link
href='mailto:privacy@sim.ai'
className='text-[var(--landing-text)] underline hover:text-white'
>
privacy@sim.ai
</Link>
.
@@ -682,7 +693,10 @@ export default function PrivacyPolicy() {
Sim interacts with are not covered by this policy and should be reported directly to the
solution vendor in accordance with their disclosure policy (if any). Before beginning your
inquiry, email us at{' '}
<Link href='mailto:security@sim.ai' className='text-[#ECECEC] underline hover:text-white'>
<Link
href='mailto:security@sim.ai'
className='text-[var(--landing-text)] underline hover:text-white'
>
security@sim.ai
</Link>{' '}
if you're unsure whether a system or endpoint is in scope.
@@ -701,7 +715,10 @@ export default function PrivacyPolicy() {
<h3 className='mb-2 font-medium text-xl'>Reporting a vulnerability</h3>
<p className='mb-4'>
To report any security flaws, send an email to{' '}
<Link href='mailto:security@sim.ai' className='text-[#ECECEC] underline hover:text-white'>
<Link
href='mailto:security@sim.ai'
className='text-[var(--landing-text)] underline hover:text-white'
>
security@sim.ai
</Link>
. The next business day, we'll acknowledge receipt of your vulnerability report and keep
@@ -745,7 +762,7 @@ export default function PrivacyPolicy() {
Email:{' '}
<Link
href='mailto:privacy@sim.ai'
className='text-[#ECECEC] underline hover:text-white'
className='text-[var(--landing-text)] underline hover:text-white'
>
privacy@sim.ai
</Link>
+35 -35
View File
@@ -2,48 +2,48 @@ import { Skeleton } from '@/components/emcn'
export default function TermsLoading() {
return (
<main className='min-h-screen bg-[#1C1C1C] text-[#ECECEC]'>
<div className='flex h-[52px] items-center border-[#2A2A2A] border-b px-6'>
<Skeleton className='h-[22px] w-[60px] rounded-[4px] bg-[#2A2A2A]' />
<div className='ml-auto flex items-center gap-[12px]'>
<Skeleton className='h-[30px] w-[64px] rounded-[5px] bg-[#2A2A2A]' />
<Skeleton className='h-[30px] w-[80px] rounded-[5px] bg-[#2A2A2A]' />
<main className='min-h-screen bg-[var(--landing-bg)] text-[var(--landing-text)]'>
<div className='flex h-[52px] items-center border-[var(--landing-border)] border-b px-6'>
<Skeleton className='h-[22px] w-[60px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='ml-auto flex items-center gap-3'>
<Skeleton className='h-[30px] w-[64px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[30px] w-[80px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-[80px] sm:px-12'>
<Skeleton className='mx-auto h-[48px] w-[320px] rounded-[4px] bg-[#2A2A2A]' />
<div className='mx-auto max-w-[800px] px-6 pt-[60px] pb-20 sm:px-12'>
<Skeleton className='mx-auto h-[48px] w-[320px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='mt-12 space-y-8'>
<div className='space-y-[10px]'>
<Skeleton className='h-[15px] w-[180px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[#2A2A2A]' />
<div className='space-y-2.5'>
<Skeleton className='h-[15px] w-[180px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[140px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[92%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[85%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[140px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[92%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[260px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[90%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[75%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[260px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[90%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[75%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[380px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[380px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-12 space-y-[10px]'>
<Skeleton className='h-[28px] w-[300px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[93%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[15px] w-[80%] rounded-[4px] bg-[#2A2A2A]' />
<div className='mt-12 space-y-2.5'>
<Skeleton className='h-[28px] w-[300px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[93%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[15px] w-[80%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
+18 -4
View File
@@ -1,7 +1,15 @@
import type { Metadata } from 'next'
import Link from 'next/link'
import { getEnv } from '@/lib/core/config/env'
import { ExternalRedirect, LegalLayout } from '@/app/(landing)/components'
export const metadata: Metadata = {
title: 'Terms of Service',
description:
'Read the terms and conditions for using the Sim platform, including subscription plans, data ownership, and acceptable use.',
alternates: { canonical: '/terms' },
}
export default function TermsOfService() {
return (
<LegalLayout title='Terms of Service'>
@@ -281,7 +289,7 @@ export default function TermsOfService() {
Agreement. The arbitration will be conducted by JAMS, an established alternative dispute
resolution provider.
</p>
<p className='mb-4 border-[#3d3d3d] border-l-4 bg-[#2A2A2A] p-3 text-[#ECECEC]'>
<p className='mb-4 border-[var(--landing-border-strong)] border-l-4 bg-[var(--landing-bg-elevated)] p-3 text-[var(--landing-text)]'>
YOU AND COMPANY AGREE THAT EACH OF US MAY BRING CLAIMS AGAINST THE OTHER ONLY ON AN
INDIVIDUAL BASIS AND NOT ON A CLASS, REPRESENTATIVE, OR COLLECTIVE BASIS. ONLY INDIVIDUAL
RELIEF IS AVAILABLE, AND DISPUTES OF MORE THAN ONE CUSTOMER OR USER CANNOT BE ARBITRATED
@@ -290,7 +298,10 @@ export default function TermsOfService() {
<p className='mb-4'>
You have the right to opt out of the provisions of this Arbitration Agreement by sending a
timely written notice of your decision to opt out to:{' '}
<Link href='mailto:legal@sim.ai' className='text-[#ECECEC] underline hover:text-white'>
<Link
href='mailto:legal@sim.ai'
className='text-[var(--landing-text)] underline hover:text-white'
>
legal@sim.ai{' '}
</Link>
within 30 days after first becoming subject to this Arbitration Agreement.
@@ -339,7 +350,7 @@ export default function TermsOfService() {
Our Copyright Agent can be reached at:{' '}
<Link
href='mailto:copyright@sim.ai'
className='text-[#ECECEC] underline hover:text-white'
className='text-[var(--landing-text)] underline hover:text-white'
>
copyright@sim.ai
</Link>
@@ -350,7 +361,10 @@ export default function TermsOfService() {
<h2 className='mb-4 font-semibold text-2xl'>18. Contact Us</h2>
<p>
If you have any questions about these Terms, please contact us at:{' '}
<Link href='mailto:legal@sim.ai' className='text-[#ECECEC] underline hover:text-white'>
<Link
href='mailto:legal@sim.ai'
className='text-[var(--landing-text)] underline hover:text-white'
>
legal@sim.ai
</Link>
</p>
+183 -147
View File
@@ -15,6 +15,23 @@
--toolbar-triggers-height: 300px; /* TOOLBAR_TRIGGERS_HEIGHT.DEFAULT */
--editor-connections-height: 172px; /* EDITOR_CONNECTIONS_HEIGHT.DEFAULT */
--terminal-height: 206px; /* TERMINAL_HEIGHT.DEFAULT */
/* z-index scale for layered UI
Popover must be above modal so dropdowns inside modals render correctly */
--z-dropdown: 100;
--z-modal: 200;
--z-popover: 300;
--z-tooltip: 400;
--z-toast: 500;
/* Shadow scale */
--shadow-subtle: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
--shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.1);
--shadow-overlay: 0 16px 48px rgba(0, 0, 0, 0.15);
--shadow-kbd: 0 4px 0 0 rgba(48, 48, 48, 1);
--shadow-kbd-sm: 0 2px 0 0 rgba(48, 48, 48, 1);
--shadow-brand-inset: inset 0 1.25px 2.5px 0 #9b77ff;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.workspace-root {
@@ -194,10 +211,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--border-success: #e0e0e0;
/* Brand & state */
--brand-400: #8e4cfb;
--brand-secondary: #33b4ff;
--brand-tertiary: #22c55e;
--brand-tertiary-2: #33c482;
--brand-accent: #33c482;
--selection: #1a5cf6;
--warning: #ea580c;
@@ -209,67 +224,50 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--font-weight-medium: 440;
--font-weight-semibold: 500;
/* Extended palette */
--c-0D0D0D: #0d0d0d;
--c-1A1A1A: #1a1a1a;
--c-1F1F1F: #1f1f1f;
--c-2A2A2A: #2a2a2a;
--c-383838: #383838;
--c-414141: #414141;
--c-442929: #442929;
--c-491515: #491515;
--c-575757: #575757;
--c-686868: #686868;
--c-707070: #707070;
--c-727272: #727272;
--c-737373: #737373;
--c-808080: #808080;
--c-858585: #858585;
--c-868686: #868686;
--c-8D8D8D: #8d8d8d;
--c-939393: #939393;
--c-A8A8A8: #a8a8a8;
--c-B8B8B8: #b8b8b8;
--c-C0C0C0: #c0c0c0;
--c-CDCDCD: #cdcdcd;
--c-D0D0D0: #d0d0d0;
--c-D2D2D2: #d2d2d2;
--c-E0E0E0: #e0e0e0;
--c-E5E5E5: #e5e5e5;
--c-E8E8E8: #e8e8e8;
--c-EEEEEE: #eeeeee;
--c-F0F0F0: #f0f0f0;
--c-F4F4F4: #f4f4f4;
--c-F5F5F5: #f5f5f5;
/* Landing / marketing - text (static dark-section colors) */
--landing-text: #ececec;
--landing-text-muted: #999999;
--landing-text-subtle: #f6f6f6;
--landing-text-dark: #1c1c1c;
--landing-text-secondary: #666666;
--landing-text-body: #cdcdcd;
--landing-text-icon: #939393;
/* Blues and cyans */
--c-00B0B0: #00b0b0;
--c-264F78: #264f78;
--c-2F55FF: #2f55ff;
--c-2FA1FF: #2fa1ff;
--c-336699: #336699;
--c-34B5FF: #34b5ff;
--c-601EE0: #601ee0;
--c-611F69: #611f69;
--c-802FFF: #802fff;
--c-8357FF: #8357ff;
--c-8C10FF: #8c10ff;
/* Landing / marketing - surfaces */
--landing-bg: #1c1c1c;
--landing-bg-elevated: #2a2a2a;
--landing-bg-card: #232323;
--landing-bg-section: #f6f6f6;
--landing-bg-subtle: #fafafa;
--landing-bg-surface: #1e1e1e;
--landing-bg-hover: #f0f0f0;
--landing-bg-skeleton: #e8e8e8;
/* Greens */
--c-4CAF50: #22c55e;
/* Landing / marketing - borders */
--landing-border: #2a2a2a;
--landing-border-light: #e5e5e5;
--landing-border-strong: #3d3d3d;
--landing-border-subtle: #d4d4d4;
/* Oranges / Ambers */
--c-F59E0B: #f59e0b;
--c-F97316: #ea580c;
--c-FF972F: #f97316;
/* Code editor (theme-aware) */
--code-bg: #f5f5f5;
--code-foreground: #1a1a1a;
--code-line-number: #737373;
/* Reds */
--c-DC2626: #dc2626;
--c-F6D2D2: #fecaca;
--c-F87171: #f87171;
--c-FF402F: #ef4444;
--c-B91C1C: #b91c1c;
--c-883827: #7c2d12;
/* Workspace: additional text */
--text-placeholder: #8d8d8d;
--text-success: #22c55e;
/* Status / semantic colors */
--error: #dc2626;
--error-muted: #fecaca;
--error-emphasis: #b91c1c;
--caution: #f59e0b;
--success: #22c55e;
/* Brand extended */
--brand-knowledge: #00b0b0;
--selection-dark: #264f78;
/* Terminal status badges */
--terminal-status-error-bg: #fef2f2;
@@ -280,6 +278,49 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--terminal-status-warning-bg: #fefce8;
--terminal-status-warning-border: #facc15;
--terminal-status-warning-color: #a16207;
/* Badge semantic colors */
--badge-success-bg: #bbf7d0;
--badge-success-text: #15803d;
--badge-error-bg: #fecaca;
--badge-error-text: #dc2626;
--badge-gray-bg: #e7e5e4;
--badge-gray-text: #57534e;
--badge-blue-bg: #bfdbfe;
--badge-blue-text: #1d4ed8;
--badge-blue-secondary-bg: #bae6fd;
--badge-blue-secondary-text: #0369a1;
--badge-purple-bg: #e9d5ff;
--badge-purple-text: #7c3aed;
--badge-orange-bg: #fed7aa;
--badge-orange-text: #c2410c;
--badge-amber-bg: #fde68a;
--badge-amber-text: #a16207;
--badge-teal-bg: #99f6e4;
--badge-teal-text: #0f766e;
--badge-cyan-bg: #cffafe;
--badge-cyan-text: #0891b2;
--badge-pink-bg: #fbcfe8;
--badge-pink-text: #be185d;
/* Search/highlight */
--highlight-search-active: #f6ad55;
--highlight-search: #fcd34d;
--highlight-match-bg: #bae6fd;
--highlight-match-text: #0369a1;
/* Selection */
--selection-bg: #add6ff;
/* Active indicator */
--indicator-online: #33c482;
--indicator-active: #4ade80;
--indicator-inactive: #b7b7b7;
--indicator-seat-filled: #34b5ff;
/* Scrollbar colors */
--scrollbar-thumb-color: #c0c0c0;
--scrollbar-thumb-hover-color: #a8a8a8;
}
.dark {
/* Surface */
@@ -316,10 +357,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--border-success: #575757;
/* Brand & state */
--brand-400: #8e4cfb;
--brand-secondary: #33b4ff;
--brand-tertiary: #22c55e;
--brand-tertiary-2: #33c482;
--brand-accent: #33c482;
--selection: #4b83f7;
--warning: #ff6600;
@@ -331,70 +370,25 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--font-weight-medium: 480;
--font-weight-semibold: 550;
/* Extended palette (exhaustive from code usage via -[#...]) */
/* Neutral deep shades */
--c-0D0D0D: #0d0d0d;
--c-1A1A1A: #1a1a1a;
--c-1F1F1F: #1f1f1f;
--c-2A2A2A: #2a2a2a;
--c-383838: #383838;
--c-414141: #414141;
--c-442929: #442929;
--c-491515: #491515;
--c-575757: #575757;
--c-686868: #686868;
--c-707070: #707070;
--c-727272: #727272;
--c-737373: #737373;
--c-808080: #808080;
--c-858585: #858585;
--c-868686: #868686;
--c-8D8D8D: #8d8d8d;
--c-939393: #939393;
--c-A8A8A8: #a8a8a8;
--c-B8B8B8: #b8b8b8;
--c-C0C0C0: #c0c0c0;
--c-CDCDCD: #cdcdcd;
--c-D0D0D0: #d0d0d0;
--c-D2D2D2: #d2d2d2;
--c-E0E0E0: #e0e0e0;
--c-E5E5E5: #e5e5e5;
--c-E8E8E8: #e8e8e8;
--c-EEEEEE: #eeeeee;
--c-F0F0F0: #f0f0f0;
--c-F4F4F4: #f4f4f4;
--c-F5F5F5: #f5f5f5;
/* Code editor (dark mode overrides) */
--code-bg: #1f1f1f;
--code-foreground: #eeeeee;
--code-line-number: #a8a8a8;
--c-CFCFCF: #cfcfcf;
/* Workspace text */
--text-placeholder: #8d8d8d;
--text-success: #22c55e;
/* Blues and cyans */
--c-00B0B0: #00b0b0;
--c-264F78: #264f78;
--c-2F55FF: #2f55ff;
--c-2FA1FF: #2fa1ff;
--c-336699: #336699;
--c-34B5FF: #34b5ff;
--c-601EE0: #601ee0;
--c-611F69: #611f69;
--c-802FFF: #802fff;
--c-8357FF: #8357ff;
--c-8C10FF: #8c10ff;
/* Status / semantic (dark overrides where needed) */
--error: #f87171;
--error-muted: #f6d2d2;
--error-emphasis: #b91c1c;
--caution: #f59e0b;
--success: #22c55e;
/* Greens */
--c-4CAF50: #4caf50;
/* Oranges / Ambers */
--c-F59E0B: #f59e0b;
--c-F97316: #f97316;
--c-FF972F: #ff972f;
/* Reds */
--c-DC2626: #dc2626;
--c-F6D2D2: #f6d2d2;
--c-F87171: #f87171;
--c-FF402F: #ff402f;
--c-B91C1C: #b91c1c;
--c-883827: #883827;
/* Brand extended */
--brand-knowledge: #00b0b0;
--selection-dark: #264f78;
/* Terminal status badges */
--terminal-status-error-bg: #491515;
@@ -405,6 +399,52 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--terminal-status-warning-bg: #3d3520;
--terminal-status-warning-border: #5c4d1f;
--terminal-status-warning-color: #d4a72c;
/* Badge semantic colors */
--badge-success-bg: rgba(34, 197, 94, 0.2);
--badge-success-text: #86efac;
--badge-error-bg: #551a1a;
--badge-error-text: #fca5a5;
--badge-gray-bg: #3a3a3a;
--badge-gray-text: #a8a8a8;
--badge-blue-bg: rgba(59, 130, 246, 0.2);
--badge-blue-text: #93c5fd;
--badge-blue-secondary-bg: rgba(51, 180, 255, 0.2);
--badge-blue-secondary-text: #7dd3fc;
--badge-purple-bg: rgba(168, 85, 247, 0.2);
--badge-purple-text: #d8b4fe;
--badge-orange-bg: rgba(249, 115, 22, 0.2);
--badge-orange-text: #fdba74;
--badge-amber-bg: rgba(245, 158, 11, 0.2);
--badge-amber-text: #fcd34d;
--badge-teal-bg: rgba(20, 184, 166, 0.2);
--badge-teal-text: #5eead4;
--badge-cyan-bg: rgba(14, 165, 233, 0.2);
--badge-cyan-text: #7dd3fc;
--badge-pink-bg: rgba(236, 72, 153, 0.2);
--badge-pink-text: #f9a8d4;
/* Search/highlight */
--highlight-search-active: #f6ad55;
--highlight-search: #fcd34d;
--highlight-match-bg: rgba(51, 180, 255, 0.2);
--highlight-match-text: #7dd3fc;
/* Selection */
--selection-bg: #264f78;
/* Active indicator */
--indicator-online: #33c482;
--indicator-active: #4ade80;
--indicator-inactive: #b7b7b7;
--indicator-seat-filled: #34b5ff;
/* Scrollbar colors */
--scrollbar-thumb-color: #5a5a5a;
--scrollbar-thumb-hover-color: #6a6a6a;
/* Shadow scale - dark */
--shadow-overlay: 0 16px 48px rgba(0, 0, 0, 0.4);
}
}
@@ -445,7 +485,7 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
caret-color: var(--text-primary);
}
.dark body {
body {
@apply antialiased;
}
::-webkit-scrollbar {
@@ -458,12 +498,12 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
}
::-webkit-scrollbar-thumb {
background-color: #c0c0c0;
background-color: var(--scrollbar-thumb-color);
border-radius: var(--radius);
}
::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
background-color: var(--scrollbar-thumb-hover-color);
}
/* Dark Mode Global Scrollbar */
@@ -472,20 +512,20 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
}
.dark ::-webkit-scrollbar-thumb {
background-color: #5a5a5a;
background-color: var(--scrollbar-thumb-color);
}
.dark ::-webkit-scrollbar-thumb:hover {
background-color: #6a6a6a;
background-color: var(--scrollbar-thumb-hover-color);
}
* {
scrollbar-width: thin;
scrollbar-color: #c0c0c0 transparent;
scrollbar-color: var(--scrollbar-thumb-color) transparent;
}
.dark * {
scrollbar-color: #5a5a5a transparent;
scrollbar-color: var(--scrollbar-thumb-color) transparent;
}
.copilot-scrollable {
@@ -624,13 +664,13 @@ input[type="search"]::-ms-clear {
}
.branded-button-custom {
background: var(--brand-primary-hex) !important;
border-color: var(--brand-primary-hex) !important;
background: var(--brand) !important;
border-color: var(--brand) !important;
}
.branded-button-custom:hover {
background: var(--brand-primary-hover-hex) !important;
border-color: var(--brand-primary-hover-hex) !important;
background: var(--brand-hover) !important;
border-color: var(--brand-hover) !important;
}
/**
@@ -722,12 +762,10 @@ input[type="search"]::-ms-clear {
--base-muted-foreground: #737373;
--gradient-primary: 263 85% 70%;
--gradient-secondary: 336 95% 65%;
--brand-primary-hex: #6f3dfa;
--brand-primary-hover-hex: #6338d9;
--brand-accent-hex: #6f3dfa;
--brand-accent-hover-hex: #6f3dfa;
--brand-background-hex: #ffffff;
--surface-elevated: #202020;
--brand: #6f3dfa;
--brand-hover: #6338d9;
--brand-link: #6f3dfa;
--brand-link-hover: #6f3dfa;
}
.dark {
@@ -761,12 +799,10 @@ input[type="search"]::-ms-clear {
--base-muted-foreground: #a3a3a3;
--gradient-primary: 263 90% 75%;
--gradient-secondary: 336 100% 72%;
--brand-primary-hex: #701ffc;
--brand-primary-hover-hex: #802fff;
--brand-accent-hex: #9d54ff;
--brand-accent-hover-hex: #a66fff;
--brand-background-hex: #0c0c0c;
--surface-elevated: #202020;
--brand: #701ffc;
--brand-hover: #802fff;
--brand-link: #9d54ff;
--brand-link-hover: #a66fff;
}
}
+1 -1
View File
@@ -165,7 +165,7 @@ export async function POST(request: NextRequest) {
// Merge customizations with the additional fields
const mergedCustomizations = {
...(customizations || {}),
primaryColor: customizations?.primaryColor || 'var(--brand-primary-hover-hex)',
primaryColor: customizations?.primaryColor || 'var(--brand-hover)',
welcomeMessage: customizations?.welcomeMessage || 'Hi there! How can I help you today?',
}
@@ -47,35 +47,37 @@ export default async function ChangelogContent() {
<div className='min-h-screen'>
<div className='relative grid md:grid-cols-2'>
{/* Left intro panel */}
<div className='relative top-0 overflow-hidden border-[#2A2A2A] border-b px-6 py-16 sm:px-10 md:sticky md:h-dvh md:border-r md:border-b-0 md:px-12 md:py-24'>
<div className='relative top-0 overflow-hidden border-[var(--landing-bg-elevated)] border-b px-6 py-16 sm:px-10 md:sticky md:h-dvh md:border-r md:border-b-0 md:px-12 md:py-24'>
<div className='relative mx-auto h-full max-w-xl md:flex md:flex-col md:justify-center'>
<h1 className='mt-6 font-[500] text-4xl tracking-tight sm:text-5xl'>Changelog</h1>
<p className='mt-4 text-[#999] text-sm'>
<h1 className='mt-6 text-balance font-[500] text-4xl tracking-tight sm:text-5xl'>
Changelog
</h1>
<p className='mt-4 text-[var(--landing-text-muted)] text-sm'>
Stay up-to-date with the latest features, improvements, and bug fixes in Sim. All
changes are documented here with detailed release notes.
</p>
<hr className='mt-6 border-[#2A2A2A]' />
<hr className='mt-6 border-[var(--landing-bg-elevated)]' />
<div className='mt-6 flex flex-wrap items-center gap-3 text-sm'>
<Link
href='https://github.com/simstudioai/sim/releases'
target='_blank'
rel='noopener noreferrer'
className='inline-flex items-center gap-2 rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] py-[5px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
className='inline-flex items-center gap-2 rounded-[5px] border border-[var(--white)] bg-[var(--white)] px-[9px] py-[5px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
<Github className='h-4 w-4' />
View on GitHub
</Link>
<Link
href='https://docs.sim.ai'
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
className='inline-flex items-center gap-2 rounded-[5px] border border-[var(--landing-border-strong)] px-[9px] py-[5px] text-[13.5px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
<BookOpen className='h-4 w-4' />
Documentation
</Link>
<Link
href='/changelog.xml'
className='inline-flex items-center gap-2 rounded-[5px] border border-[#3d3d3d] px-[9px] py-[5px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
className='inline-flex items-center gap-2 rounded-[5px] border border-[var(--landing-border-strong)] px-[9px] py-[5px] text-[13.5px] text-[var(--landing-text)] transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
<Rss className='h-4 w-4' />
RSS Feed
@@ -98,7 +98,7 @@ export default function ChangelogList({ initialEntries }: Props) {
<div key={entry.tag}>
<div className='flex items-center justify-between gap-4'>
<div className='flex items-center gap-2'>
<div className='font-[500] text-[#ECECEC] text-[18px] tracking-tight'>
<div className='font-[500] text-[var(--landing-text)] text-lg tracking-tight'>
{entry.tag}
</div>
{entry.contributors && entry.contributors.length > 0 && (
@@ -113,7 +113,7 @@ export default function ChangelogList({ initialEntries }: Props) {
title={`@${contributor}`}
className='block'
>
<Avatar className='size-6 ring-2 ring-[#1C1C1C]'>
<Avatar className='size-6 ring-2 ring-[var(--landing-bg)]'>
<AvatarImage
src={`https://avatars.githubusercontent.com/${contributor}`}
alt={`@${contributor}`}
@@ -124,14 +124,14 @@ export default function ChangelogList({ initialEntries }: Props) {
</a>
))}
{entry.contributors.length > 5 && (
<div className='relative flex size-6 items-center justify-center rounded-full bg-[#2A2A2A] text-[#ECECEC] text-[10px] ring-2 ring-[#1C1C1C] hover:z-10'>
<div className='relative flex size-6 items-center justify-center rounded-full bg-[var(--landing-bg-elevated)] text-[var(--landing-text)] text-micro ring-2 ring-[var(--landing-bg)] hover:z-10'>
+{entry.contributors.length - 5}
</div>
)}
</div>
)}
</div>
<div className='text-[#999] text-xs'>
<div className='text-[var(--landing-text-muted)] text-xs'>
{new Date(entry.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
@@ -146,7 +146,7 @@ export default function ChangelogList({ initialEntries }: Props) {
h2: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<h3
className='mt-5 mb-2 font-[500] text-[#ECECEC] text-[13px] tracking-tight'
className='mt-5 mb-2 font-[500] text-[var(--landing-text)] text-small tracking-tight'
{...props}
>
{children}
@@ -155,7 +155,7 @@ export default function ChangelogList({ initialEntries }: Props) {
h3: ({ children, ...props }) =>
isContributorsLabel(children) ? null : (
<h4
className='mt-4 mb-1 font-[500] text-[#ECECEC] text-[13px] tracking-tight'
className='mt-4 mb-1 font-[500] text-[var(--landing-text)] text-small tracking-tight'
{...props}
>
{children}
@@ -170,25 +170,31 @@ export default function ChangelogList({ initialEntries }: Props) {
const text = String(children)
if (/^\s*contributors\s*:?\s*$/i.test(text)) return null
return (
<li className='text-[#999] text-[13px] leading-relaxed' {...props}>
<li
className='text-[var(--landing-text-muted)] text-small leading-relaxed'
{...props}
>
{children}
</li>
)
},
p: ({ children, ...props }) =>
/^\s*contributors\s*:?\s*$/i.test(String(children)) ? null : (
<p className='mb-3 text-[#999] text-[13px] leading-relaxed' {...props}>
<p
className='mb-3 text-[var(--landing-text-muted)] text-small leading-relaxed'
{...props}
>
{children}
</p>
),
strong: ({ children, ...props }) => (
<strong className='font-[500] text-[#ECECEC]' {...props}>
<strong className='font-[500] text-[var(--landing-text)]' {...props}>
{children}
</strong>
),
code: ({ children, ...props }) => (
<code
className='rounded bg-[#2A2A2A] px-1 py-0.5 font-mono text-[#ECECEC] text-xs'
className='rounded bg-[var(--landing-bg-elevated)] px-1 py-0.5 font-mono text-[var(--landing-text)] text-xs'
{...props}
>
{children}
@@ -217,7 +223,7 @@ export default function ChangelogList({ initialEntries }: Props) {
type='button'
onClick={loadMore}
disabled={loading}
className='rounded-[5px] border border-[#3d3d3d] px-3 py-1.5 text-[#ECECEC] text-[13px] transition-colors hover:bg-[#2A2A2A] disabled:opacity-60'
className='rounded-[5px] border border-[var(--landing-border-strong)] px-3 py-1.5 text-[var(--landing-text)] text-small transition-colors hover:bg-[var(--landing-bg-elevated)] disabled:opacity-60'
>
{loading ? 'Loading…' : 'Show more'}
</button>
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function ChangelogLayout({ children }: { children: React.Re
const blogPosts = await getNavBlogPosts()
return (
<div
className={`${martianMono.variable} relative min-h-screen bg-[#1C1C1C] font-[430] font-season text-[#ECECEC]`}
className={`${martianMono.variable} relative min-h-screen bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]`}
>
<header>
<Navbar blogPosts={blogPosts} />
+16 -16
View File
@@ -4,29 +4,29 @@ export default function ChangelogLoading() {
return (
<div className='min-h-screen'>
<div className='relative grid md:grid-cols-2'>
<div className='relative top-0 overflow-hidden border-[#2A2A2A] border-b px-6 py-16 sm:px-10 md:sticky md:h-dvh md:border-r md:border-b-0 md:px-12 md:py-24'>
<div className='relative top-0 overflow-hidden border-[var(--landing-border)] border-b px-6 py-16 sm:px-10 md:sticky md:h-dvh md:border-r md:border-b-0 md:px-12 md:py-24'>
<div className='relative mx-auto h-full max-w-xl md:flex md:flex-col md:justify-center'>
<Skeleton className='mt-6 h-[48px] w-[200px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-4 h-[14px] w-[300px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-[4px] h-[14px] w-[260px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='mt-6 h-[1px] w-full bg-[#2A2A2A]' />
<Skeleton className='mt-6 h-[48px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-4 h-[14px] w-[300px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-1 h-[14px] w-[260px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-6 h-[1px] w-full bg-[var(--landing-bg-elevated)]' />
<div className='mt-6 flex flex-wrap items-center gap-3'>
<Skeleton className='h-[32px] w-[130px] rounded-[5px] bg-[#2A2A2A]' />
<Skeleton className='h-[32px] w-[120px] rounded-[5px] bg-[#2A2A2A]' />
<Skeleton className='h-[32px] w-[80px] rounded-[5px] bg-[#2A2A2A]' />
<Skeleton className='h-[32px] w-[130px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[32px] w-[120px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[32px] w-[80px] rounded-[5px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
<div className='px-6 py-16 sm:px-10 md:px-12 md:py-24'>
<div className='max-w-2xl space-y-[32px]'>
<div className='max-w-2xl space-y-8'>
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className='space-y-[12px]'>
<Skeleton className='h-[20px] w-[160px] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[14px] w-[100px] rounded-[4px] bg-[#2A2A2A]' />
<div className='space-y-[8px]'>
<Skeleton className='h-[14px] w-full rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[14px] w-[90%] rounded-[4px] bg-[#2A2A2A]' />
<Skeleton className='h-[14px] w-[75%] rounded-[4px] bg-[#2A2A2A]' />
<div key={i} className='space-y-3'>
<Skeleton className='h-[20px] w-[160px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[14px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='space-y-2'>
<Skeleton className='h-[14px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[14px] w-[90%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[14px] w-[75%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
))}
+1 -1
View File
@@ -466,7 +466,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
if (authRequired) {
// const title = new URLSearchParams(window.location.search).get('title') || 'chat'
// const primaryColor =
// new URLSearchParams(window.location.search).get('color') || 'var(--brand-primary-hover-hex)'
// new URLSearchParams(window.location.search).get('color') || 'var(--brand-hover)'
if (authRequired === 'password') {
return <PasswordAuth identifier={identifier} onAuthSuccess={handleAuthSuccess} />
@@ -182,18 +182,18 @@ export default function EmailAuth({ identifier, onAuthSuccess }: EmailAuthProps)
return (
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-screen flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<main className='relative flex min-h-screen flex-col text-[var(--landing-text)]'>
<header className='shrink-0 bg-[var(--landing-bg)]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
<div className='w-full max-w-lg px-4'>
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[500] text-[#ECECEC] text-[32px] tracking-tight'>
<h1 className='font-[500] text-[32px] text-[var(--landing-text)] tracking-tight'>
{showOtpVerification ? 'Verify Your Email' : 'Email Verification'}
</h1>
<p className='font-[380] text-[#999] text-[16px]'>
<p className='font-[380] text-[var(--text-subtle)] text-md'>
{showOtpVerification
? `A verification code has been sent to ${email}`
: 'This chat requires email verification'}
@@ -246,7 +246,7 @@ export default function EmailAuth({ identifier, onAuthSuccess }: EmailAuthProps)
</form>
) : (
<div className='space-y-6'>
<p className='text-center text-[#999] text-sm'>
<p className='text-center text-[var(--text-subtle)] text-sm'>
Enter the 6-digit code to verify your account. If you don't see it in your
inbox, check your spam folder.
</p>
@@ -301,7 +301,7 @@ export default function EmailAuth({ identifier, onAuthSuccess }: EmailAuthProps)
</span>
) : (
<button
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
className='font-medium text-[var(--brand-link)] underline-offset-4 transition hover:text-[var(--brand-link-hover)] hover:underline'
onClick={handleResendOtp}
disabled={isVerifyingOtp || isResendDisabled}
>
@@ -311,14 +311,14 @@ export default function EmailAuth({ identifier, onAuthSuccess }: EmailAuthProps)
</p>
</div>
<div className='text-center font-light text-[14px]'>
<div className='text-center font-light text-sm'>
<button
onClick={() => {
setShowOtpVerification(false)
setOtpValue('')
setAuthError(null)
}}
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
className='font-medium text-[var(--brand-link)] underline-offset-4 transition hover:text-[var(--brand-link-hover)] hover:underline'
>
Change email
</button>
@@ -80,18 +80,18 @@ export default function PasswordAuth({ identifier, onAuthSuccess }: PasswordAuth
return (
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-screen flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<main className='relative flex min-h-screen flex-col text-[var(--landing-text)]'>
<header className='shrink-0 bg-[var(--landing-bg)]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
<div className='w-full max-w-lg px-4'>
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[500] text-[#ECECEC] text-[32px] tracking-tight'>
<h1 className='font-[500] text-[32px] text-[var(--landing-text)] tracking-tight'>
Password Required
</h1>
<p className='font-[380] text-[#999] text-[16px]'>
<p className='font-[380] text-[var(--text-subtle)] text-md'>
This chat is password-protected
</p>
</div>
@@ -103,47 +103,59 @@ export default function PasswordAuth({ identifier, onAuthSuccess }: PasswordAuth
}}
className='mt-8 w-full max-w-[410px] space-y-6'
>
<div className='space-y-2'>
<div className='space-y-6'>
<div className='flex items-center justify-between'>
<Label htmlFor='password'>Password</Label>
</div>
<div className='relative'>
<Input
id='password'
name='password'
required
type={showPassword ? 'text' : 'password'}
autoCapitalize='none'
autoComplete='new-password'
autoCorrect='off'
placeholder='Enter password'
value={password}
onChange={handlePasswordChange}
onKeyDown={handleKeyDown}
className={cn(
'pr-10',
showValidationError &&
passwordErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
autoFocus
/>
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] hover:text-[#ECECEC]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
{showValidationError && passwordErrors.length > 0 && (
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{passwordErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
<div className='relative'>
<Input
id='password'
name='password'
required
type={showPassword ? 'text' : 'password'}
autoCapitalize='none'
autoComplete='new-password'
autoCorrect='off'
placeholder='Enter password'
value={password}
onChange={handlePasswordChange}
onKeyDown={handleKeyDown}
className={cn(
'pr-10',
showValidationError &&
passwordErrors.length > 0 &&
'border-red-500 focus:border-red-500'
)}
autoFocus
/>
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--text-subtle)] hover:text-[var(--landing-text)]'
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
)}
<div
className={cn(
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
showValidationError && passwordErrors.length > 0
? 'grid-rows-[1fr]'
: 'grid-rows-[0fr]'
)}
aria-live='polite'
>
<div className='overflow-hidden'>
<div className='mt-1 space-y-1 text-red-400 text-xs'>
{passwordErrors.map((error, index) => (
<p key={index}>{error}</p>
))}
</div>
</div>
</div>
</div>
</div>
<BrandedButton
@@ -20,13 +20,13 @@ interface ChatHeaderProps {
export function ChatHeader({ chatConfig, starCount }: ChatHeaderProps) {
const brand = useBrandConfig()
const primaryColor = chatConfig?.customizations?.primaryColor || 'var(--brand-primary-hex)'
const primaryColor = chatConfig?.customizations?.primaryColor || 'var(--brand)'
const customImage = chatConfig?.customizations?.imageUrl || chatConfig?.customizations?.logoUrl
return (
<nav
aria-label='Chat navigation'
className={`flex w-full items-center justify-between px-4 pt-[12px] pb-[21px] sm:px-8 sm:pt-[8.5px] md:px-[44px] md:pt-[16px]`}
className={`flex w-full items-center justify-between px-4 pt-3 pb-[21px] sm:px-8 sm:pt-[8.5px] md:px-[44px] md:pt-4`}
>
<div className='flex items-center gap-[34px]'>
<div className='flex items-center gap-3'>
@@ -40,19 +40,19 @@ export function ChatHeader({ chatConfig, starCount }: ChatHeaderProps) {
className='h-6 w-6 rounded-md object-cover'
/>
)}
<h2 className='font-medium text-[18px] text-foreground'>
<h2 className='font-medium text-foreground text-lg'>
{chatConfig?.customizations?.headerText || chatConfig?.title || 'Chat'}
</h2>
</div>
</div>
{!brand.logoUrl && (
<div className='flex items-center gap-[16px]'>
<div className='flex items-center gap-4'>
<a
href='https://github.com/simstudioai/sim'
target='_blank'
rel='noopener noreferrer'
className='flex items-center gap-2 text-[16px] text-muted-foreground transition-colors hover:text-foreground'
className='flex items-center gap-2 text-md text-muted-foreground transition-colors hover:text-foreground'
aria-label={`GitHub repository - ${starCount} stars`}
>
<GithubIcon className='h-[16px] w-[16px]' aria-hidden='true' />
+1 -1
View File
@@ -303,7 +303,7 @@ export const ChatInput: React.FC<{
<div className='truncate font-medium text-gray-800 text-xs dark:text-gray-200'>
{file.name}
</div>
<div className='text-[10px] text-gray-500 dark:text-gray-400'>
<div className='text-gray-500 text-micro dark:text-gray-400'>
{formatFileSize(file.size)}
</div>
</div>
@@ -119,7 +119,7 @@ export function ChatFileDownload({ file }: ChatFileDownloadProps) {
<div className='flex h-8 w-8 flex-shrink-0 items-center justify-center'>{renderIcon()}</div>
<div className='min-w-0 flex-1 text-left'>
<div className='w-[100px] truncate text-xs'>{file.name}</div>
<div className='text-[10px] text-[var(--text-muted)]'>{formatFileSize(file.size)}</div>
<div className='text-[var(--text-muted)] text-micro'>{formatFileSize(file.size)}</div>
</div>
<div className='flex-shrink-0'>
{isDownloading ? (
@@ -208,7 +208,7 @@ const MarkdownRenderer = memo(function MarkdownRenderer({
const processedContent = content.trim()
return (
<div className='space-y-4 break-words font-sans text-[#0D0D0D] text-base leading-relaxed dark:text-gray-100'>
<div className='space-y-4 break-words font-sans text-[var(--text-primary)] text-base leading-relaxed dark:text-gray-100'>
<ReactMarkdown remarkPlugins={REMARK_PLUGINS} components={components}>
{processedContent}
</ReactMarkdown>
@@ -146,7 +146,7 @@ export const ClientChatMessage = memo(
{attachment.name}
</div>
{attachment.size && (
<div className='text-[10px] text-gray-500 md:text-xs dark:text-gray-400'>
<div className='text-gray-500 text-micro md:text-xs dark:text-gray-400'>
{formatFileSize(attachment.size)}
</div>
)}
@@ -163,7 +163,7 @@ export const ClientChatMessage = memo(
{/* Only render message bubble if there's actual text content (not just file count message) */}
{message.content && !String(message.content).startsWith('Sent') && (
<div className='flex justify-end'>
<div className='max-w-[80%] rounded-3xl bg-[#F4F4F4] px-4 py-3 dark:bg-gray-600'>
<div className='max-w-[80%] rounded-3xl bg-[var(--surface-4)] px-4 py-3 dark:bg-gray-600'>
<div className='whitespace-pre-wrap break-words text-base text-gray-800 leading-relaxed dark:text-gray-100'>
{isJsonObject ? (
<pre>{JSON.stringify(message.content, null, 2)}</pre>
@@ -95,7 +95,7 @@ export function FormField({
onCheckedChange={onChange}
style={value ? { backgroundColor: primaryColor } : undefined}
/>
<span className={'text-[14px] text-muted-foreground'}>{value ? 'Yes' : 'No'}</span>
<span className={'text-muted-foreground text-sm'}>{value ? 'Yes' : 'No'}</span>
</div>
)
@@ -122,7 +122,7 @@ export function FormField({
placeholder={
placeholder || (field.type === 'array' ? '["item1", "item2"]' : '{"key": "value"}')
}
className='min-h-[100px] rounded-[10px] font-mono text-[13px] shadow-sm transition-colors focus:border-gray-400 focus:ring-2 focus:ring-gray-100'
className='min-h-[100px] rounded-[10px] font-mono text-small shadow-sm transition-colors focus:border-gray-400 focus:ring-2 focus:ring-gray-100'
/>
)
@@ -141,7 +141,7 @@ export function FormField({
className={cn(
'flex cursor-pointer flex-col items-center justify-center rounded-[10px] border-2 border-dashed px-6 py-8 transition-colors',
isDragging
? 'border-[var(--brand-primary-hex)] bg-[var(--brand-primary-hex)]/5'
? 'border-[var(--brand)] bg-[color-mix(in_srgb,var(--brand)_5%,transparent)]'
: 'border-border hover:border-muted-foreground/50'
)}
>
@@ -156,7 +156,7 @@ export function FormField({
className='mb-2 h-6 w-6 text-muted-foreground'
style={isDragging ? { color: primaryColor } : undefined}
/>
<p className={'text-center text-[14px] text-muted-foreground'}>
<p className={'text-center text-muted-foreground text-sm'}>
<span style={{ color: primaryColor }} className='font-medium'>
Click to upload
</span>{' '}
@@ -169,13 +169,13 @@ export function FormField({
{files.map((file, idx) => (
<div
key={idx}
className='flex items-center justify-between rounded-[8px] border border-border bg-muted/30 px-3 py-2'
className='flex items-center justify-between rounded-lg border border-border bg-muted/30 px-3 py-2'
>
<div className='min-w-0 flex-1'>
<p className={'truncate font-medium text-[13px] text-foreground'}>
<p className={'truncate font-medium text-foreground text-small'}>
{file.name}
</p>
<p className={'text-[12px] text-muted-foreground'}>
<p className={'text-caption text-muted-foreground'}>
{formatFileSize(file.size)}
</p>
</div>
@@ -212,7 +212,7 @@ export function FormField({
return (
<div className='space-y-2'>
<Label className={'font-medium text-[14px] text-foreground'}>
<Label className={'font-medium text-foreground text-sm'}>
{displayLabel}
{isRequired && <span className='ml-0.5 text-[var(--text-error)]'>*</span>}
</Label>
@@ -33,18 +33,18 @@ export function PasswordAuth({ onSubmit, error }: PasswordAuthProps) {
return (
<AuthBackground className='dark font-[430] font-season'>
<main className='relative flex min-h-screen flex-col text-[#ECECEC]'>
<header className='shrink-0 bg-[#1C1C1C]'>
<main className='relative flex min-h-screen flex-col text-[var(--landing-text)]'>
<header className='shrink-0 bg-[var(--landing-bg)]'>
<Navbar logoOnly />
</header>
<div className='relative z-30 flex flex-1 items-center justify-center px-4 pb-24'>
<div className='w-full max-w-lg px-4'>
<div className='flex flex-col items-center justify-center'>
<div className='space-y-1 text-center'>
<h1 className='font-[500] text-[#ECECEC] text-[32px] tracking-tight'>
<h1 className='font-[500] text-[32px] text-[var(--landing-text)] tracking-tight'>
Password Required
</h1>
<p className='font-[380] text-[#999] text-[16px]'>
<p className='font-[380] text-[var(--text-subtle)] text-md'>
Enter the password to access this form.
</p>
</div>
@@ -65,12 +65,12 @@ export function PasswordAuth({ onSubmit, error }: PasswordAuthProps) {
<button
type='button'
onClick={() => setShowPassword(!showPassword)}
className='-translate-y-1/2 absolute top-1/2 right-3 text-[#999] hover:text-[#ECECEC]'
className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--text-subtle)] hover:text-[var(--landing-text)]'
>
{showPassword ? <EyeOff className='h-4 w-4' /> : <Eye className='h-4 w-4' />}
</button>
</div>
{error && <p className='text-[14px] text-red-500'>{error}</p>}
{error && <p className='text-red-500 text-sm'>{error}</p>}
</div>
<BrandedButton
@@ -9,7 +9,7 @@ export function PoweredBySim() {
return (
<div
className={
'fixed right-0 bottom-0 left-0 z-50 pb-8 text-center font-[340] text-[#999] text-[13px] leading-relaxed'
'fixed right-0 bottom-0 left-0 z-50 pb-8 text-center font-[340] text-[var(--text-subtle)] text-small leading-relaxed'
}
>
<a
@@ -6,7 +6,7 @@ interface ThankYouScreenProps {
primaryColor?: string
}
/** Default green color matching --brand-tertiary-2 */
/** Default green color matching --brand-accent */
const DEFAULT_THANK_YOU_COLOR = '#33C482'
/** Legacy blue default that should be treated as "no custom color" */
@@ -32,7 +32,7 @@ export function ThankYouScreen({ title, message, primaryColor }: ThankYouScreenP
>
{title}
</h2>
<p className={'mt-3 max-w-md font-[380] text-[#999] text-[16px]'}>{message}</p>
<p className={'mt-3 max-w-md font-[380] text-[var(--text-subtle)] text-md'}>{message}</p>
</div>
</main>
)

Some files were not shown because too many files have changed in this diff Show More