improvement(landing): refine hero and mothership visuals#5181
Conversation
Tell-then-show landing: the Mothership section defines the five capabilities (Mothership · Pod · Formation · Dispatch · Return); the Features section now shows each as a real Sim UI callout floating over a static, edge-faded platform backdrop (Linear's "callout over a faded platform" pattern). - FeatureStage template: copy + masked static LandingPreview + elevated callout - LandingPreview: static autoplay=false snapshots with per-stage view/workflowId - Callouts: Mothership chat, model picker, parallel-agents Formation graph, deploy targets, logs table - Pre-footer CTA set over the Mothership render; removed the old capabilities grid Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…utes Add config-driven, padding-safe layouts consumed by route pages: - platform-page: hero (shared CTA) + centered logos + N card rows (3|4), JSON-LD, single <h1>, server-only; Workflows route as first consumer. - solutions-page: structural mirror (kept separate to diverge later); IT, Engineering, Finance, Compliance, HR routes under /solutions. - Hoist shared LandingShell/HeroCta/Logos to components/ (top-level = shared); refactor hero to consume them. - Restructure all of (landing) to the workspace folder-per-component convention (each component in its own folder + index.ts barrel).
…rm/solutions work - Resolve 5 conflicts onto my patterns: landing.tsx uses LandingShell; hero.tsx uses shared HeroCta + Logos; components/index.ts unions both export sets (Cta/Ethos/Mothership + Platform/Solutions/LandingShell). - LandingShell now owns the brand token layer + bottom reveal, so every landing-family page (home, platform, solutions) inherits them. - HeroCta absorbs origin's concentric rounded-[13px]; hero visual hex (#fafafa) replaced with --surface-1 token. - Take origin's rewritten hero-visual subtree wholesale (flat); to be re-foldered to convention in a follow-up.
Move the hero-visual + stage-home keyframe animations out of CSS modules into tailwind.config (matching the existing dash-animation pattern) and delete both module.css files. Components now use animate-hero-* utilities + arbitrary properties for the per-element delays, SVG stroke draw, and gradient shimmer; reduced-motion preserved via motion-reduce: variants. Upgrade the shimmer's hardcoded #b4b4b4 to the --text-subtle token. brand-tokens.module.css is intentionally kept: it reassigns --surface-*/ --text-* token VALUES via a doubled-class selector for specificity over .light, which Tailwind utilities cannot express.
Replace brand-tokens.module.css with a BRAND_TOKENS constant of Tailwind arbitrary-property utilities applied on the LandingShell wrapper, so the brand hex lives in the component, not a stylesheet. They emit in the utilities layer and override .light (@layer base) by cascade order — verified the brand --text-primary (#121212) wins over .light (#1a1a1a). No more .module.css files remain in the landing.
Drop <Testimonials /> from the landing composition (component kept for re-adding later).
Hero visual: clicking send zooms into the button, morphs the disc into the gooey thinking loader (held, then cycling), slides it straight across to a phrase indicator with the camera following (no zoom-out), then zooms back out as the reply types and the chat morphs into the GitHub→Agent→Jira workflow. The chat card holds a fixed size through the zoomed scene and the greeting reserves its space, so nothing drifts; the user bubble reveals only on zoom-out. Loader ink tweens dark→gradient via the thinking-loader stop-color/flood-color transition. Also folds in in-progress landing work: knowledge + integrations feature callouts, CTA chat, mothership + line-glyph, wordmark tweak; removes the ethos and testimonials sections. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the landing page fully responsive while keeping the desktop layout byte-identical (desktop classes stay the unprefixed baseline; smaller screens layer max-* overrides on top). - Navbar: hide desktop clusters below lg, add MobileNav hamburger sheet (scroll-lock, Escape/tap close, reduced-motion aware) - Hero: collapse the absolute split (visual + logos) to a stacked column below xl so iPad-landscape avoids the headline/visual collision - Mothership: 4-col grid steps to 2 (tablet) then 1 (phone) - Features: drop the floating callout below md, show the un-masked backdrop preview full-width - CTA + Footer: scale type/padding; footer 7-col steps to 3 then 2 - Document the breakpoint strategy in the landing CLAUDE.md Also includes the in-progress mothership goo/iso brand marks and the marks-lab preview route the section depends on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The grey user bubble's fade-in raced the card's upward grow on send. Hold the bubble's reveal until after the parent-driven grow settles so the card expands fully before the bubble appears. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…landing restructure Reconciles Andres's hero-visual animation rework, mothership goo/iso marks, features/footer/mobile-nav, and marks-lab with Emir's structural refactor (LandingShell spine, extracted HeroCta/Logos, navbar component folders, CSS-modules→Tailwind, platform/solutions routes). Conflict resolutions keep Emir's architecture as the spine and route Andres's behavior through it. Hero visual panel uses --surface-1 (no border), matching Andres's shipped JSX and Emir's fill choice.
…+ --landing-* coupling - Delete dead (landing) auth-modal (a duplicate of (home)'s, still on the old --landing-* / dark tokens) — its removal drops the new landing's last styling tie to the old landing. - Delete 5 merge-orphaned, zero-consumer callouts (deploy-callout, mothership-chat-callout, mothership-chat-preview, workflow-graph-preview, model-picker-preview). - Relocate the one live preview (logs-table-preview) into its consumer features/components/ + add a barrel; dissolve the owner-less feature-callouts/ shell. - Fix stale --landing-bg-surface reference in landing-preview-mount. (landing) now has zero (home) imports and zero --landing-* token usage.
Styling (within (landing)): - Replace ~90 hardcoded hex colors with the in-scope brand tokens they already equal (--surface-*/--text-*/--border*); divider edges -> --border, field/card edges -> --border-1. Delete the redundant C color-palette mirrors in the landing-preview home/sidebar and route them through tokens. - Convert static inline SVG styles (display:block/outline:none) to Tailwind. - 6 un-tokenizable hexes remain (dark send-button fills, status-green dot) — no brand token exists; left as-is. a11y / SEO: - Decorative mothership goo/iso marks: role='img'+aria-label -> aria-hidden. - Preview chrome titles <h1> -> <span> (kills duplicate client-only H1s). - sitemap.ts: add /workflows and the five /solutions/* routes. Structure: - Folder the bare logo-mark/mobile-nav leaves + barrels; complete the navbar components barrel and consolidate navbar.tsx to a single barrel import.
- Revert the right visual panel to the previous full-height framing (top-8 bottom-8) — hero text (pt-[112px]) and the logos panel are untouched, so their positions and spacing are unchanged. - Apply the canonical border-shadow chip surface: --surface-2 fill + the shared chipBorderShadowRing (1px hairline ring + soft drop shadow) from emcn, the documented chrome for a landing media panel.
- Replace Volvo with the thinkproject wordmark (official SVG, tagline/descriptor cropped out, all paths unified to --text-primary #1a1a1a; aspect 6.01). - Reorder the shared 6-logo set so the 3x2 hero grid reads: Rivian|VW (top-left), eXp Realty (top-center), Russell (top-right); Artie (bottom-left), thinkproject (bottom-center), Mobile Health (bottom-right). - Enlarge Rivian|VW a touch (height 15 -> 17, same aspect). - eXp Realty, Artie, Russell, Mobile Health, Rivian|VW all retained.
Replace the arbitrary text-[20px]/text-[16px] on the hero description with named scale tokens — text-lg (18px) desktop, text-md (16px) on phones — a touch smaller and the canonical lead size (1.2x the platform's 15px base).
…agent" Replace "solving automations" with the higher-intent "AI automations" and move the line break after "agent" so "for AI automations." sits on the second line.
- HeroCta email bar: rounded-[13px] -> rounded-lg, so the bar, the inset Book-a-demo chip, the Sign-up chip, and the navbar chips all share one radius. - Hero logos: box each wordmark in a bordered --surface-1 card (platform card chrome: rounded-lg + --border-1, 100px tall) on a responsive 3-up grid (2-up on phones) at a consistent gap-5 rhythm. Wide marks scale to fit (max-w-full h-auto). The platform/solutions 'row' layout stays bare wordmarks.
- HeroCta email bar back to rounded-[13px] (= inner chip 8px + ~5px inset) so the Book-a-demo chip's right corners nest concentrically inside the bar. - Logo cards: smaller and tighter — h-20 (80px), px-4, gap-3 (12px, the product UI card-grid rhythm).
The 80px cards read too wide-for-their-height. Restore h-[100px] (keeping the tighter gap-3/px-4) and instead shrink the wordmarks to 0.85x their optical size in the grid via GRID_ICON_SCALE — row layout unchanged.
- Sign-up chip overridden to the email bar's rounded-[13px], so the two hero CTAs share one corner radius. - Logo icons: GRID_ICON_SCALE 0.85 -> 0.65 and card padding px-4 -> px-2; card dimensions (h-[100px], gap-3) unchanged.
Cards read massive — too tall (100px) and stretched to fill the panel. Drop to h-16 (64px), cap width at w-[150px], and make the grid w-fit so it hugs the cards instead of stretching. gap-3 and the 0.65 icon scale unchanged.
Cards read too small. Bump all dimensions: h-16->h-20 (80px), w-[150px]->w-[180px], px-2->px-3, and icon scale 0.65->0.8. Grid stays content-hugging at gap-3.
- Card height h-20 -> h-[88px] (width w-[180px] unchanged), icon scale 0.8 -> 0.85. - Top row reordered: eXp (left), Russell (center), Rivian|VW (right).
- Card height h-[88px] -> h-24 (96px); width unchanged. - Top row: Rivian|VW (left), Russell (center), eXp (right).
…ogos Top-left, gap-3 above the logo grid (matching the grid rhythm); text-sm (navbar text size) in --text-muted (the label token).
… rhythm - Recolor all six customer logo SVGs to #3b3b3b (--text-body light value), so they match the Sim navbar wordmark's color. Landing is light-only, so the hardcoded value always equals var(--text-body). - Trusted-by label gap gap-3 -> gap-[22px] (the hero's description->CTA spacing).
…ip's Sign-up read too round. Take the bar + Sign-up to h-[40px] / rounded-lg (8px, the navbar chip radius), and keep the inset Book-a-demo concentric: h-[2em] + rounded (4px) with a 4px inset (8 = 4 + 4).
rounded (4px) read too square next to the bar's rounded-lg (8px). Bump to rounded-md (6px) — echoes the bar's curvature, still inside the 4px inset.
Final tidy after this session's hero/CTA/logo iteration: - hero-cta: extract the duplicated 16px label knob (px-[0.571em] + text-[16px] + font-size:inherit) into a single CTA_LABEL constant, matching the 'single knob' the TSDoc already describes — used by both Book-a-demo and Sign-up. - logos: remove the grayscale filter (now a no-op — all wordmarks were recolored to a single #3b3b3b), inline the single-use LOGO_GAP_X, and flatten the nested cn() into plain layout ternaries (dropping the now-unused cn import).
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ab13546. Configure here.
| applyDemoStep(DESKTOP_STEPS[0]) | ||
| scheduleNextStep() | ||
| return clearDemoTimer | ||
| }, [applyDemoStep, scheduleNextStep, clearDemoTimer]) |
There was a problem hiding this comment.
Desktop demo ignores resize
Low Severity
The hero LandingPreview auto-cycle starts only once on mount when the viewport is at least lg. If the page first loads below that breakpoint and the user later widens the window, isDesktop never updates and the timed demo sequence never starts, leaving a static workflow on an otherwise desktop-sized hero.
Reviewed by Cursor Bugbot for commit ab13546. Configure here.
| const [highlightedBlockId, setHighlightedBlockId] = useState<string | null>(null) | ||
| const [autoTableId, setAutoTableId] = useState<string | null>(null) | ||
| const [autoTypeHome, setAutoTypeHome] = useState(false) | ||
| const [isDesktop, setIsDesktop] = useState(true) |
There was a problem hiding this comment.
Mobile hero assumes desktop
Low Severity
LandingPreview initializes isDesktop to true, so the first client render always takes the desktop branch (multi-view AnimatePresence and cycling) until the mount effect runs. On narrow viewports that produces a visible flash of the wrong hero preview before switching to the static mobile workflow.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ab13546. Configure here.
Branding: drop the bespoke BRAND_TOKENS palette and bottom-reveal from LandingShell (use the platform's own light tokens); re-ink the wordmark, logo-mark, and hero loader from the gradient+glow to a solid --text-body so the marks read as one ink with the nav text. Add a `shimmer` prop to ThinkingLoader for a static --text-body label, and stroke the squeeze arcs with the shared gradient. Hero visual: the cursor now enters from below the field and chases the send button live through the zoom (retimed beats, no arrive-then-wait); the greeting fades in gently instead of shimmer-revealing; the click ring becomes a press-dip (hero-cursor-press replaces hero-click-ring and hero-greeting-reveal). Extract BlockHandles so the morphed GitHub card carries a real edge handle in scene space; seed the compose card at its true height; pop the sent bubble in immediately.
Reconcile Andres' hero-animation + mothership-illustration work onto the Tailwind-converted, restructured landing branch: - Hero: adopt the new scene-graph + in-scene Jira->KB morph; preserve the GPU-camera + holdCardHeight anti-jitter fix and the cursor rework. - Mothership: wire the new iso illustrations (build/ingest/integrate/monitor), drop the superseded iso-nested-cube, rebuild the iso-marks registry. - Convert his CSS-module keyframes to Tailwind (hero-kb-content-morph); keep platform tokens over hardcoded hex throughout. - Honor deletions: 3 feature-callout previews, old hero logos, hero-visual CSS. - Polish: align KB modal file rows + give the embeddings beat a smooth container; loader status phrase never wraps and uses normal weight.
Re-author the four Mothership iso-mark illustrations (Integrate, Ingest, Build, Monitor) on the refined isometric geometry, keeping the existing animation vocabulary intact: hover line-draw plus per-mark auto-motion (integrate float, ingest pulse, monitor panel-separate, build grid-flow). Map the raw exports onto the shared token palette/line weight for consistency and tune per-mark sizes for one optical weight. Build is now pure CSS (grid-flow replaces the RAF wave), so it drops 'use client' and renders as a server component. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New public /pricing page: Free/Pro/Max/Enterprise cards with the full comparison breakdown transposed from shared upgrade data + JSON-LD; prices, CTAs, and features derive from shared billing constants so they can't drift from the in-app upgrade page. - Migrate /privacy, /terms, and /changelog into the (landing) route group via a shared prose-page system (single source of truth for legal/prose chrome). - Landing polish: solid-ink iso-mark illustrations + footer/cta/features/ mothership spacing and token cleanups; sitemap adds /pricing. - Audit pass: crawlable ChipLink CTAs, correct heading hierarchy, structured-data featureList derived from the visible comparison data, legal plan name Team->Max.
Align auth-page logic with origin/staging (PR #5073) while keeping the new chip-styled UI: - Add Microsoft as a better-auth social sign-in provider (auth.ts) and surface it through the OAuth provider checker, providers API + contract, login/signup forms, SocialLoginButtons, and the landing auth modal. - Gate email/password signup behind the emailSignupEnabled server flag (DISABLE_EMAIL_SIGNUP) so signup becomes OAuth-only when configured. - Add DISABLE_MICROSOFT_AUTH / DISABLE_EMAIL_SIGNUP env + feature flags.
Resolve all 52 conflicts. Landing + auth UI kept ours; staging logic and infra taken (feature-flags/env-flags split, sandbox bundles, emcn package, platform pages). Migrate landing/auth onto staging's refactors: @/components/emcn -> @sim/emcn, cn from @sim/emcn, feature-flag consts from env-flags. Relocate ThinkingLoader into (landing) components; adapt verify-content to staging's use-verification hook; Navbar gains optional stars + logoOnly for non-marketing shells (resume, public-file). No landing UI/behavior change; auth logic parity verified. Verified: tsc 0 errors, biome clean, check:api-validation passed.
…5292) Monochrome brand icons hardcoded a single white or black fill matched to their colored tile, so they vanished when rendered bare on the home Suggested actions list (white-on-white in light mode, black-on-black in dark mode). Convert those marks to currentColor so they adapt to context, and make tile foregrounds contrast-aware via getTileIconColorClass instead of a hardcoded text-white. Also centralize all color math in apps/sim/lib/colors (perceived brightness, hex/rgb/hsl conversion, contrast-text) and route every consumer through it: the bare-icon audit, block tiles, logs trace view, whitelabeling theming, workspace presence, and the PPTX renderer no longer carry duplicate copies. Adds a bare-icon CI audit (scripts/check-bare-icons.ts) and authoring guidance.
…teQuery) The React Query conversion of an SSR-seeded, cross-origin, infrequently-changing paginated list introduced caching edge cases (empty-seed load-more, singleton cache serving stale page 1 on client-side nav) without real benefit. Restore the original useState(initialEntries)+fetch timeline, which is simpler, correct, and already review-clean from #5181.
… polish (#5298) * chore(landing): cleanup pass — size-* shorthand, drop redundant refs/memos, changelog useInfiniteQuery - emcn: collapse 29 square h-N w-N pairs to size-* across models/integrations/blog/faq/landing-preview - landing-preview: remove redundant animationKeyRef (functional setState) + dead isDesktopRef - model-directory: hoist static provider options to a module const - auth-modal: drop useMemo over the cheap getBrandConfig() call - changelog: replace useState+fetch pagination with a co-located useInfiniteQuery hook (first page stays SSR-seeded via initialData) * feat(landing): URL-state filters via nuqs (SSR-friendly) + stars fallback bump - integrations grid, models directory: search + category/provider filters now live in the URL (nuqs useQueryStates), server-parsed via createSearchParamsCache so filtered views render server-side (crawlable, shareable) — mirrors the blog index SSR pattern. Search URL writes debounced; in-memory filtering stays instant. - pricing: billing-period toggle moves to ?billing=annual (shareable, no flash). - co-located search-params.ts per page as the single source of truth (server + client). - stars: bump fallback floor 28800 -> 28900 to track the current count. * fix(landing): use the light sim mark (not green) in the integration CTA Replace brandbook/logo/small.png — referenced only by the 'Start automating {service} today' CTA on integration pages — with the black-on-light sim mark so the tile reads as a neutral brand mark beside the partner icon instead of a green block. * fix(landing): calmer release-timeline dot hover Drop the scale-150 + brightness-75 hover (a 50% size jump plus a muddy darken) for an understated opacity-fill (0.85 -> 1) with a slight scale-110, matching the comparison chart's opacity-driven hover language. * fix(landing): keep changelog load-more when initial page is empty; drop inline comments - changelog: getNextPageParam no longer treats an empty server-seeded page 1 as end-of-feed, so a failed/empty initial GitHub fetch still surfaces 'Show more' (addresses Cursor Bugbot). - replace the inline comments added in this branch with TSDoc on the search-param caches (documents the dynamic-render → SSR-filtered behavior), per the codebase's TSDoc-only convention. * revert(landing): keep the original changelog timeline (drop useInfiniteQuery) The React Query conversion of an SSR-seeded, cross-origin, infrequently-changing paginated list introduced caching edge cases (empty-seed load-more, singleton cache serving stale page 1 on client-side nav) without real benefit. Restore the original useState(initialEntries)+fetch timeline, which is simpler, correct, and already review-clean from #5181.
* feat(landing): reintroduce /contact page styled like /demo - Restore the /contact page (removed in #5181) with a two-column layout mirroring /demo: value prop + trusted-by logos on the left, a message form card on the right, on the platform light tokens and chip components - Restore the contact contract, /api/contact route (rate-limit, honeypot, Turnstile, help-inbox notification + visitor confirmation), now fully contract-bound via parseRequest - Add a useSubmitContact React Query mutation hook - Link Contact from the footer Resources column and add it to the sitemap * fix(contact): server-authoritative captcha + review fixes - Make captcha server-authoritative: drop the client-trusted captchaUnavailable flag; a valid Turnstile token is the only way past the stricter fallback bucket, so callers can't opt out of the challenge - Re-execute the Turnstile widget on every submit (incl. after expiry) instead of falling into the no-captcha path once the token expires - Reset the pre-submit gate on mutation settle so rapid double-clicks can't fire a duplicate /api/contact request - Map only feature_request to its email type; every other topic resolves to a General Inquiry confirmation so support requests aren't labeled bug reports - Drop the confirmation-email promise from the success copy (it's best-effort) - Collapse the duplicated no-captcha rate-limit branch; hoist shared response constants; read the Turnstile site key as a module constant * fix(contact): drop redundant Turnstile hostname pin The Turnstile site key is already domain-bound in Cloudflare, so pinning expectedHostname to the marketing SITE_URL (www.sim.ai) only rejected valid tokens issued on self-hosted, preview, and apex-vs-www hosts. Remove the pin and rely on Cloudflare's own domain binding. * fix(contact): fail closed on the no-captcha rate-limit backstop checkRateLimitDirect fails open on limiter-storage errors so a limiter outage never takes down normal traffic. But the contact route's no-captcha bucket is the only throttle on token-less submits, so a fail-open there let uncaptcha'd requests reach the email path unthrottled during an outage. - Add an opt-in { failClosed } option to checkRateLimitDirect; default behavior (fail open) is unchanged - Use failClosed on the contact no-captcha backstop so an unenforceable limit rejects instead of admitting - Cover both fail-open and fail-closed paths with tests * refactor(contact): TSDoc over inline comments Move the captcha-design rationale into the route handler's TSDoc and drop the inline body/JSX comments, per the project's TSDoc-only comment convention.


Summary
/pricingpage: Free/Pro/Max/Enterprise cards with the full comparison breakdown transposed from shared upgrade data + JSON-LD; prices, CTAs, and features derive from shared billing constants so they can't drift from the in-app upgrade page/privacy,/terms, and/changeloginto the(landing)route group via a sharedprose-pagesystem (single source of truth for legal/prose chrome)ChipLinkCTAs, correct heading hierarchy, structured-datafeatureListderived from the visible comparison data, legal plan name Team→MaxType of Change
Testing
bun run lintbun run check:api-validation:strictbun run type-check/landing-previewChecklist
Follow-ups
buildLandingMetadata()helper to DRY the per-pagemetadataacross all 11(landing)pages (deferred: the 7 existing pages use differing Twitteralttext and need a coordinated migration)