diff --git a/docs/design/ui-contract/20260717-run-theater-friction/after-landing-example-link.png b/docs/design/ui-contract/20260717-run-theater-friction/after-landing-example-link.png new file mode 100644 index 000000000..79117573a Binary files /dev/null and b/docs/design/ui-contract/20260717-run-theater-friction/after-landing-example-link.png differ diff --git a/docs/design/ui-contract/20260717-run-theater-friction/after-one-click-journey-destination.png b/docs/design/ui-contract/20260717-run-theater-friction/after-one-click-journey-destination.png new file mode 100644 index 000000000..ed2855944 Binary files /dev/null and b/docs/design/ui-contract/20260717-run-theater-friction/after-one-click-journey-destination.png differ diff --git a/docs/design/ui-contract/20260717-run-theater-friction/after-run-thread-complete.png b/docs/design/ui-contract/20260717-run-theater-friction/after-run-thread-complete.png new file mode 100644 index 000000000..d5467388a Binary files /dev/null and b/docs/design/ui-contract/20260717-run-theater-friction/after-run-thread-complete.png differ diff --git a/docs/design/ui-contract/20260717-run-theater-friction/manifest.json b/docs/design/ui-contract/20260717-run-theater-friction/manifest.json new file mode 100644 index 000000000..eee302179 --- /dev/null +++ b/docs/design/ui-contract/20260717-run-theater-friction/manifest.json @@ -0,0 +1,12 @@ +{ + "schema": "nodebench-ui-contract-v1", + "capturedAt": "2026-07-17T23:05:00Z", + "slice": "run-theater-friction", + "app": "nodebench-ai (decision workspace, /redesign/chat)", + "notes": "Two changes. (1) Run theater: the research checklist's stage dots are joined by a vertical ember thread - solid behind completed stages, actively drawing beside the running stage, dashed-faint ahead - progress reads as the thread being spun, same motif as the landing constellation and answer ticks; reduced-motion gets the static drawn state. (2) Friction: 'See a real answered example, receipts included' link under the starter chips routes one click, zero auth, zero spend to the persisted production receipt /r/36imyvtrf0d3, which carries the real answer INTO a live composer (continued-conversation) - value before identity. after-one-click-journey-destination.png is the actual click-through destination captured from the landing. No before pairs: the link did not exist and the thread joins previously-unconnected dots; landing before-state is in 20260717-ember-landing/.", + "pages": [ + { "route": "/redesign/chat", "viewport": { "width": 1440, "height": 900 }, "theme": "dark", "after": "after-landing-example-link.png" }, + { "route": "/redesign/chat?qaState=answer", "viewport": { "width": 1440, "height": 900 }, "theme": "dark", "after": "after-run-thread-complete.png" }, + { "route": "/redesign/chat/r/36imyvtrf0d3", "viewport": { "width": 1440, "height": 900 }, "theme": "dark", "after": "after-one-click-journey-destination.png" } + ] +} diff --git a/src/features/redesign/agent-workspace.css b/src/features/redesign/agent-workspace.css index 67efd31c5..0a9f5d6a2 100644 --- a/src/features/redesign/agent-workspace.css +++ b/src/features/redesign/agent-workspace.css @@ -444,6 +444,83 @@ } } +/* ── Run theater — the thread being spun, stage by stage ───────────────── + The research checklist's stage dots are joined by a vertical ember thread: + solid behind completed stages, actively drawing beside the running stage, + dashed-faint ahead of it. Progress reads as the thread advancing — the + same motif as the landing constellation and the answer ticks. */ +[data-redesign] .rd-run-thread li { + position: relative; +} + +[data-redesign] .rd-run-thread li::before { + content: ""; + position: absolute; + left: 7.5px; + top: calc(-50% + 5px); + width: 1px; + height: calc(100% - 4px); + pointer-events: none; +} + +[data-redesign] .rd-run-thread li:first-child::before { + display: none; +} + +[data-redesign] .rd-run-thread li[data-state="done"]::before { + background: var(--rd-accent); + opacity: 0.45; +} + +[data-redesign] .rd-run-thread li[data-state="running"]::before { + background: linear-gradient(180deg, var(--rd-accent), transparent); + transform-origin: top center; + animation: rd-thread-spin 1.4s var(--rd-ease-settle) infinite; +} + +[data-redesign] .rd-run-thread li[data-state="waiting"]::before { + background: repeating-linear-gradient( + 180deg, + var(--rd-ink-mute) 0 3px, + transparent 3px 7px + ); + opacity: 0.25; +} + +@keyframes rd-thread-spin { + 0% { transform: scaleY(0); opacity: 0.9; } + 75% { transform: scaleY(1); opacity: 0.9; } + 100% { transform: scaleY(1); opacity: 0.35; } +} + +@media (prefers-reduced-motion: reduce) { + [data-redesign] .rd-run-thread li[data-state="running"]::before { + animation: none; + transform: scaleY(1); + } +} + +/* Quiet proof link beneath the starter chips — one click to a real persisted + answer with receipts, no account, no spend. */ +[data-redesign] .rd-chat-empty__example { + position: relative; + z-index: 1; + align-self: center; + margin-top: 18px; + color: var(--rd-ink-mute); + font-size: 13px; + text-decoration: none; + border-bottom: 1px solid transparent; + transition: color var(--rd-dur-quick) var(--rd-ease-settle), + border-color var(--rd-dur-quick) var(--rd-ease-settle); +} + +[data-redesign] .rd-chat-empty__example:hover, +[data-redesign] .rd-chat-empty__example:focus-visible { + color: var(--rd-accent-strong); + border-bottom-color: var(--rd-accent); +} + /* Narrow viewports: quieter constellation, and the two low side nodes give their space back to the stacked chips. */ @media (max-width: 760px) { diff --git a/src/features/redesign/components/ChatEmptyState.tsx b/src/features/redesign/components/ChatEmptyState.tsx index 321694788..b3e78bb9d 100644 --- a/src/features/redesign/components/ChatEmptyState.tsx +++ b/src/features/redesign/components/ChatEmptyState.tsx @@ -167,6 +167,15 @@ export function ChatEmptyState({ onPick, starters = STARTERS }: ChatEmptyStatePr ))} + {/* Value before identity: a real, persisted production answer — prompt, + evidence rows, model, cost, deterministic hash — reachable in one + click with no account and no spend. The route carries the answer + into a live composer, so the example IS a usable starting point. + Swap the hash when a stronger showcase receipt is minted. */} + + See a real answered example, receipts included → + + ); } diff --git a/src/features/research/LiveResearchChecklist.tsx b/src/features/research/LiveResearchChecklist.tsx index e774e9bc7..98a045ee5 100644 --- a/src/features/research/LiveResearchChecklist.tsx +++ b/src/features/research/LiveResearchChecklist.tsx @@ -234,7 +234,11 @@ export function LiveResearchChecklist({ {warnings.length > 0 ? ` · ${warnings.length} flagged` : ""} + {/* rd-run-thread: on the redesign surface an ember thread connects the + stage dots and is drawn downward by progress (agent-workspace.css). + Inert everywhere else — the class has no styles outside [data-redesign]. */}