GeezerVison is showing github.com — improvement(landing): homepage redesign with live hero and real platf… · simstudioai/sim@8e52ff4
File treeExpand file treeCollapse file tree Open diff view settings readme-tour-capture/[workspaceId]
how-to-create-an-ai-agent
packages/emcn/src/components/tooltip
Some content is hiddenLarge Commits have some content hidden by default. Use the searchbox below for content that may be hidden. Dismiss bannerExpand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/CLAUDE.md +2-2Lines changed: 2 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -18,11 +18,11 @@ The landing page looks like the product. Its visual language is the workspace UI | 18 | 18 | - **Never touch global styles.** No additions to `app/_styles/globals.css`. All styling is local Tailwind classes; `cn()` from `@/lib/core/utils/cn` for conditionals; no inline `style` attributes. | 19 | 19 | - **Responsive - desktop is the source of truth, scaled down via `max-*` overrides.** The page is fully responsive (iPad + phone). The desktop layout stays the unprefixed baseline; smaller screens are handled by *layering* `max-*` overrides on top, so desktop renders byte-identically. Tiers: | 20 | 20 | - `max-xl:` (≤1279) - the hero's two-panel split (absolute visual + logos) collapses to a stacked, in-flow column. The split needs ≥1280 to avoid the headline colliding with the visual panel; iPad-landscape (1024) therefore gets the stacked hero with the desktop nav. | 21 | | - - `max-lg:` (≤1023) - the desktop nav clusters hide (`hidden lg:flex`) and `MobileNav` (hamburger sheet) takes over; multi-column grids step down (mothership 4→2, footer 7→3); shared gutter `px-12 → max-lg:px-8`; section gaps tighten. | | 21 | + - `max-lg:` (≤1023) - the desktop nav clusters hide (`hidden lg:flex`) and `MobileNav` (hamburger sheet) takes over; multi-column grids step down (mothership 4→2, footer 7→3); shared gutter `px-20 → max-lg:px-8`; section gaps tighten. | 22 | 22 | - `max-md:` (≤767) - Features beats drop the floating callout (`max-md:hidden`) and show the un-masked backdrop preview full-width. | 23 | 23 | - `max-sm:` (≤639) - single-column grids, smallest type scale, `px-5` gutter, hero CTA row stacks. | 24 | 24 |
| 25 | | - When adding a new section, give it the same `px-12 max-lg:px-8 max-sm:px-5` gutter so the navbar wordmark stays aligned with section content at every width. Verify desktop is unchanged and there is zero horizontal overflow at 1280 / 1024 / 768 / 390 before shipping. | | 25 | + When adding a new section, give it the same `px-20 max-lg:px-8 max-sm:px-5` gutter so the navbar wordmark stays aligned with section content at every width. Verify desktop is unchanged and there is zero horizontal overflow at 1280 / 1024 / 768 / 390 before shipping. | 26 | 26 |
| 27 | 27 | ## Performance - page speed is a feature | 28 | 28 |
| |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/blog/[slug]/loading.tsx +3-3Lines changed: 3 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -3,7 +3,7 @@ import { Skeleton } from '@sim/emcn' | 3 | 3 | export default function BlogPostLoading() { | 4 | 4 | return ( | 5 | 5 | <article className='w-full bg-[var(--bg)]'> | 6 | | - <div className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | | 6 | + <div className='mx-auto w-full max-w-[1460px] px-20 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | 7 | 7 | <div className='mb-6'> | 8 | 8 | <Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--surface-hover)]' /> | 9 | 9 | </div> | @@ -31,8 +31,8 @@ export default function BlogPostLoading() { | 31 | 31 |
| 32 | 32 | <div className='mt-8 h-px w-full bg-[var(--border)]' /> | 33 | 33 |
| 34 | | - <div className='mx-auto w-full max-w-[1446px]'> | 35 | | - <div className='mx-12 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'> | | 34 | + <div className='mx-auto w-full max-w-[1460px]'> | | 35 | + <div className='mx-20 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'> | 36 | 36 | <div className='mx-auto max-w-[900px] px-6 py-16'> | 37 | 37 | <div className='space-y-4'> | 38 | 38 | <Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--surface-hover)]' /> | |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/blog/[slug]/page.tsx +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -39,7 +39,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string | 39 | 39 | return ( | 40 | 40 | <article className='w-full bg-[var(--bg)]' itemScope itemType='https://schema.org/TechArticle'> | 41 | 41 | <JsonLd data={graphJsonLd} /> | 42 | | - <header className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | | 42 | + <header className='mx-auto w-full max-w-[1460px] px-20 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | 43 | 43 | <div className='mb-6'> | 44 | 44 | <BackLink href='/blog' label='Back to Blog' /> | 45 | 45 | </div> | @@ -118,7 +118,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string | 118 | 118 |
| 119 | 119 | <div className='mt-8 h-px w-full bg-[var(--border)]' /> | 120 | 120 |
| 121 | | - <div className='mx-auto w-full max-w-[1446px] px-12 max-sm:px-5 max-lg:px-8'> | | 121 | + <div className='mx-auto w-full max-w-[1460px] px-20 max-sm:px-5 max-lg:px-8'> | 122 | 122 | <div className='border-[var(--border)] border-x'> | 123 | 123 | <div className='mx-auto max-w-[900px] px-6 py-16' itemProp='articleBody'> | 124 | 124 | <div className='prose prose-lg max-w-none prose-blockquote:border-[var(--border-1)] prose-hr:border-[var(--border)] prose-headings:font-season prose-a:text-[var(--text-primary)] prose-blockquote:text-[var(--text-muted)] prose-code:text-[var(--text-primary)] prose-headings:text-[var(--text-primary)] prose-li:text-[var(--text-body)] prose-p:text-[var(--text-body)] prose-strong:text-[var(--text-primary)] prose-headings:tracking-[-0.02em]'> | |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/blog/loading.tsx +3-3Lines changed: 3 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -3,9 +3,9 @@ import { Skeleton } from '@sim/emcn' | 3 | 3 | export default function BlogLoading() { | 4 | 4 | return ( | 5 | 5 | <section className='bg-[var(--bg)]'> | 6 | | - <div className='mx-auto w-full max-w-[1446px]'> | | 6 | + <div className='mx-auto w-full max-w-[1460px]'> | 7 | 7 | {/* Header skeleton */} | 8 | | - <div className='px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | | 8 | + <div className='px-20 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | 9 | 9 | <Skeleton className='mb-5 h-[20px] w-[60px] rounded-md bg-[var(--surface-hover)]' /> | 10 | 10 | <div className='flex flex-col gap-4 md:flex-row md:items-end md:justify-between'> | 11 | 11 | <Skeleton className='h-[40px] w-[240px] rounded-[4px] bg-[var(--surface-hover)]' /> | @@ -14,7 +14,7 @@ export default function BlogLoading() { | 14 | 14 | </div> | 15 | 15 |
| 16 | 16 | {/* Content area with vertical border rails */} | 17 | | - <div className='mx-12 mt-8 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'> | | 17 | + <div className='mx-20 mt-8 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'> | 18 | 18 | <div className='h-px w-full bg-[var(--border)]' /> | 19 | 19 |
| 20 | 20 | {/* Featured skeleton */} | |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/blog/page.tsx +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -97,7 +97,7 @@ export default async function BlogIndex({ | 97 | 97 | <JsonLd data={collectionJsonLd} /> | 98 | 98 |
| 99 | 99 | {/* Section header */} | 100 | | - <div className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | | 100 | + <div className='mx-auto w-full max-w-[1460px] px-20 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'> | 101 | 101 | <div className='flex flex-col gap-4 md:flex-row md:items-end md:justify-between'> | 102 | 102 | <h1 className='text-balance text-[28px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[40px]'> | 103 | 103 | Latest from Sim | @@ -112,7 +112,7 @@ export default async function BlogIndex({ | 112 | 112 | <div className='mt-8 h-px w-full bg-[var(--border)]' /> | 113 | 113 |
| 114 | 114 | {/* Content area with vertical border rails */} | 115 | | - <div className='mx-auto w-full max-w-[1446px] px-12 max-sm:px-5 max-lg:px-8'> | | 115 | + <div className='mx-auto w-full max-w-[1460px] px-20 max-sm:px-5 max-lg:px-8'> | 116 | 116 | <div className='border-[var(--border)] border-x'> | 117 | 117 | {/* Featured posts */} | 118 | 118 | {featured.length > 0 && ( | |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/careers/careers.tsx +4-4Lines changed: 4 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -21,8 +21,8 @@ interface CareersProps { | 21 | 21 | * crawlable HTML; the interactive {@link JobBoard} hydrates on top to add | 22 | 22 | * Team/Location filtering. | 23 | 23 | * | 24 | | - * Both sections share the landing gutter — capped and centered at `max-w-[1446px]` | 25 | | - * with the navbar-aligned `px-12 max-lg:px-8 max-sm:px-5` so the headline starts on | | 24 | + * Both sections share the landing gutter — capped and centered at `max-w-[1460px]` | | 25 | + * with the navbar-aligned `px-20 max-lg:px-8 max-sm:px-5` so the headline starts on | 26 | 26 | * the same vertical line as the wordmark. The hero carries the single `<h1>` | 27 | 27 | * (containing "Sim" and "AI workspace") plus an sr-only product summary for AI | 28 | 28 | * citation (landing CLAUDE.md → GEO); the roles section owns its own `<h2>`. | @@ -45,7 +45,7 @@ export default async function Careers({ searchParams }: CareersProps) { | 45 | 45 | <section | 46 | 46 | id='careers-hero' | 47 | 47 | aria-labelledby='careers-heading' | 48 | | - className='mx-auto flex w-full max-w-[1446px] flex-col gap-5 px-12 pt-20 pb-10 max-sm:px-5 max-sm:pt-16 max-lg:px-8' | | 48 | + className='mx-auto flex w-full max-w-[1460px] flex-col gap-5 px-20 pt-20 pb-10 max-sm:px-5 max-sm:pt-16 max-lg:px-8' | 49 | 49 | > | 50 | 50 | <p className='sr-only'> | 51 | 51 | Careers at Sim, the open-source AI workspace where teams build, deploy, and manage AI | @@ -70,7 +70,7 @@ export default async function Careers({ searchParams }: CareersProps) { | 70 | 70 | <section | 71 | 71 | id='open-roles' | 72 | 72 | aria-labelledby='open-roles-heading' | 73 | | - className='mx-auto flex w-full max-w-[1446px] flex-col gap-10 px-12 pt-6 pb-24 max-sm:px-5 max-sm:pb-16 max-lg:px-8' | | 73 | + className='mx-auto flex w-full max-w-[1460px] flex-col gap-10 px-20 pt-6 pb-24 max-sm:px-5 max-sm:pb-16 max-lg:px-8' | 74 | 74 | > | 75 | 75 | <h2 | 76 | 76 | id='open-roles-heading' | |
Collapse file Copy file name to clipboardExpand all lines: apps/sim/app/(landing)/components/cta/cta.tsx +3-3Lines changed: 3 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -11,15 +11,15 @@ import { ChipLink } from '@sim/emcn' | 11 | 11 | * `landing.tsx`) above it and the `Footer`'s top margin below it. The headline | 12 | 12 | * mirrors the hero `<h1>` exactly (48px / `leading-[1.1]` and the same responsive | 13 | 13 | * ramp), so the page opens and closes on the same display size. Horizontal | 14 | | - * padding (`px-12`) matches every section above, and the section is capped and | 15 | | - * centered at the shared `max-w-[1446px]`. | | 14 | + * padding (`px-20`) matches every section above, and the section is capped and | | 15 | + * centered at the shared `max-w-[1460px]`. | 16 | 16 | */ | 17 | 17 | export function Cta() { | 18 | 18 | return ( | 19 | 19 | <section | 20 | 20 | id='cta' | 21 | 21 | aria-labelledby='cta-heading' | 22 | | - className='mx-auto flex w-full max-w-[1446px] flex-col items-center gap-[22px] px-12 text-center max-sm:px-5 max-lg:px-8' | | 22 | + className='mx-auto flex w-full max-w-[1460px] flex-col items-center gap-[22px] px-20 text-center max-sm:px-5 max-lg:px-8' | 23 | 23 | > | 24 | 24 | <h2 | 25 | 25 | id='cta-heading' | |
Collapse file Copy file name to clipboard +37Lines changed: 37 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +import type { ReactNode } from 'react' | | 2 | +import { cn } from '@sim/emcn' | | 3 | + | | 4 | +const CALLOUT_FADE = | | 5 | + '[-webkit-mask-image:linear-gradient(to_bottom,#000_72%,transparent)] [mask-image:linear-gradient(to_bottom,#000_72%,transparent)]' | | 6 | + | | 7 | +interface CalloutFrameProps { | | 8 | + /** Width/layout for the panel (e.g. `w-[340px]`). */ | | 9 | + className?: string | | 10 | + /** Sizing for the inner body (e.g. `h-[300px]`). */ | | 11 | + bodyClassName?: string | | 12 | + /** Dissolve the body's lower edge so the surface reads as continuing below. */ | | 13 | + fade?: boolean | | 14 | + children: ReactNode | | 15 | +} | | 16 | + | | 17 | +/** | | 18 | + * The shared chrome for a callout: an elevated panel that lifts a real Sim UI | | 19 | + * surface off the backdrop, wearing the hero platform window's exact chrome - | | 20 | + * 10px radius, `--surface-1` fill, and the hairline-ring + layered soft shadow | | 21 | + * - so every floating window on the page reads as one family. Optionally fades | | 22 | + * its body's foot so a long surface dissolves rather than ends on a hard edge. | | 23 | + */ | | 24 | +export function CalloutFrame({ className, bodyClassName, fade, children }: CalloutFrameProps) { | | 25 | + return ( | | 26 | + <div | | 27 | + className={cn( | | 28 | + 'overflow-hidden rounded-[10px] bg-[var(--surface-1)] shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_2px_6px_0_rgba(0,0,0,0.05),0_4px_42px_0_rgba(0,0,0,0.06)]', | | 29 | + className | | 30 | + )} | | 31 | + > | | 32 | + <div className={cn('relative overflow-hidden', fade && CALLOUT_FADE, bodyClassName)}> | | 33 | + {children} | | 34 | + </div> | | 35 | + </div> | | 36 | + ) | | 37 | +} |
Collapse file Copy file name to clipboard +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +export { CalloutFrame } from './callout-frame' |
Collapse file Copy file name to clipboard +117Lines changed: 117 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +import type { ReactNode } from 'react' | | 2 | +import { ChipTag, cn } from '@sim/emcn' | | 3 | +import { ArrowRight } from 'lucide-react' | | 4 | +import Image from 'next/image' | | 5 | +import Link from 'next/link' | | 6 | + | | 7 | +interface FeatureCardProps { | | 8 | + /** Capability name shown as a chip tag pinned to the card's top-right corner. */ | | 9 | + eyebrow: string | | 10 | + /** The beat's headline (`<h3>` - the section owns the single `<h2>`). */ | | 11 | + title: string | | 12 | + /** Supporting copy beneath the headline. */ | | 13 | + description: string | | 14 | + /** Optional trailing link (e.g. the feature's platform page). */ | | 15 | + href?: string | | 16 | + /** Label for {@link href}. */ | | 17 | + linkLabel?: string | | 18 | + /** Backdrop image under the floating callout (public path). */ | | 19 | + backdropSrc: string | | 20 | + /** | | 21 | + * Which side the media stage sits on from `lg` up (cards alternate down the | | 22 | + * section, Cursor-style). Below `lg` the card always stacks media-first. | | 23 | + */ | | 24 | + mediaSide?: 'left' | 'right' | | 25 | + /** | | 26 | + * Square the card's bottom corners so its bottom edge merges with a | | 27 | + * full-bleed divider drawn at the same line (the section's last card). | | 28 | + */ | | 29 | + flushBottom?: boolean | | 30 | + /** The elevated real-UI callout floating over the backdrop. */ | | 31 | + children: ReactNode | | 32 | +} | | 33 | + | | 34 | +/** | | 35 | + * Cursor-style feature card - one large OUTLINED container (a light | | 36 | + * `--border` hairline on a transparent ground, no grey fill) holding a media | | 37 | + * stage (a painted backdrop with the beat's real-UI callout floating over it) | | 38 | + * and a vertically-centered copy column: `<h3>`, muted description, and an | | 39 | + * optional arrow link. `mediaSide` picks which side the media sits on so the | | 40 | + * cards can alternate down the section. The beat's name sits as a borderless | | 41 | + * grey-filled {@link ChipTag} pinned to the card's top corner on the COPY | | 42 | + * side (top-right when media is left, top-left when media is right), just | | 43 | + * inside the outline - never floating over the media image. | | 44 | + * | | 45 | + * Below `lg` the card stacks - media on top, copy beneath - and the media | | 46 | + * shortens so the card stays scannable in the compact grid. | | 47 | + */ | | 48 | +export function FeatureCard({ | | 49 | + eyebrow, | | 50 | + title, | | 51 | + description, | | 52 | + href, | | 53 | + linkLabel, | | 54 | + backdropSrc, | | 55 | + mediaSide = 'left', | | 56 | + flushBottom = false, | | 57 | + children, | | 58 | +}: FeatureCardProps) { | | 59 | + const mediaRight = mediaSide === 'right' | | 60 | + return ( | | 61 | + <article | | 62 | + className={cn( | | 63 | + 'relative grid gap-10 rounded-[10px] border border-[var(--border)] p-4 max-lg:grid-cols-1 max-lg:gap-6', | | 64 | + mediaRight ? 'grid-cols-[386px_1fr]' : 'grid-cols-[1fr_386px]', | | 65 | + flushBottom && 'rounded-b-none' | | 66 | + )} | | 67 | + > | | 68 | + <ChipTag | | 69 | + variant='mono' | | 70 | + className={cn('absolute top-4 z-10', mediaRight ? 'left-4' : 'right-4')} | | 71 | + > | | 72 | + {eyebrow} | | 73 | + </ChipTag> | | 74 | + <div | | 75 | + aria-hidden='true' | | 76 | + className={cn( | | 77 | + 'relative h-[650px] overflow-hidden rounded-[4px] max-sm:h-[280px] max-lg:order-1 max-lg:h-[360px]', | | 78 | + mediaRight && 'lg:order-2' | | 79 | + )} | | 80 | + > | | 81 | + <Image | | 82 | + src={backdropSrc} | | 83 | + alt='' | | 84 | + fill | | 85 | + sizes='(max-width: 1460px) 70vw, 900px' | | 86 | + className='object-cover' | | 87 | + /> | | 88 | + <div className='absolute inset-0 flex items-center justify-center p-4 [&>*]:max-w-full'> | | 89 | + {children} | | 90 | + </div> | | 91 | + </div> | | 92 | + | | 93 | + <div | | 94 | + className={cn( | | 95 | + 'flex flex-col justify-center max-lg:order-2 max-lg:pb-2', | | 96 | + mediaRight ? 'pl-4 max-lg:pl-0' : 'pr-4 max-lg:pr-0' | | 97 | + )} | | 98 | + > | | 99 | + <h3 className='text-balance font-medium text-[22px] text-[var(--text-primary)] leading-[1.3] max-sm:text-[20px]'> | | 100 | + {title} | | 101 | + </h3> | | 102 | + <p className='mt-3 text-pretty text-[15px] text-[var(--text-muted)] leading-[1.6]'> | | 103 | + {description} | | 104 | + </p> | | 105 | + {href && linkLabel && ( | | 106 | + <Link | | 107 | + href={href} | | 108 | + className='mt-5 flex items-center gap-1.5 text-[15px] text-[var(--text-body)] transition-colors hover-hover:text-[var(--text-primary)]' | | 109 | + > | | 110 | + {linkLabel} | | 111 | + <ArrowRight className='size-[15px]' /> | | 112 | + </Link> | | 113 | + )} | | 114 | + </div> | | 115 | + </article> | | 116 | + ) | | 117 | +} |
|