diff --git a/README.md b/README.md
index 943918f5b..98c12d745 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-[](docs/user-guide.md)
+[](docs/user-guide.md)
@@ -67,7 +67,7 @@ Every WiFi router already fills your space with radio waves. When people move, b
Breathing and vital signals
-Pose research and limitations
+Pose research and limitations
Edge sensor mesh
diff --git a/assets/readme/feature-pose-spatial-v6.svg b/assets/readme/feature-pose-spatial-v6.svg
new file mode 100644
index 000000000..9340c5627
--- /dev/null
+++ b/assets/readme/feature-pose-spatial-v6.svg
@@ -0,0 +1,47 @@
+Pose research Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits. RUVIEW / 03 Pose research Train and compare body keypoints. Live accuracy differs from benchmarks. READ THE EVIDENCE
diff --git a/assets/readme/hero-spatial-v6.svg b/assets/readme/hero-spatial-v6.svg
new file mode 100644
index 000000000..55de901fc
--- /dev/null
+++ b/assets/readme/hero-spatial-v6.svg
@@ -0,0 +1,47 @@
+RuView: invisible signals, visible possibility Animated conceptual RF room with an anatomical human silhouette, moving scan plane and propagating waves. Illustration only, not a sensor measurement. RUVIEW / SPATIAL INTELLIGENCE Invisible signals. Visible possibility. Explore the space between sensing and understanding. WiFi sensing. Edge models. Local automation. Enter the field RF SENSING / OPEN RESEARCH / EDGE COMPUTE CONCEPTUAL RF FIELD
diff --git a/assets/readme/manifest.json b/assets/readme/manifest.json
index 65fbb38ff..fd65b4d1a 100644
--- a/assets/readme/manifest.json
+++ b/assets/readme/manifest.json
@@ -4,7 +4,7 @@
"motion": "Decorative CSS; prefers-reduced-motion supported; no script or remote dependencies",
"assets": [
{
- "file": "assets/readme/hero-spatial-v5.svg",
+ "file": "assets/readme/hero-spatial-v6.svg",
"title": "RuView: invisible signals, visible possibility",
"target": "docs/user-guide.md",
"width": 1200,
@@ -67,7 +67,7 @@
"height": 276
},
{
- "file": "assets/readme/feature-pose-spatial-v5.svg",
+ "file": "assets/readme/feature-pose-spatial-v6.svg",
"title": "Pose research",
"target": "docs/benchmarks/pose-estimation-cog.md",
"width": 580,
diff --git a/docs/readme-visual-system.md b/docs/readme-visual-system.md
index f7d2f8050..a5235bf63 100644
--- a/docs/readme-visual-system.md
+++ b/docs/readme-visual-system.md
@@ -54,8 +54,15 @@ The overview and RF room are conceptual illustrations, not live measurements or
Sources: `docs/user-guide.md`, `docs/benchmarks/pose-estimation-cog.md`, `v2/crates/wifi-densepose-signal/README.md`, `firmware/esp32-csi-node/README.md`, `docs/integrations/home-assistant.md`, and `harness/ruview/README.md`.
-The hero and pose card use `-spatial-v5.svg`: an original anatomical human
+The hero and pose card use `-spatial-v6.svg`: an original anatomical human
silhouette with shaded torso, head, hands and feet, overlapping limbs, surface
-contours and amber joint markers. Subtle limb motion pivots behind the torso
-to preserve joint continuity. This is vector artwork, not a real body scan or
+contours and amber joint markers. A 1.6-second gait drives opposing arm and leg swings, weight shifts and ground
+shadow compression. An 8-second CSS perspective turn and moving surface light
+give the anatomical illustration depth. Each limb pivots at its own shoulder
+or hip. The effect remains a projected SVG illustration, not a skinned WebGL
+model. The stylesheet disables gait and perspective movement for reduced motion.
+Chrome 154 validation confirmed the 3D transform, opposing limb transforms,
+animation in an HTML image, and a frozen standalone SVG under reduced motion.
+Reduced-motion emulation did not freeze the embedded image in that test; image
+preference propagation remains a browser-dependent limitation. This is vector artwork, not a real body scan or
measured pose reconstruction. Other assets retain their v4 filenames.
diff --git a/scripts/render-readme-visuals.py b/scripts/render-readme-visuals.py
index 7a574bae9..7e36839f0 100644
--- a/scripts/render-readme-visuals.py
+++ b/scripts/render-readme-visuals.py
@@ -26,13 +26,13 @@ def icon(kind,x,y,scale=1):
elif kind=='pose':
# Authored anatomical mesh silhouette; a conceptual figure, not captured pose data.
s+=' '
- s+=''
+ s+=''
# Far arm and leg provide occlusion and depth.
- s+=' '
- s+=' '
- s+=' '
+ s+=' '
+ s+=' '
+ s+=' '
# Near arm: shoulder, upper arm, elbow, forearm and closed hand.
- s+=' '
+ s+=' '
# Shoulder and chest taper into abdomen and pelvis.
s+=' '
# Three-quarter head with jaw, nose and ear contour.
@@ -40,10 +40,12 @@ def icon(kind,x,y,scale=1):
s+=' '
# Surface contours make volume legible at README scale.
s+=path('M-9 -23Q-2 -18 10 -23M-7 -15Q0 -11 9 -16M-6 -8Q1 -5 8 -9M-7 1Q1 5 9 1M0 -26L2 9M-4 -40Q0 -42 7 -40M2 -32L6 -34',LIME,.55,extra='opacity=".65"')
- s+=path('M-5 -45Q1 -49 6 -46M-10 -22L-6 -8M9 17L17 28M-12 20L-20 42',C,.7,extra='opacity=".8"')
+ s+=path('M-5 -45Q1 -49 6 -46M-10 -22L-6 -8',C,.7,extra='opacity=".8"')
+ s+=' '
# Joint markers remain attached to the anatomical surface.
for px,py in [(-9,-24),(10,-23),(1,-12),(2,9)]:
s+=circle(px,py,3.5,AMBER,extra='stroke-opacity=".25"')+circle(px,py,1.5,AMBER,extra=f'fill="{AMBER}" class="spark"')
+ s+=' '
s+=path('M-42 -42V-53H-30M30 -53H42V-42M42 45V55H30M-30 55H-42V45',BLUE,.8)
elif kind in ['vitals','models']:
for j in range(3):
@@ -67,6 +69,9 @@ def icon(kind,x,y,scale=1):
s+=circle(0,0,15,LIME)+path('M-6 0L-1 5L8 -6',LIME,2)
else:s+=path('M-35 0L0 -25L35 0L0 25Z',C,2)
base=' '
+ if kind=='pose':
+ base=' '
+ return f'{base}{s} '
return f'{base}{s} '
def spatial(x,y,scale=1):
@@ -105,6 +110,22 @@ text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,
@keyframes layer{50%{transform:translateY(-5px)}}
@keyframes cursor{0%,100%{transform:translateX(-35px);opacity:.3}50%{transform:translateX(35px);opacity:1}}
+
+.humanTurn{transform-box:fill-box;transform-origin:center 85%;animation:humanTurn 8s ease-in-out infinite}
+.humanBody{animation:humanBody 1.6s ease-in-out infinite}
+.armFar{transform-origin:-9px -24px;animation:armSwing 1.6s ease-in-out infinite}
+.armNear{transform-origin:10px -23px;animation:armSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
+.legFar{transform-origin:-3px 9px;animation:legSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
+.legNear{transform-origin:2px 9px;animation:legSwing 1.6s ease-in-out infinite}
+.humanLight{animation:humanLight 8s ease-in-out infinite}
+.gaitShadow{transform-box:fill-box;transform-origin:center;animation:gaitShadow 1.6s ease-in-out infinite}
+@keyframes humanTurn{0%,100%{transform:perspective(450px) rotateY(-24deg) rotateZ(-2deg)}50%{transform:perspective(450px) rotateY(24deg) rotateZ(2deg)}}
+@keyframes humanBody{0%,50%,100%{transform:translateY(0)}25%,75%{transform:translateY(-2px)}}
+@keyframes armSwing{0%,100%{transform:rotate(-17deg)}50%{transform:rotate(17deg)}}
+@keyframes legSwing{0%,100%{transform:rotate(-12deg)}50%{transform:rotate(12deg)}}
+@keyframes humanLight{0%,100%{opacity:.1}50%{opacity:.45}}
+@keyframes gaitShadow{0%,50%,100%{transform:scaleX(1);opacity:.7}25%,75%{transform:scaleX(.85);opacity:.45}}
+
@keyframes spark{50%{opacity:.25}}
@keyframes flow{to{stroke-dashoffset:-136}}
@keyframes breathe{50%{opacity:.4}}
@@ -116,10 +137,10 @@ text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,
def frame(w,h,title,desc,body):
value=_frame(w,h,title,desc,body)
motion=MOTION
- for name in ['float','flow','breathe','trace','scan','orbit','spark','wave','sweep','limb','stride','layer','cursor']:
+ for name in ['float','flow','breathe','trace','scan','orbit','spark','wave','sweep','limb','stride','layer','cursor','humanTurn','humanBody','armFar','armNear','legFar','legNear','humanLight','gaitShadow']:
if f'class="{name}"' not in body:
motion=re.sub(r'\.'+name+r'\{[^}]*\}', '', motion)
- for name in ['float','flow','breathe','trace','scan','spark','wave','sweep','limb','layer','cursor']:
+ for name in ['float','flow','breathe','trace','scan','spark','wave','sweep','limb','layer','cursor','humanTurn','humanBody','armSwing','legSwing','humanLight','gaitShadow']:
if f'animation:{name} ' not in motion:
marker='@keyframes '+name+'{'; start=motion.find(marker)
if start>=0:
@@ -141,7 +162,7 @@ def frame(w,h,title,desc,body):
manifest=[]
def save(name,w,h,title,desc,body,target):
- name+='-spatial-v5' if name in ['hero','feature-pose'] else '-spatial-v4'
+ name+='-spatial-v6' if name in ['hero','feature-pose'] else '-spatial-v4'
s=frame(w,h,title,desc,body);raw=(s+'\n').encode('utf-8');parse_svg(raw);(OUT/(name+'.svg')).write_bytes(raw);manifest.append({'file':'assets/readme/'+name+'.svg','title':title,'target':target,'width':w,'height':h})
# Cinematic RF stage: authored projected planes, not a live measurement.
s=text(48,45,'RUVIEW / SPATIAL INTELLIGENCE',12,C,extra='class="mono"')