Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions frontend/components/landing/three-categories/dashboard-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ const DashboardImage = ({ className }: Props) => {

const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "start start"],
offset: ["start end", "end start"],
});

// Pan from top-left (0%, 0%) to bottom-right (100%, 100%)
// Since image is 140% of container, we can pan 40% in each direction
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 0.8, 1]);
const y = useTransform(scrollYProgress, [0, 0.5, 1], ["40px", "10px", "-60px"]);
const y = useTransform(scrollYProgress, [0, 0.5, 1], ["80px", "30px", "10px"]);
const graphsY = useTransform(scrollYProgress, [0, 0.3, 0.5, 1], ["0px", "-10px", "-100px", "-400px"]);

return (
<div
Expand All @@ -31,10 +32,24 @@ const DashboardImage = ({ className }: Props) => {
className
)}
>
<motion.div className="relative w-[140%] h-[140%] left-[40px] rounded-sm overflow-hidden" style={{ y, opacity }}>
<Image src="/assets/landing/dashboards.png" alt="Dashboard" fill className="object-cover object-left" />
<motion.div
className="relative w-[140%] bg-background h-[200%] left-[40px] rounded-sm overflow-hidden"
style={{ y, opacity }}
>
<motion.img
src="/assets/landing/dashboards-graphs.png"
alt="Dashboard graphs"
className="absolute top-0 left-0 w-full object-contain"
style={{ y: graphsY }}
/>
<Image
src="/assets/landing/dashboards-frame.png"
alt="Dashboard"
width={600}
height={400}
className="absolute left-0 top-0 w-full object-contain"
/>
</motion.div>

{/* Gradient overlay at bottom left */}
<div className="absolute bottom-0 left-0 flex h-[80%] items-center justify-center w-full bg-gradient-to-t from-landing-surface-700/60 to-landing-surface-700/0" />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import ChapterButton from "./chapter-button";

const chapters = [
{ label: "Run local,\ndebug in browser", startTime: 0 },
{ label: "Rerun at step N with\nprevious context preserved", startTime: 26 },
{ label: "Tune your\nsystem prompts", startTime: 37 },
{ label: "Instantly reflect\nchanges as you save", startTime: 50 },
{ label: "Rerun at step N with\nprevious context preserved", startTime: 12 },
{ label: "Tune your\nsystem prompts", startTime: 14 },
{ label: "Instantly reflect\nchanges as you save", startTime: 24 },
];

const DebuggerVideo = () => {
Expand Down Expand Up @@ -71,7 +71,7 @@ const DebuggerVideo = () => {
))}
</div>

<div className="relative w-full aspect-74/45 border border-landing-surface-400 rounded-lg overflow-hidden">
<div className="relative w-full aspect-74/44 border border-landing-surface-400 rounded-lg overflow-hidden">
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center z-10">
<Loader2 size={40} className="text-landing-text-400 animate-spin" />
Expand All @@ -80,7 +80,7 @@ const DebuggerVideo = () => {
<MuxPlayer
ref={playerRef}
className={cn("block", { invisible: isLoading })}
playbackId="GHkdcXx8unYRtXWDvuSoY8LDwDgWAx6p5soFBTI02vTU"
playbackId="wzXovPCSl4Hig02iX9zk01hk2vBzfJ02ccLfFUMkECvWBc"
metadata={{
video_title: "Debugger",
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import { type MotionValue } from "framer-motion";

import StaggeredPath from "./staggered-path";

const Y_VALUES = [0.5, 12.5996, 24.6992, 36.8008, 48.9004, 61];

const OFFSETS = [0.08, -0.18, 0.13, -0.07, 0.21, -0.12];

/** Straight horizontal lines (between clusters & reports) */
const AnimatedThreads3 = ({ progress }: { progress: MotionValue<number> }) => (
<svg width="220" height="62" viewBox="0 0 220 62" fill="none" className="w-full h-full">
{/* Track */}
{Y_VALUES.map((y, i) => (
<path key={`track-${i}`} d={`M0 ${y}H220`} stroke="var(--color-landing-surface-400)" />
))}
{/* Progress */}
{Y_VALUES.map((y, i) => (
<StaggeredPath key={`progress-${i}`} d={`M0 ${y}H220`} offset={OFFSETS[i]} progress={progress} />
))}
</svg>
);

export default AnimatedThreads3;
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const SignalsSection = ({ className }: Props) => {
const bufferHeight = useTransform(springBufferHeight, (v) => `${v}vh`);

return (
<div className="h-[2000px] w-full" ref={ref}>
<div className="h-[2500px] w-full" ref={ref}>
<div className={cn("sticky top-[calc(50%-430px)] flex flex-col md:gap-[54px] items-start w-full", "gap-8")}>
<div className="flex flex-col gap-1 items-start w-full">
<motion.div className="w-full" style={{ height: bufferHeight }} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"use client";

import { motion, type MotionValue, useTransform } from "framer-motion";

const REPORT_SIGNALS = [
{ name: "Browser errors", events: 11 },
{ name: "Slow tasks and optimization", events: 246 },
{ name: "Server startup errors", events: 94 },
];

interface Props {
progress: MotionValue<number>;
}

const ReportsPanel = ({ progress }: Props) => {
const slackOpacity = useTransform(progress, [0.4, 0.7], [0, 1]);
const slackY = useTransform(progress, [0.4, 0.7], [20, 0]);
const emailOpacity = useTransform(progress, [0.4, 0.7], [1, 0.6]);

return (
<div className="relative w-full h-[80%]">
{/* Email report card */}
<motion.div
style={{ opacity: emailOpacity }}
className="bg-[#1b1b1c] border border-[#2e2e2f] flex flex-col items-start overflow-hidden rounded w-full h-full"
>
{/* Email header */}
<div className="bg-[rgba(37,37,38,0.5)] flex items-center px-4 md:px-6 py-2 w-full">
<p className="font-sans text-xs text-landing-text-500 whitespace-nowrap">from: reports@mail.lmnr.ai</p>
</div>

{/* Email body */}
<div className="border-t border-[#2e2e2f] flex flex-col gap-4 md:gap-6 items-start pb-4 md:pb-5 pt-3 md:pt-3.5 px-4 md:px-6 w-full relative">
{/* Title + Total events */}
<div className="flex flex-col gap-1 items-start text-landing-text-300 w-full whitespace-nowrap">
<div className="flex items-end justify-between w-full">
<p className="font-sans font-medium text-base md:text-xl">Signals Report</p>
<div className="flex flex-col items-end justify-center w-[200px] md:w-[240px]">
<p className="font-sans text-[10px] md:text-xs">Total events</p>
<p className="font-sans font-medium text-base md:text-xl">365</p>
</div>
</div>
<div className="font-sans text-[10px] md:text-xs">
<p className="mb-0">AI Startup · Signals Events Summary</p>
<p>Mar 06, 2026 - Mar 13, 2026</p>
</div>
</div>

{/* My Agent section */}
<div className="flex flex-col gap-2 items-start w-full">
<p className="font-sans font-medium text-sm md:text-base text-landing-text-300 whitespace-nowrap">
My Agent
</p>

{/* Signals table */}
<div className="bg-[rgba(37,37,38,0.5)] border border-landing-surface-400 flex flex-col items-start overflow-hidden rounded w-full">
{/* Table header */}
<div className="border-b border-landing-surface-400 flex items-start px-3 md:px-4 w-full">
<div className="flex flex-1 items-center min-w-0 py-1 md:py-1.5">
<p className="font-sans text-[10px] md:text-xs text-landing-text-300">Signal</p>
</div>
<div className="flex items-center py-1 md:py-1.5 shrink-0">
<p className="font-sans text-[10px] md:text-xs text-landing-text-300">Events</p>
</div>
</div>
{/* Table rows */}
{REPORT_SIGNALS.map((signal, i) => (
<div
key={i}
className="border-b border-landing-surface-400 last:border-b-0 flex items-start px-3 md:px-4 w-full"
>
<div className="flex flex-1 items-center min-w-0 py-1.5 md:py-2">
<p className="font-sans text-xs md:text-base text-landing-text-300 whitespace-nowrap">
{signal.name}
</p>
</div>
<div className="flex items-center py-1.5 md:py-2 shrink-0">
<p className="font-sans text-xs md:text-base text-landing-text-300 whitespace-nowrap">
{signal.events}
</p>
</div>
</div>
))}
</div>

{/* Summary */}
<div className="bg-[rgba(37,37,38,0.5)] border border-landing-surface-400 flex flex-col gap-2 items-start overflow-hidden px-3 md:px-4 py-2.5 md:py-3 rounded w-full">
<p className="font-sans text-[10px] md:text-xs text-landing-text-300 whitespace-nowrap">Summary</p>
<p className="font-sans text-[10px] md:text-xs text-landing-text-300">
The agent frequently performs inefficient sequential edits on dozens of files instead of using scripted
automation, leading to high latency and redundant tool calls. Sandbox environments often require manual
Git authentication and dependency setup.
</p>
</div>
</div>

{/* Gradient overlay fading bottom of email */}
<div className="absolute bottom-0 left-0 w-full h-[70%] bg-gradient-to-b from-transparent via-[rgba(22,22,23,0.5)] to-landing-surface-700 pointer-events-none" />
</div>
</motion.div>

{/* Slack notification card */}
<motion.div
style={{
opacity: slackOpacity,
y: slackY,
background:
"linear-gradient(90deg, rgba(37, 37, 38, 0.8) 0%, rgba(37, 37, 38, 0.8) 100%), linear-gradient(90deg, rgb(27, 27, 28) 0%, rgb(27, 27, 28) 100%)",
}}
className="absolute left-[100px] md:left-[146px] top-[160px] md:top-[210px] w-[320px] md:w-[395px] border border-landing-surface-400 flex gap-2.5 md:gap-3 items-start overflow-hidden px-3 md:px-4 py-2.5 md:py-3 rounded z-10"
>
{/* Laminar icon */}
<div className="shrink-0 size-6 md:size-8 bg-landing-surface-700 rounded flex items-center justify-center">
<svg width="60" height="60" viewBox="0 0 76 76" fill="none" className="size-3.5 md:size-5">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.32507 73.4886C0.00220402 72.0863 0.0802819 69.9867 0.653968 68.1462C3.57273 58.7824 5.14534 48.8249 5.14534 38.5C5.14534 27.8899 3.48464 17.6677 0.408998 8.0791C-0.129499 6.40029 -0.266346 4.50696 0.811824 3.11199C2.27491 1.21902 4.56777 0 7.14535 0H37.1454C58.1322 0 75.1454 17.0132 75.1454 38C75.1454 58.9868 58.1322 76 37.1454 76H7.14535C4.85185 76 2.78376 75.0349 1.32507 73.4886Z"
fill="var(--color-landing-text-400)"
/>
</svg>
</div>

{/* Content */}
<div className="flex flex-1 flex-col gap-1 items-start justify-center min-w-0">
<div className="flex items-start justify-between w-full whitespace-nowrap font-sans text-[10px] md:text-xs">
<p className="text-landing-text-200">Laminar</p>
<p className="text-landing-text-300">3:18 pm</p>
</div>
<div className="flex gap-1 items-start w-full">
<p className="font-sans text-[10px] md:text-xs text-landing-text-400 whitespace-nowrap">Event:</p>
<div className="bg-landing-surface-600 flex items-center justify-center px-2 rounded shrink-0">
<p className="font-sans text-[10px] md:text-xs text-[rgba(208,117,78,0.6)] whitespace-nowrap">
Agent failure
</p>
</div>
</div>
<div className="flex gap-1 items-start w-full">
<p className="font-sans text-[10px] md:text-xs text-landing-text-400 whitespace-nowrap">Category:</p>
<div className="bg-landing-surface-600 flex items-center justify-center px-2 rounded shrink-0">
<p className="font-sans text-[10px] md:text-xs text-[rgba(208,117,78,0.6)] whitespace-nowrap">
logic_error
</p>
</div>
</div>
<p className="font-sans text-[10px] md:text-xs text-landing-text-400 w-full">
Description:
<br />
{`The LLM in the 'refine_report' task failed to follow the instruction to keep the summary to 3-4 sentences,.`}
</p>
</div>
</motion.div>
</div>
);
};

export default ReportsPanel;
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { cn } from "@/lib/utils";
import AnimatedThreads0 from "./animated-threads-0";
import AnimatedThreads1 from "./animated-threads-1";
import AnimatedThreads2 from "./animated-threads-2";
import AnimatedThreads3 from "./animated-threads-3";
import ClustersPanel from "./clusters-panel";
import DefinitionCard from "./definition-card";
import EventsTable from "./events-table";
import ReportsPanel from "./reports-panel";
import SectionTitle from "./section-title";

const SECTION_WIDTH = "w-[420px] md:w-[515px]";
Expand All @@ -29,12 +31,14 @@ const SignalsImage = ({ className, scrollProgress: scrollProgressProp }: Props)
const x = useTransform(scrollProgress, [0, 1], ["0%", "-100%"]);

// Map scroll ranges to 0→1 progress for each child
const threads0Progress = useTransform(scrollProgress, [0.0, 0.15], [0, 1]);
const threads1Progress = useTransform(scrollProgress, [0.2, 0.5], [0, 1]);
const threads2Progress = useTransform(scrollProgress, [0.55, 0.9], [0, 1]);
const typingProgress = useTransform(scrollProgress, [0.12, 0.3], [0, 1]);
const eventsScrollProgress = useTransform(scrollProgress, [0.3, 1], [0, 1]);
const clustersProgress = useTransform(scrollProgress, [0.7, 1], [0, 1]);
const threads0Progress = useTransform(scrollProgress, [0.0, 0.12], [0, 1]);
const threads1Progress = useTransform(scrollProgress, [0.16, 0.36], [0, 1]);
const threads2Progress = useTransform(scrollProgress, [0.44, 0.64], [0, 1]);
const threads3Progress = useTransform(scrollProgress, [0.72, 0.88], [0, 1]);
const typingProgress = useTransform(scrollProgress, [0.1, 0.24], [0, 1]);
const eventsScrollProgress = useTransform(scrollProgress, [0.24, 0.72], [0, 1]);
const clustersProgress = useTransform(scrollProgress, [0.56, 0.8], [0, 1]);
const reportsProgress = useTransform(scrollProgress, [0.8, 1], [0, 1]);

return (
<div
Expand Down Expand Up @@ -96,6 +100,24 @@ const SignalsImage = ({ className, scrollProgress: scrollProgressProp }: Props)
<ClustersPanel progress={clustersProgress} />
</div>
</div>

{/* Connector 3: Straight horizontal lines (duplicate of Connector 1) */}
<div className="flex h-full items-end shrink-0">
<div className={cn("flex flex-col items-center justify-center rounded w-[220px]", SECTION_HEIGHT)}>
<div className="h-[62px] w-[220px]">
<AnimatedThreads3 progress={threads3Progress} />
</div>
</div>
</div>

{/* Column 5: Reports (Email + Slack) */}
<div className={cn("flex flex-col h-full items-start justify-between shrink-0", SECTION_WIDTH)}>
<SectionTitle lines={["Receive insights about your traces", "automatically in email and Slack"]} />

<div className={cn("flex flex-col justify-center w-full", SECTION_HEIGHT)}>
<ReportsPanel progress={reportsProgress} />
</div>
</div>
</motion.div>
</div>
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/public/assets/landing/dashboards.png
Binary file not shown.
Binary file modified frontend/public/assets/landing/debugger.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading