From 4d3b3f3a0fe8d1f1f5a72fef7cc912518fa0db31 Mon Sep 17 00:00:00 2001 From: hshum Date: Mon, 1 Jun 2026 18:21:36 -0700 Subject: [PATCH] polish(scratchnode): handoff cascade + room-code copy flash Additive follow-up to the motion pass (0ec4c9d3). Two micro-interactions it did not cover, both on public/proto/home-v5.html and reusing the existing --motion-*/--ease-out tokens: - Room-code chip flashes a green "Copied" confirmation on copy, paired with the existing toast (text + colour, not colour alone). - NodeBench handoff overlay sections cascade up (nbSectionIn) after the panel slides in, with a prefers-reduced-motion guard. Leaves the public/private behaviour contract untouched. Verified: 7/7 e2e (output-contract + live-route-honesty), reduced-motion suppression, no horizontal overflow at 375px. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG/pages/proto-home-v5.md | 5 +++++ public/proto/home-v5.html | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG/pages/proto-home-v5.md b/CHANGELOG/pages/proto-home-v5.md index b92f6af6..ad0b96a7 100644 --- a/CHANGELOG/pages/proto-home-v5.md +++ b/CHANGELOG/pages/proto-home-v5.md @@ -6,3 +6,8 @@ Append-only lane for the ScratchNode live-event prototype and production static Centralized lightweight motion tokens and added a visual polish pass across the ScratchNode room: ambient backdrop, composer focus/private-mode cues, chat and answer reveals, Live Assist card choreography, and tactile Memory Wall note transitions. The change keeps the existing public/private behavior contract intact while making the demo feel more premium on desktop and mobile. **Commit**: `this commit`. **Author**: Homen Shum + Augment Agent. + +## 2026-06-01 — Follow-up: handoff cascade + room-code copy flash +Additive layer on top of the motion polish pass. Two micro-interactions that the prior pass did not cover: (1) the room-code chip now flashes a green "✓ Copied" confirmation on copy (paired with the existing toast — text + colour, not colour alone), and (2) the NodeBench handoff overlay sections cascade up after the panel slides in, so the "Now in NodeBench" moment feels composed rather than a hard cut. Both reuse the existing `--motion-*`/`--ease-out` tokens, ship `prefers-reduced-motion` guards, and leave the public/private behavior contract untouched. Verified: 7/7 e2e (output-contract + live-route-honesty), reduced-motion suppression, no mobile overflow. + +**Commit**: `this commit`. **Author**: Homen Shum + Claude. diff --git a/public/proto/home-v5.html b/public/proto/home-v5.html index 70049ade..4d16e1eb 100644 --- a/public/proto/home-v5.html +++ b/public/proto/home-v5.html @@ -124,9 +124,12 @@ min-height: 36px; padding: 6px 14px; border-radius: var(--r-sm); background: transparent; border: 1px solid var(--line); color: var(--accent); font-family: var(--mono); font-size: 12px; font-weight: 700; - letter-spacing: .18em; cursor: pointer; transition: border-color .12s; + letter-spacing: .18em; cursor: pointer; + transition: border-color var(--motion-fast) var(--ease-out), color var(--motion-fast) var(--ease-out), background var(--motion-fast) var(--ease-out); } .h-code:hover { border-color: var(--accent); } +/* Tactile "copied" confirmation — green flash + check, paired with the toast (text + colour, not colour alone). */ +.h-code.is-copied { color: var(--green); border-color: rgba(94,168,103,.5); background: rgba(94,168,103,.1); } .h-menu { width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-sm); border: 1px solid var(--line); background: transparent; color: var(--ink-muted); } .h-menu:hover { color: var(--ink); border-color: var(--ink-faint); } @media (max-width: 540px) { .h-menu { width: 44px; height: 44px; } .h-code { min-height: 44px; } } @@ -1178,6 +1181,15 @@ .nodebench-overlay .nb-close { margin-left: auto; background: none; border: 1px solid rgba(255,255,255,.1); color: #e8e6e3; padding: 6px 12px; border-radius: 6px; font-size: 12px; } .nodebench-overlay .nb-body { padding: 22px; max-width: 1100px; margin: 0 auto; width: 100%; } .nodebench-overlay .nb-section { margin-bottom: 24px; } +/* Cinematic handoff: after the overlay slides in, its sections cascade up so the + "Now in NodeBench" moment feels composed rather than a hard cut. */ +@keyframes nbSectionIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } } +.nodebench-overlay[data-visible="true"] .nb-body > .nb-section { animation: nbSectionIn var(--motion-slow) var(--ease-out) both; } +.nodebench-overlay[data-visible="true"] .nb-body > .nb-section:nth-child(1) { animation-delay: .1s; } +.nodebench-overlay[data-visible="true"] .nb-body > .nb-section:nth-child(2) { animation-delay: .18s; } +.nodebench-overlay[data-visible="true"] .nb-body > .nb-section:nth-child(3) { animation-delay: .26s; } +.nodebench-overlay[data-visible="true"] .nb-body > .nb-section:nth-child(n+4) { animation-delay: .32s; } +@media (prefers-reduced-motion: reduce) { .nodebench-overlay[data-visible="true"] .nb-body > .nb-section { animation: none; } } .nodebench-overlay .nb-section-head { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: rgba(255,255,255,.5); text-transform: uppercase; margin-bottom: 8px; } .nodebench-overlay .nb-card { padding: 14px; border-radius: 10px; background: rgba(255,255,255,.025); border: 1px solid rgba(255,255,255,.06); } .nodebench-overlay .nb-card + .nb-card { margin-top: 8px; } @@ -2899,11 +2911,26 @@

Keyboard shortcuts

return; } if (navigator.clipboard) { - navigator.clipboard.writeText(text).then(function(){ toast('Copied join link', EVENT_ROOM_CODE + ' · paste in any chat.'); }); + navigator.clipboard.writeText(text).then(function(){ toast('Copied join link', EVENT_ROOM_CODE + ' · paste in any chat.'); flashRoomCode(); }); } else { toast('Room: ' + EVENT_ROOM_CODE, 'Share this URL with attendees.'); } } +// Brief inline "copied" confirmation on the room-code chip (check + green flash), then restore. +// Pairs with the toast — text + colour, not colour alone. +function flashRoomCode() { + var btn = document.getElementById('sn-room-code-btn'); + if (!btn || btn.dataset.flashing === '1') return; + var original = btn.textContent; + btn.dataset.flashing = '1'; + btn.classList.add('is-copied'); + btn.textContent = '✓ Copied'; + setTimeout(function() { + btn.classList.remove('is-copied'); + btn.textContent = original; + delete btn.dataset.flashing; + }, 1300); +} // ── Menu ── // Track the element that opened each overlay so we can return focus on close