@@ -24,22 +24,33 @@ const workflowLanes = [
2424] as const ;
2525
2626// Both ends run 180 units off-canvas so the pulse dash (0.1 of the path,
27- // ~161-169 units) sits fully off-screen at the loop's extremes.
28- const workflowLanePath = ( offset : number ) => {
27+ // ~161-169 units) sits fully off-screen at the loop's extremes. The card keeps
28+ // the original turn positions; the hero pushes the turns out to sit inside the
29+ // wide field's visible center. The pulse dash math in main.css assumes the
30+ // card geometry, so heroes render rails only (see WorkflowMotif).
31+ const workflowLanePath = ( offset : number , rightX = 262 , leftX = 58 ) => {
2932 const rightRadius = 38 - offset ;
3033 const leftRadius = 38 + offset ;
3134 return [
3235 `M -180 ${ 48 + offset } ` ,
33- "H 262" ,
36+ `H ${ rightX } ` ,
3437 `a ${ rightRadius } ${ rightRadius } 0 0 1 0 ${ rightRadius * 2 } ` ,
35- "H 58" ,
38+ `H ${ leftX } ` ,
3639 `a ${ leftRadius } ${ leftRadius } 0 0 0 0 ${ leftRadius * 2 } ` ,
37- "H 262" ,
40+ `H ${ rightX } ` ,
3841 `a ${ rightRadius } ${ rightRadius } 0 0 1 0 ${ rightRadius * 2 } ` ,
3942 "H -180" ,
4043 ] . join ( " " ) ;
4144} ;
4245
46+ // Card motifs draw one 320×400 cluster stretched over the 4/5 plate. Heroes
47+ // are wide and short, so slicing that same viewBox magnifies the artwork ~4×
48+ // and the center cut-out mask swallows it — the shapes stop reading as the
49+ // card imagery. Instead heroes repeat the cluster across a 1600×400 field so
50+ // the pattern stays at card scale, matching the Actors hero's tiled marks.
51+ const HERO_FIELD_VIEWBOX = "0 0 1600 400" ;
52+ const CARD_VIEWBOX = "0 0 320 400" ;
53+
4354const agentOSCircles = [
4455 { cx : 16 , cy : 36 , direction : "forward" , tone : "bright" , delay : 0 } ,
4556 { cx : 96 , cy : 108 , direction : "backward" , tone : "soft" , delay : 0 } ,
@@ -253,7 +264,7 @@ const containerClassName = (
253264const WorkflowMotif = ( { surface } : Pick < ProductMotifProps , "surface" > ) => (
254265 < svg
255266 className = "h-full w-full"
256- viewBox = "0 0 320 400"
267+ viewBox = { surface === "hero" ? HERO_FIELD_VIEWBOX : CARD_VIEWBOX }
257268 preserveAspectRatio = { surface === "hero" ? "xMidYMid slice" : "none" }
258269 >
259270 { workflowLanes . map ( ( lane ) => (
@@ -268,16 +279,22 @@ const WorkflowMotif = ({ surface }: Pick<ProductMotifProps, "surface">) => (
268279 >
269280 < path
270281 className = { `workflow-rail workflow-rail--${ lane . tone } ` }
271- d = { workflowLanePath ( lane . offset ) }
272- pathLength = "1"
273- vectorEffect = "non-scaling-stroke"
274- />
275- < path
276- className = { `workflow-pulse workflow-pulse--${ lane . lane } ` }
277- d = { workflowLanePath ( lane . offset ) }
282+ d = {
283+ surface === "hero"
284+ ? workflowLanePath ( lane . offset , 1150 , 450 )
285+ : workflowLanePath ( lane . offset )
286+ }
278287 pathLength = "1"
279288 vectorEffect = "non-scaling-stroke"
280289 />
290+ { surface === "card" && (
291+ < path
292+ className = { `workflow-pulse workflow-pulse--${ lane . lane } ` }
293+ d = { workflowLanePath ( lane . offset ) }
294+ pathLength = "1"
295+ vectorEffect = "non-scaling-stroke"
296+ />
297+ ) }
281298 </ g >
282299 ) ) }
283300 </ svg >
@@ -322,48 +339,68 @@ const LifeRect = ({
322339 />
323340) ;
324341
342+ // Hero copies sit 13 columns apart: a three-column dead gap between 10-column
343+ // boards, so the frozen generation-0 frames never read as one merged board.
344+ const DYNAMIC_APPS_HERO_COLUMNS = [ 0 , 13 , 26 , 39 ] as const ;
345+
325346const DynamicAppsMotif = ( { surface } : Pick < ProductMotifProps , "surface" > ) => (
326347 < svg
327348 className = "h-full w-full"
328- viewBox = "0 0 320 400"
349+ viewBox = { surface === "hero" ? HERO_FIELD_VIEWBOX : CARD_VIEWBOX }
329350 preserveAspectRatio = { surface === "hero" ? "xMidYMid slice" : "none" }
330351 >
331- { lifeBoardCells . map ( ( cell ) => (
332- < LifeRect key = { `${ cell . col } -${ cell . row } ` } cell = { cell } />
352+ { ( surface === "hero" ? DYNAMIC_APPS_HERO_COLUMNS : [ 0 ] ) . map ( ( colShift ) => (
353+ < g key = { colShift } >
354+ { lifeBoardCells . map ( ( cell ) => (
355+ < LifeRect
356+ key = { `${ cell . col } -${ cell . row } ` }
357+ cell = { cell }
358+ colOffset = { colShift }
359+ />
360+ ) ) }
361+ < g className = "dynamic-app-glider" >
362+ { gliderCells . map ( ( cell ) => (
363+ < LifeRect
364+ key = { `glider-${ cell . col } -${ cell . row } ` }
365+ cell = { cell }
366+ colOffset = { GLIDER_ORIGIN . col + colShift }
367+ rowOffset = { GLIDER_ORIGIN . row }
368+ />
369+ ) ) }
370+ </ g >
371+ </ g >
333372 ) ) }
334- < g className = "dynamic-app-glider" >
335- { gliderCells . map ( ( cell ) => (
336- < LifeRect
337- key = { `glider-${ cell . col } -${ cell . row } ` }
338- cell = { cell }
339- colOffset = { GLIDER_ORIGIN . col }
340- rowOffset = { GLIDER_ORIGIN . row }
341- />
342- ) ) }
343- </ g >
344373 </ svg >
345374) ;
346375
376+ // The circle lattice is periodic at 320 in x (80/72 staggered grid), so
377+ // hero copies at 320-unit steps continue the cluster seamlessly.
378+ const AGENTOS_HERO_COLUMNS = [ 0 , 320 , 640 , 960 , 1280 ] as const ;
379+
347380const AgentOSMotif = ( { surface } : Pick < ProductMotifProps , "surface" > ) => (
348381 < svg
349382 className = "h-full w-full"
350- viewBox = "0 0 320 400"
383+ viewBox = { surface === "hero" ? HERO_FIELD_VIEWBOX : CARD_VIEWBOX }
351384 preserveAspectRatio = { surface === "hero" ? "xMidYMid slice" : "none" }
352385 >
353- { agentOSCircles . map ( ( circle ) => (
354- < circle
355- key = { `${ circle . cx } -${ circle . cy } ` }
356- className = { `agentos-circle agentos-circle--${ circle . direction } agentos-circle--${ circle . tone } ` }
357- style = {
358- {
359- "--agentos-circle-delay" : `${ circle . delay } ms` ,
360- } as CSSProperties
361- }
362- cx = { circle . cx }
363- cy = { circle . cy }
364- r = "62"
365- vectorEffect = "non-scaling-stroke"
366- />
386+ { ( surface === "hero" ? AGENTOS_HERO_COLUMNS : [ 0 ] ) . map ( ( xShift ) => (
387+ < g key = { xShift } >
388+ { agentOSCircles . map ( ( circle ) => (
389+ < circle
390+ key = { `${ circle . cx } -${ circle . cy } ` }
391+ className = { `agentos-circle agentos-circle--${ circle . direction } agentos-circle--${ circle . tone } ` }
392+ style = {
393+ {
394+ "--agentos-circle-delay" : `${ circle . delay } ms` ,
395+ } as CSSProperties
396+ }
397+ cx = { circle . cx + xShift }
398+ cy = { circle . cy }
399+ r = "62"
400+ vectorEffect = "non-scaling-stroke"
401+ />
402+ ) ) }
403+ </ g >
367404 ) ) }
368405 </ svg >
369406) ;
0 commit comments