feat(persona-room): Phase 2 room shell — designed self-contained page - #2237
Conversation
Living-doc room (plan 07_...md), Phase 2: a self-contained, host-agnostic static page rendering the persona (Phase-1 content model). Editorial treatment: - Dual-frequency palette (warm=beats, cool=code — 'same person, different frequency'), theme-aware light+dark, responsive. - Orbitron display (website design language) inlined as base64 woff2 (11.8KB) — no external font fetch; works on any host + offline. - Waveform/signal hero motif (canvas, reduced-motion aware) evoking BPM/Geometry Bus; scroll reveals. - Sections: plain-language lead (employer-first), highlights, skills clusters, featured (filled links + investor/soundcloud TODOs), PMOVES→employer table, automation fabric, Remotion + PreTeXt 'coming' slots, closer/connect. Deployable behind the #2221 Traefik edge (non-CF). Preview rendered + reviewed. Next: Phase 3 Remotion walkthrough, Phase 4 PreTeXt panels, Phase 5 host cutover. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe persona room landing page is implemented as a self-contained HTML document with responsive themed styling, structured content sections, and client-side theme toggling, scroll reveals, and an animated hero waveform. ChangesPersona room page
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant PersonaPage
participant IntersectionObserver
participant HeroWaveCanvas
Visitor->>PersonaPage: Click theme button
PersonaPage->>PersonaPage: Toggle data-theme
IntersectionObserver->>PersonaPage: Observe reveal elements
PersonaPage->>PersonaPage: Apply .in on intersection
PersonaPage->>HeroWaveCanvas: Resize and animate waveform
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 695309ebf5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| <footer><div class="wrap"> | ||
| <span>© 2026 Russell Richardson · CATACLYSM STUDIOS INC</span> | ||
| <span>Rendered from <code>pmoves/rooms/persona/persona.json</code> · a room on pmoves.ai</span> |
There was a problem hiding this comment.
Render the page from the canonical persona model
The page claims to be rendered from persona.json, but the inspected script only handles theme switching, reveal effects, and the canvas; every identity, highlight, skill, and link is duplicated as literal HTML. Consequently, updates to the declared single source of truth will leave the published room stale unless every value is manually synchronized. Add a generation/rendering step or load the model at runtime rather than presenting this snapshot as model-backed.
Useful? React with 👍 / 👎.
| .reveal{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s ease} | ||
| .reveal.in{opacity:1;transform:none} |
There was a problem hiding this comment.
Keep room content visible before JavaScript enhancement
When JavaScript is disabled, blocked by a host CSP, or fails before the IntersectionObserver setup completes, every section carrying class="reveal" remains permanently transparent; only users with reduced-motion enabled bypass this rule. Because all six substantive sections use that class, affected visitors receive essentially only the hero and footer. Default the sections to visible and apply the hidden state only after JavaScript has initialized.
Useful? React with 👍 / 👎.
| --ground:#f5f4ef; --panel:#ffffff; --panel-2:#f0efe8; --line:#e0ddd2; | ||
| --ink:#15161c; --ink-dim:#4b4f5e; --ink-faint:#7c8092; | ||
| --warm:#c9761a; --warm-soft:#a75f13; --cool:#0f9c93; --cool-soft:#0b7c74; --focus:#0f9c93; |
There was a problem hiding this comment.
Raise light-theme contrast for small text
When light theme is selected, several normal-size foreground colors fail the 4.5:1 contrast threshold against the shipped light background: --cool is about 3.08:1, --warm about 3.13:1, and --ink-faint about 3.56:1. These variables are used for 11–14px section numbers, headings, links, table cells, eyebrow text, and footer text, making those elements difficult to read for low-vision users. Use darker light-theme accent and faint-text values while retaining the current decorative palette.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
pmoves/rooms/persona/index.html (2)
1-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider Open Graph/social preview tags for this outreach page.
There's no
og:title/og:description/og:imagehere; given this page is meant to be shared for job/collaboration outreach (per PR objectives), link previews on LinkedIn/X/Slack will fall back to generic rendering.<meta property="og:title" content="Russell Richardson (DARKXSIDE) — Founder, PMOVES.AI"> <meta property="og:description" content="Founder of PMOVES.AI and Applied AI Architect. Open to mission-aligned roles."> <meta property="og:type" content="profile">🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pmoves/rooms/persona/index.html` around lines 1 - 7, Add Open Graph metadata in the head of the persona page alongside the existing description meta tag: define og:title, og:description, and og:type with outreach-appropriate values, and include an og:image referencing the page’s share preview asset if one exists.
244-266: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winWaveform loop recomputes styles every frame and never pauses off-screen/hidden.
css()callsgetComputedStyle(root)twice perdraw()(Lines 252-253), and therAFloop (Line 265) runs indefinitely even when the tab is backgrounded. Caching the two colors (recompute only on theme toggle/resize) and pausing viavisibilitychangewould cut unnecessary work for a purely decorative background animation.Suggested optimization
+ var warmCol, coolCol; + function refreshColors(){ warmCol=css('--warm'); coolCol=css('--cool'); } function draw(){ var w=c.width,h=c.height; ctx.clearRect(0,0,w,h); - var waves=[{col:css('--warm'),amp:h*0.10,freq:2.1,ph:t*0.9,y:h*0.52}, - {col:css('--cool'),amp:h*0.13,freq:1.4,ph:-t*0.7,y:h*0.56}]; + var waves=[{col:warmCol,amp:h*0.10,freq:2.1,ph:t*0.9,y:h*0.52}, + {col:coolCol,amp:h*0.13,freq:1.4,ph:-t*0.7,y:h*0.56}]; ... } - size(); draw(); window.addEventListener('resize',function(){size();draw();}); - if(!reduce){ (function loop(){ t+=0.02; draw(); requestAnimationFrame(loop); })(); } + refreshColors(); size(); draw(); window.addEventListener('resize',function(){size();draw();}); + document.addEventListener('visibilitychange',function(){ if(!document.hidden){ refreshColors(); draw(); } }); + var running=false; + if(!reduce){ (function loop(){ if(document.hidden){ running=false; return; } t+=0.02; draw(); requestAnimationFrame(loop); })(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pmoves/rooms/persona/index.html` around lines 244 - 266, Optimize the waveform animation around draw(), css(), and the requestAnimationFrame loop by caching the --warm and --cool colors instead of calling getComputedStyle(root) on every frame, refreshing the cache on theme changes or resize as appropriate. Add visibilitychange handling to pause the animation while the document is hidden and resume it when visible, while preserving reduced-motion behavior and the existing rendering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pmoves/rooms/persona/index.html`:
- Around line 122-126: Ensure the main content remains visible when JavaScript
is unavailable or fails: update the reveal styling and IntersectionObserver flow
around the `.reveal` class so its hidden state has a non-JavaScript fallback,
while preserving the animation when the script runs. Apply the fix consistently
to all primary sections using `class="reveal"`.
- Line 2: Remove the hardcoded data-theme value from the html element and update
the theme initialization logic to choose localStorage’s saved theme first, then
the system prefers-color-scheme preference, with the existing dark theme as
fallback. Update the toggle handler to persist each theme change to localStorage
so the selection survives reloads, and remove the now-unreachable CSS guard that
depends on the missing data-theme attribute.
- Line 230: The persona page must either load, render, and validate
pmoves/rooms/persona/persona.json or stop claiming it is rendered from that
file; align the implementation and displayed source accordingly. Also update the
PMOVES roadmap and next-steps documents with this room/persona shipment, current
“_Last updated” timestamps, and the applicable local-check output or an
explicitly documented intentional skip.
---
Nitpick comments:
In `@pmoves/rooms/persona/index.html`:
- Around line 1-7: Add Open Graph metadata in the head of the persona page
alongside the existing description meta tag: define og:title, og:description,
and og:type with outreach-appropriate values, and include an og:image
referencing the page’s share preview asset if one exists.
- Around line 244-266: Optimize the waveform animation around draw(), css(), and
the requestAnimationFrame loop by caching the --warm and --cool colors instead
of calling getComputedStyle(root) on every frame, refreshing the cache on theme
changes or resize as appropriate. Add visibilitychange handling to pause the
animation while the document is hidden and resume it when visible, while
preserving reduced-motion behavior and the existing rendering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 864a59a8-12cd-4904-86bf-ea6865e9b878
📒 Files selected for processing (1)
pmoves/rooms/persona/index.html
| @@ -0,0 +1,269 @@ | |||
| <!doctype html> | |||
| <html lang="en" data-theme="dark"> | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prefers-color-scheme light theme is dead code; theme choice isn't persisted.
<html data-theme="dark"> is hardcoded on Line 2, so the guard on Line 28 (:root:not([data-theme])) can never match — the entire @media (prefers-color-scheme:light) block (Lines 27-33) never applies. Visitors with a light-mode OS preference still get the dark theme on first load, contradicting the "responsive light/dark themes" goal. Separately, the toggle handler (Lines 236-240) only mutates the attribute in memory — there's no localStorage read/write, so the choice resets to dark on every reload.
Proposed fix: initialize theme from storage/system preference in JS, and drop the hardcoded attribute
-<html lang="en" data-theme="dark">
+<html lang="en"> var root=document.documentElement, btn=document.getElementById('theme');
+ (function initTheme(){
+ var saved=localStorage.getItem('theme');
+ if(saved){ root.setAttribute('data-theme', saved); }
+ })();
btn.addEventListener('click',function(){
var cur=root.getAttribute('data-theme')||'dark';
- root.setAttribute('data-theme', cur==='dark'?'light':'dark');
+ var next=cur==='dark'?'light':'dark';
+ root.setAttribute('data-theme', next);
+ localStorage.setItem('theme', next);
});Also applies to: 22-33, 236-240
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/rooms/persona/index.html` at line 2, Remove the hardcoded data-theme
value from the html element and update the theme initialization logic to choose
localStorage’s saved theme first, then the system prefers-color-scheme
preference, with the existing dark theme as fallback. Update the toggle handler
to persist each theme change to localStorage so the selection survives reloads,
and remove the now-unreachable CSS guard that depends on the missing data-theme
attribute.
| .reveal{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s ease} | ||
| .reveal.in{opacity:1;transform:none} | ||
| @media (prefers-reduced-motion:reduce){ | ||
| html{scroll-behavior:auto}.reveal{opacity:1;transform:none;transition:none}.btn:hover,.card:hover{transform:none} | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
All main content is invisible without JavaScript.
Every primary section (Lines 155-216) carries class="reveal", and .reveal{opacity:0;...} (Lines 122-123) is only cleared by the IntersectionObserver in the script (Lines 242-243). If JS fails to load/execute for any reason (blocked, error, disabled), the entire body content — the whole point of this outreach page — stays permanently invisible with no fallback.
Proposed fix: noscript fallback
</style>
+<noscript><style>.reveal{opacity:1!important;transform:none!important}</style></noscript>
</head>Also applies to: 155-216, 242-243
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/rooms/persona/index.html` around lines 122 - 126, Ensure the main
content remains visible when JavaScript is unavailable or fails: update the
reveal styling and IntersectionObserver flow around the `.reveal` class so its
hidden state has a non-JavaScript fallback, while preserving the animation when
the script runs. Apply the fix consistently to all primary sections using
`class="reveal"`.
|
|
||
| <footer><div class="wrap"> | ||
| <span>© 2026 Russell Richardson · CATACLYSM STUDIOS INC</span> | ||
| <span>Rendered from <code>pmoves/rooms/persona/persona.json</code> · a room on pmoves.ai</span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether ROADMAP/NEXT_STEPS were touched alongside this file, and whether persona.json exists for drift comparison.
fd -a ROADMAP.md pmoves/docs
fd -a NEXT_STEPS.md pmoves/docs
fd -a persona.json pmoves/rooms/persona
git log --oneline -3 -- pmoves/docs/NEXT_STEPS.md pmoves/docs/PMOVES.AI\ PLANS/ROADMAP.md 2>/dev/nullRepository: POWERFULMOVES/PMOVES.AI
Length of output: 859
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repo status/stat =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat 2>/dev/null || true
echo
echo "== tracked relevant files =="
git ls-files 'pmoves/rooms/persona/*' 'pmoves/docs/PMOVES.AI PLANS/ROADMAP.md' 'pmoves/docs/PMOVES.AI PLANS/NEXT_STEPS.md' 'docs/NEXT_STEPS.md' 'docs/LOCAL_CI_CHECKS.md' | sort
echo
echo "== git status concise =="
git status --short --untracked-files=no 2>/dev/null || true
echo
echo "== persona html/footer/json structure =="
wc -l pmoves/rooms/persona/index.html pmoves/rooms/persona/persona.json
sed -n '215,235p' pmoves/rooms/persona/index.html
python3 - <<'PY'
import json
from pathlib import Path
p=Path("pmoves/rooms/persona/persona.json")
try:
data=json.loads(p.read_text())
print("persona.json keys:", sorted(data.keys())[:20], "top-level keys count", len(data))
for k,v in data.items():
if isinstance(v, dict):
print(f" {k}: keys={sorted(v.keys())[:20]}, type_dict")
elif isinstance(v, list):
print(f" {k}: len={len(v)}, type_list, sample={v[:3]}")
else:
print(f" {k}: {type(v).__name__}: {repr(v)[:200]}")
except Exception as e:
print("persona.json parse/read error:", repr(e))
PY
echo
echo "== search persona json fetch/render/process mentions =="
rg -n "persona\.json|fetch\\(|fetchJSON|load|render|persona" pmoves/rooms/persona/index.html pmoves/rooms/persona -g '*.{js,ts,html,json,md}' | head -200
echo
echo "== roadmap/next_updates snippets =="
for f in \
'pmoves/docs/PMOVES.AI PLANS/ROADMAP.md' \
'pmoves/docs/PMOVES.AI PLANS/NEXT_STEPS.md' \
'docs/NEXT_STEPS.md' \
'docs/LOCAL_CI_CHECKS.md'
do
echo "--- $f ---"
if [ -f "$f" ]; then
wc -l "$f"
rg -n "_Last updated|Last updated|persona|PMOVES.AI|check|LOCAL_CI_CHECKS|CI|room|persona" "$f" -A 3 -B 3 | head -200
else
echo "missing"
fi
done
echo
echo "== recent commits touching all relevant trees =="
git log --oneline -10 --all -- 'pmoves/rooms/*' 'pmoves/docs/*' 'docs/ROADMAP.md' 'docs/NEXT_STEPS.md' 'docs/LOCAL_CI_CHECKS.md'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 45553
Align the persona room source with its rendered claim and update the required PMOVES docs.
pmoves/rooms/persona/index.html declares itself as rendered from pmoves/rooms/persona/persona.json, but the page is hand-authored static HTML and contains no JSON load/render/validation, so future persona.json changes won’t be automatically reconciled. This also needs documentation/practical gating consistent with the pmoves/**/* guidelines: update pmoves/docs/PMOVES.AI PLANS/ROADMAP.md and pmoves/docs/PMOVES.AI PLANS/NEXT_STEPS.md for this room/persona shipment with _Last updated timestamps, and include the applicable local checks output or an intentional skip.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pmoves/rooms/persona/index.html` at line 230, The persona page must either
load, render, and validate pmoves/rooms/persona/persona.json or stop claiming it
is rendered from that file; align the implementation and displayed source
accordingly. Also update the PMOVES roadmap and next-steps documents with this
room/persona shipment, current “_Last updated” timestamps, and the applicable
local-check output or an explicitly documented intentional skip.
Source: Coding guidelines
Item 5 (living-doc room) — Phase 2: a self-contained, host-agnostic static page rendering the persona (Phase-1
persona.json).Preview: the rendered room (design + all sections) — https://claude.ai/code/artifact/bee111bb-6cdf-4ca2-8482-d51d90260e22
Design (editorial treatment)
Host-agnostic — deployable behind the #2221 Traefik edge (non-CF).
Next
Phase 3 Remotion walkthrough (a2ui renderer) · Phase 4 PreTeXt panels · Phase 5 host cutover to pmoves.ai.
🤖 Generated with Claude Code
Summary by CodeRabbit