@@ -32,11 +32,11 @@ const CLOUD = "https://onecomputer-openvtc.eastus2.cloudapp.azure.com";
3232const DOCS = "/docs/" ;
3333
3434const agentOptions = [
35- { id : "claude" , name : "Claude" , kind : "Desktop · Code" , href : "https://claude.com/download" } ,
36- { id : "nanoclaw" , name : "NanoClaw" , kind : "Container-native" , href : "https://github.com/qwibitai/nanoclaw" } ,
37- { id : "openclaw" , name : "OpenClaw" , kind : "Open agent platform" , href : "https://github.com/openclaw/openclaw" } ,
38- { id : "codex" , name : "Codex" , kind : "Coding agent" , href : "https://openai.com/codex/" } ,
39- { id : "hermes" , name : "Hermes Agent" , kind : "Open · extensible" , href : "https://github.com/NousResearch/hermes-agent" } ,
35+ { id : "claude" , name : "Claude" , mark : "C" , kind : "Desktop · Code" , command : "claude" , badges : [ "Claude Desktop" , "Claude Code" ] , accent : "#d9946d ", href : "https://claude.com/download" } ,
36+ { id : "nanoclaw" , name : "NanoClaw" , mark : "N" , kind : "Container-native" , command : "nanoclaw" , badges : [ "NanoClaw runtime" , "Container-native" ] , accent : "#d3b75e ", href : "https://github.com/qwibitai/nanoclaw" } ,
37+ { id : "openclaw" , name : "OpenClaw" , mark : "O" , kind : "Open agent platform" , command : "openclaw gateway" , badges : [ "OpenClaw gateway" , "Channel bridge" ] , accent : "#c976e4 ", href : "https://github.com/openclaw/openclaw" } ,
38+ { id : "codex" , name : "Codex" , mark : "X" , kind : "Coding agent" , command : "codex" , badges : [ "Codex workspace" , "Terminal agent" ] , accent : "#8bd8b0 ", href : "https://openai.com/codex/" } ,
39+ { id : "hermes" , name : "Hermes Agent" , mark : "H" , kind : "Open · extensible" , command : "hermes" , badges : [ "Hermes Agent" , "Tool runtime" ] , accent : "#8fb6ed ", href : "https://github.com/NousResearch/hermes-agent" } ,
4040] ;
4141
4242const productSteps = [
@@ -126,30 +126,34 @@ const reveal = {
126126 } ) ,
127127} ;
128128
129- function AgentRotator ( { compact = false } ) {
129+ function useAgentCycle ( interval = 4200 , paused = false ) {
130130 const reduceMotion = useReducedMotion ( ) ;
131131 const [ active , setActive ] = useState ( 0 ) ;
132132
133133 useEffect ( ( ) => {
134- if ( reduceMotion ) return undefined ;
134+ if ( reduceMotion || paused ) return undefined ;
135135 const timer = window . setInterval (
136136 ( ) => setActive ( ( current ) => ( current + 1 ) % agentOptions . length ) ,
137- 2700 ,
137+ interval ,
138138 ) ;
139139 return ( ) => window . clearInterval ( timer ) ;
140- } , [ reduceMotion ] ) ;
140+ } , [ active , interval , paused , reduceMotion ] ) ;
141+
142+ return { active, setActive, agent : agentOptions [ active ] } ;
143+ }
141144
142- const agent = agentOptions [ active ] ;
145+ function AgentName ( { agent, compact = false } ) {
146+ const reduceMotion = useReducedMotion ( ) ;
143147
144148 return (
145149 < span className = { `agent-rotator ${ compact ? "agent-rotator-compact" : "" } ` } aria-live = "polite" >
146- < AnimatePresence initial = { false } mode = "wait" >
150+ < AnimatePresence initial = { false } >
147151 < motion . span
148152 key = { agent . id }
149- initial = { { opacity : 0 , y : 14 , filter : "blur(8px )" } }
153+ initial = { { opacity : 0 , y : 12 , filter : "blur(7px )" } }
150154 animate = { { opacity : 1 , y : 0 , filter : "blur(0px)" } }
151- exit = { { opacity : 0 , y : - 14 , filter : "blur(8px )" } }
152- transition = { { duration : reduceMotion ? 0 : 0.38 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
155+ exit = { { opacity : 0 , y : - 12 , filter : "blur(7px )" } }
156+ transition = { { duration : reduceMotion ? 0 : 0.42 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
153157 >
154158 { agent . name }
155159 </ motion . span >
@@ -158,6 +162,14 @@ function AgentRotator({ compact = false }) {
158162 ) ;
159163}
160164
165+ function AgentRotator ( { compact = false } ) {
166+ const { agent } = useAgentCycle ( 3200 ) ;
167+
168+ return (
169+ < AgentName agent = { agent } compact = { compact } />
170+ ) ;
171+ }
172+
161173function Logo ( ) {
162174 return (
163175 < span className = "logo-lockup" >
@@ -184,10 +196,13 @@ function Reveal({ children, className = "", delay = 0 }) {
184196 ) ;
185197}
186198
187- function ProductCapture ( { step, hero = false } ) {
199+ function ProductCapture ( { step, hero = false , agent = null } ) {
200+ const workspaceBadges = agent ?. badges || [ "Claude Desktop" , "Claude Code" ] ;
201+
188202 return (
189203 < motion . div
190204 className = { `capture capture-${ step . kind } ${ hero ? "capture-hero" : "" } ` }
205+ style = { agent ? { "--agent-accent" : agent . accent } : undefined }
191206 initial = { { opacity : 0 , scale : 0.975 , y : 16 } }
192207 animate = { { opacity : 1 , scale : 1 , y : 0 } }
193208 exit = { { opacity : 0 , scale : 0.985 , y : - 12 } }
@@ -207,11 +222,53 @@ function ProductCapture({ step, hero = false }) {
207222 < div className = "capture-viewport" >
208223 < img src = { step . image } alt = { step . alt } loading = { hero ? "eager" : "lazy" } />
209224 { step . kind === "workspace" && (
210- < div className = "workspace-badges" aria-hidden = "true" >
211- < span > < Monitor size = { 13 } /> Claude Desktop</ span >
212- < span > < Cpu size = { 13 } /> Claude Code</ span >
213- < span className = "workspace-badge-live" > < i /> Isolated sandbox</ span >
214- </ div >
225+ < AnimatePresence initial = { false } >
226+ < motion . div
227+ className = "workspace-badges"
228+ key = { agent ?. id || "claude" }
229+ aria-hidden = "true"
230+ initial = { { opacity : 0 , y : - 8 } }
231+ animate = { { opacity : 1 , y : 0 } }
232+ exit = { { opacity : 0 , y : 8 } }
233+ transition = { { duration : 0.32 } }
234+ >
235+ < span > < Monitor size = { 13 } /> { workspaceBadges [ 0 ] } </ span >
236+ < span > < Cpu size = { 13 } /> { workspaceBadges [ 1 ] } </ span >
237+ < span className = "workspace-badge-live" > < i /> Isolated sandbox</ span >
238+ </ motion . div >
239+ </ AnimatePresence >
240+ ) }
241+ { hero && agent && (
242+ < AnimatePresence initial = { false } >
243+ < motion . div
244+ className = "agent-runtime-layer"
245+ key = { agent . id }
246+ initial = { { opacity : 0 } }
247+ animate = { { opacity : 1 } }
248+ exit = { { opacity : 0 } }
249+ transition = { { duration : 0.38 } }
250+ >
251+ < div className = "agent-visual-tint" />
252+ < motion . div
253+ className = "agent-runtime-card"
254+ initial = { { opacity : 0 , y : 14 , scale : 0.97 } }
255+ animate = { { opacity : 1 , y : 0 , scale : 1 } }
256+ exit = { { opacity : 0 , y : - 10 , scale : 0.98 } }
257+ transition = { { duration : 0.42 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
258+ >
259+ < div className = "agent-runtime-top" >
260+ < span className = "agent-runtime-mark" > { agent . mark } </ span >
261+ < span className = "agent-runtime-title" > < small > Connected agent</ small > < strong > { agent . name } </ strong > </ span >
262+ < span className = "agent-runtime-ready" > < i /> Ready</ span >
263+ </ div >
264+ < div className = "agent-runtime-command" > < small > $ onecomputer agent attach</ small > < code > { agent . command } </ code > </ div >
265+ < div className = "agent-runtime-foot" >
266+ < span > < ShieldCheck size = { 12 } /> Policy-bound</ span >
267+ < span > < LockKeyhole size = { 12 } /> Isolated</ span >
268+ </ div >
269+ </ motion . div >
270+ </ motion . div >
271+ </ AnimatePresence >
215272 ) }
216273 < div className = "capture-redactions" aria-hidden = "true" >
217274 { step . kind === "ciso" && (
@@ -276,6 +333,8 @@ function Nav({ progress, page = "home" }) {
276333
277334function Hero ( ) {
278335 const heroRef = useRef ( null ) ;
336+ const [ rotationPaused , setRotationPaused ] = useState ( false ) ;
337+ const { active : activeAgentIndex , setActive : setActiveAgentIndex , agent : activeAgent } = useAgentCycle ( 4400 , rotationPaused ) ;
279338 const { scrollYProgress } = useScroll ( {
280339 target : heroRef ,
281340 offset : [ "start start" , "end start" ] ,
@@ -303,7 +362,11 @@ function Hero() {
303362 animate = { { opacity : 1 , y : 0 } }
304363 transition = { { delay : 0.25 , duration : 0.8 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
305364 >
306- A safe computer for < span > < AgentRotator /> </ span >
365+ < span className = "hero-headline-static" > A safe computer</ span >
366+ < span className = "hero-agent-line" >
367+ < span className = "hero-headline-for" > for</ span >
368+ < span className = "hero-agent-stage" style = { { "--agent-accent" : activeAgent . accent } } > < AgentName agent = { activeAgent } /> </ span >
369+ </ span >
307370 </ motion . h1 >
308371 < motion . p
309372 className = "hero-lede"
@@ -343,12 +406,16 @@ function Hero() {
343406
344407 < motion . div
345408 className = "hero-product"
409+ onMouseEnter = { ( ) => setRotationPaused ( true ) }
410+ onMouseLeave = { ( ) => setRotationPaused ( false ) }
411+ onFocusCapture = { ( ) => setRotationPaused ( true ) }
412+ onBlurCapture = { ( ) => setRotationPaused ( false ) }
346413 style = { { y : visualY , scale : visualScale } }
347414 initial = { { opacity : 0 , y : 45 , rotateX : 6 } }
348415 animate = { { opacity : 1 , y : 0 , rotateX : 0 } }
349416 transition = { { delay : 0.35 , duration : 1 , ease : [ 0.22 , 1 , 0.36 , 1 ] } }
350417 >
351- < ProductCapture step = { productSteps [ 0 ] } hero />
418+ < ProductCapture step = { productSteps [ 0 ] } hero agent = { activeAgent } />
352419 < motion . div
353420 className = "floating-proof proof-policy"
354421 animate = { { y : [ 0 , - 7 , 0 ] } }
@@ -369,6 +436,26 @@ function Hero() {
369436 Company policy < b > connected</ b >
370437 </ span >
371438 </ motion . div >
439+ < div className = "hero-agent-dock" aria-label = "Choose an agent runtime" >
440+ < span className = "hero-agent-dock-label" > Runtime</ span >
441+ { agentOptions . map ( ( agent , index ) => (
442+ < motion . button
443+ type = "button"
444+ key = { agent . id }
445+ className = { index === activeAgentIndex ? "is-active" : "" }
446+ style = { { "--agent-accent" : agent . accent } }
447+ aria-label = { `Show ${ agent . name } workspace` }
448+ aria-pressed = { index === activeAgentIndex }
449+ title = { agent . name }
450+ onClick = { ( ) => setActiveAgentIndex ( index ) }
451+ whileHover = { { y : - 2 } }
452+ whileTap = { { scale : 0.94 } }
453+ >
454+ < span > { agent . mark } </ span >
455+ < small > { agent . name } </ small >
456+ </ motion . button >
457+ ) ) }
458+ </ div >
372459 </ motion . div >
373460 </ div >
374461
0 commit comments