nothumanallowed 13.5.19 → 13.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.5.19",
3
+ "version": "13.5.21",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.5.19';
8
+ export const VERSION = '13.5.21';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -2620,8 +2620,11 @@ function renderSettings(el) {
2620
2620
  ]) +
2621
2621
  '<div class="card" style="margin-top:16px"><div class="card__title">Google Account</div>' +
2622
2622
  '<div style="font-size:11px;color:var(--dim);margin-bottom:8px">Connect Gmail, Calendar, Drive, Contacts, and Tasks. Opens a browser window for Google sign-in.</div>' +
2623
- (settingsData.hasGoogle ? '<div style="color:var(--green);font-size:12px;margin-bottom:8px">\\u2705 Connected</div>' : '') +
2623
+ (settingsData.hasGoogle ? '<div style="color:var(--green);font-size:12px;margin-bottom:8px">&#9989; Connected</div>' : '') +
2624
+ '<div style="display:flex;gap:8px;flex-wrap:wrap">' +
2624
2625
  '<button onclick="connectGoogle()" style="background:var(--green3);color:var(--bg);padding:8px 20px;border-radius:var(--r);font-weight:700;font-size:12px;cursor:pointer;border:none">' + (settingsData.hasGoogle ? 'Reconnect Google' : 'Connect Google') + '</button>' +
2626
+ (settingsData.hasGoogle ? '<button onclick="disconnectGoogle()" style="background:var(--red3,#7f1d1d);color:#fca5a5;padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px;cursor:pointer;border:1px solid var(--red,#ef4444)">Disconnect Google</button>' : '') +
2627
+ '</div>' +
2625
2628
  '<div id="googleStatus" style="margin-top:8px;font-size:10px;color:var(--dim)"></div>' +
2626
2629
  '</div>' +
2627
2630
  '</div>';
@@ -2637,6 +2640,17 @@ function connectGoogle() {
2637
2640
  if (s) { s.textContent = 'Error: ' + e.message; s.style.color = 'var(--red)'; }
2638
2641
  });
2639
2642
  }
2643
+ function disconnectGoogle() {
2644
+ var s = document.getElementById('googleStatus');
2645
+ if (s) s.textContent = 'Disconnecting...';
2646
+ apiPost('/api/google/revoke', {}).then(function() {
2647
+ if (s) { s.textContent = 'Google account disconnected.'; s.style.color = 'var(--dim)'; }
2648
+ setTimeout(function(){ location.reload(); }, 1200);
2649
+ }).catch(function() {
2650
+ // Fallback: delete token file via CLI hint
2651
+ if (s) { s.textContent = 'Run: nha google revoke'; s.style.color = 'var(--amber)'; }
2652
+ });
2653
+ }
2640
2654
 
2641
2655
  function settingsSection(id, title, desc, fields) {
2642
2656
  var h = '<form class="card" style="margin-bottom:16px" id="settings-' + id + '" onsubmit="event.preventDefault();saveSettingsSection(\\x27' + id + '\\x27)">' +
@@ -3238,7 +3252,7 @@ function renderSidebar() {
3238
3252
  \x27<div class="sidebar__brand">\x27+
3239
3253
  \x27<button class="sidebar__close" onclick="closeSidebar()" title="Close menu">&times;</button>\x27+
3240
3254
  \x27<div style="display:flex;align-items:center;gap:8px">\x27+
3241
- \x27<div class="sidebar__brand-name">NHA</div>\x27+
3255
+ \x27<div class="sidebar__brand-name">NHA <span style="font-size:11px;font-weight:500;opacity:.7;letter-spacing:.5px">3rdArm</span></div>\x27+
3242
3256
  \x27<span id="wsIndicator" style="color:var(--dim);font-size:8px" title="Daemon WebSocket">&#9679;</span>\x27+
3243
3257
  \x27</div>\x27+
3244
3258
  \x27<div id="sidebarPageTitle" style="font-size:11px;color:var(--bright);margin-top:4px;font-weight:600">\x27+t(\x27nav_dashboard\x27)+\x27</div>\x27+
@@ -3311,6 +3325,7 @@ var studioAbortController = null;
3311
3325
  var parlActiveAgent = null; // active agent label during parliament streaming
3312
3326
  var parlDoneAgents = {}; // set of completed agent labels during parliament
3313
3327
  var _parlPersistHtml = null; // persists parliament block HTML across tab navigations
3328
+ var _PARL_STAMP = '<!--nha-parl-v13.5.21-->';
3314
3329
 
3315
3330
  function stopStudio() {
3316
3331
  if (!studioState.running) return;
@@ -3931,10 +3946,14 @@ function renderStudioNodes() {
3931
3946
  : (isActive ? \x27\u2022\u2022\u2022 lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27)));
3932
3947
  var bubbleBg = isOrch ? \x27rgba(99,102,241,.15)\x27 : (isActive ? \x27rgba(99,102,241,.12)\x27 : (isDone ? \x27rgba(0,0,0,.08)\x27 : \x27rgba(239,68,68,.12)\x27));
3933
3948
  var glowBox = isActive ? (\x270 0 0 3px \x27+accentColor+\x2744,0 8px 24px \x27+accentColor+\x2733\x27) : (isDone ? (\x270 0 0 2px rgba(0,0,0,.25)\x27) : \x27none\x27);
3949
+ // orchWalkClass goes on the CHAR only, not the whole station card
3934
3950
  var orchWalkClass = (isOrch && hasActive) ? \x27 iso-orch-walking\x27 : (isOrch && doneCount===totalCount&&totalCount>0 ? \x27 iso-orch-done\x27 : \x27\x27);
3935
- var charHtml = isoCharSvg({emojiIdx: isOrch ? 99 : emojiIdx, isActive: isActive, isDone: isDone, scale: 1.1, accentColor: accentColor});
3951
+ var charHtml = \x27<div class="iso-char-mover\x27+orchWalkClass+\x27">\x27+isoCharSvg({emojiIdx: isOrch ? 99 : emojiIdx, isActive: isActive, isDone: isDone, scale: 1.1, accentColor: accentColor})+\x27</div>\x27;
3936
3952
  var clickAttr = isOrch ? \x27\x27 : (\x27data-agent-label="\x27+esc(label)+\x27" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))"\x27);
3937
- return \x27<div class="iso-station\x27+orchWalkClass+\x27" \x27+clickAttr+\x27 style="box-shadow:\x27+glowBox+\x27;border-color:\x27+accentColor+\x27;transition:box-shadow .4s">\x27+
3953
+ // Flying doc emoji when active
3954
+ var flyDoc = isActive ? \x27<div class="iso-fly-doc" style="color:\x27+accentColor+\x27">\x27+String.fromCodePoint(0x1F4C4)+\x27</div>\x27 : \x27\x27;
3955
+ return \x27<div class="iso-station" \x27+clickAttr+\x27 style="box-shadow:\x27+glowBox+\x27;border-color:\x27+accentColor+\x27;transition:box-shadow .4s">\x27+
3956
+ flyDoc+
3938
3957
  \x27<div class="iso-bubble\x27+(isActive?\x27 iso-bubble--active\x27:\x27\x27)+\x27" style="border-color:\x27+accentColor+\x27;color:\x27+accentColor+\x27;background:\x27+bubbleBg+\x27;visibility:\x27+(bubbleText||isOrch?\x27visible\x27:\x27hidden\x27)+\x27">\x27+esc(bubbleText)+\x27</div>\x27+
3939
3958
  \x27<div class="iso-tool-badge">\x27+toolEmoji+\x27</div>\x27+
3940
3959
  charHtml+
@@ -4654,7 +4673,7 @@ async function runStudio() {
4654
4673
  if (!parlBlockBuilt || !pb.innerHTML.trim()) {
4655
4674
  parlBlockBuilt = true;
4656
4675
 
4657
- // ── Seat card builder — emoji + bubble + name ──────────────────────
4676
+ // ── FREE agent builder — no box, just floating emoji+name ─────────
4658
4677
  function buildSeat(prop, seatIdx) {
4659
4678
  var lbl = prop.label || prop.agent;
4660
4679
  var safeLbl = lbl.replace(new RegExp('[^a-zA-Z0-9_-]','g'),\x27_\x27);
@@ -4663,12 +4682,11 @@ async function runStudio() {
4663
4682
  void seatIdx;
4664
4683
  return \x27<div class="br-seat" id="brseat_\x27+safeLbl+\x27" data-lbl="\x27+esc(lbl)+\x27">\x27+
4665
4684
  \x27<div class="br-bubble" id="brbubble_\x27+safeLbl+\x27" style="display:none"></div>\x27+
4666
- \x27<div class="br-char">\x27+agentEmoji+\x27</div>\x27+
4685
+ \x27<div class="br-char" id="brchar_\x27+safeLbl+\x27">\x27+agentEmoji+\x27</div>\x27+
4667
4686
  \x27<div class="br-seat-name" id="brname_\x27+safeLbl+\x27">\x27+esc(lbl)+\x27</div>\x27+
4668
4687
  \x27</div>\x27;
4669
4688
  }
4670
4689
 
4671
- // Split agents: even indices top, odd indices bottom
4672
4690
  var topSeats = [];
4673
4691
  var botSeats = [];
4674
4692
  proposals.forEach(function(prop, si) {
@@ -4677,72 +4695,150 @@ async function runStudio() {
4677
4695
  var topHtml = topSeats.map(buildSeat).join(\x27\x27);
4678
4696
  var botHtml = botSeats.map(buildSeat).join(\x27\x27);
4679
4697
 
4680
- // Orchestrator at left head of table
4698
+ // Orchestrator head free floating, no box
4681
4699
  var orchEmoji2 = String.fromCodePoint(0x1F9D1,0x200D,0x1F4BC);
4682
4700
  var crownEm = String.fromCodePoint(0x1F451);
4683
- var orchHeadHtml = \x27<div class="br-orch" id="brOrch" style="display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 10px;flex-shrink:0">\x27+
4701
+ var orchHeadHtml = \x27<div class="br-orch" id="brOrch">\x27+
4684
4702
  \x27<div class="br-orch-speech" id="brOrchSpeech" style="display:none"></div>\x27+
4685
- \x27<span style="font-size:18px;line-height:1">\x27+crownEm+\x27</span>\x27+
4686
- \x27<span style="font-size:48px;line-height:1;filter:drop-shadow(0 0 14px #818cf8BB)">\x27+orchEmoji2+\x27</span>\x27+
4703
+ \x27<div class="br-orch-inner">\x27+
4704
+ \x27<span class="br-orch-crown">\x27+crownEm+\x27</span>\x27+
4705
+ \x27<span class="br-orch-emoji" id="brOrchEmoji">\x27+orchEmoji2+\x27</span>\x27+
4706
+ \x27</div>\x27+
4687
4707
  \x27<div class="br-orch-label">Orchestratore</div>\x27+
4688
4708
  \x27</div>\x27;
4689
4709
 
4690
- // Conference table SVG — wide walnut rectangle
4710
+ // ── Conference table SVG — rich walnut with real objects ────────────
4691
4711
  var tblSvg = \x27<svg viewBox="0 0 1000 200" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" style="position:absolute;top:50%;left:0;width:100%;height:140px;transform:translateY(-50%);z-index:1;pointer-events:none">\x27+
4692
- \x27<defs><linearGradient id="tblGrad" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5a3a18"/><stop offset="0.5" stop-color="#3d2510"/><stop offset="1" stop-color="#2a1808"/></linearGradient></defs>\x27+
4693
- \x27<rect x="8" y="12" width="984" height="176" rx="20" fill="rgba(0,0,0,.22)"/>\x27+
4694
- \x27<rect x="0" y="4" width="1000" height="176" rx="18" fill="url(#tblGrad)"/>\x27+
4695
- \x27<rect x="0" y="4" width="1000" height="8" rx="8" fill="rgba(255,255,255,.08)"/>\x27+
4696
- \x27<line x1="0" y1="60" x2="1000" y2="60" stroke="rgba(0,0,0,.07)" stroke-width="2"/>\x27+
4697
- \x27<line x1="0" y1="100" x2="1000" y2="100" stroke="rgba(0,0,0,.09)" stroke-width="2"/>\x27+
4698
- \x27<line x1="0" y1="140" x2="1000" y2="140" stroke="rgba(0,0,0,.07)" stroke-width="2"/>\x27+
4699
- \x27<text x="500" y="115" text-anchor="middle" font-family="system-ui" font-size="52" font-weight="900" fill="rgba(150,130,255,.1)" letter-spacing="8">NHA</text>\x27+
4700
- \x27<rect x="180" y="60" width="60" height="40" rx="4" fill="rgba(99,102,241,.18)" stroke="rgba(99,102,241,.35)" stroke-width="1.5"/>\x27+
4701
- \x27<rect x="390" y="55" width="70" height="45" rx="4" fill="rgba(99,102,241,.14)" stroke="rgba(99,102,241,.3)" stroke-width="1.5"/>\x27+
4702
- \x27<rect x="650" y="62" width="55" height="38" rx="4" fill="rgba(99,102,241,.18)" stroke="rgba(99,102,241,.35)" stroke-width="1.5"/>\x27+
4703
- \x27<circle cx="300" cy="90" r="14" fill="rgba(180,120,60,.4)" stroke="rgba(180,120,60,.6)" stroke-width="1.5"/>\x27+
4704
- \x27<circle cx="750" cy="92" r="14" fill="rgba(180,120,60,.4)" stroke="rgba(180,120,60,.6)" stroke-width="1.5"/>\x27+
4712
+ \x27<defs>\x27+
4713
+ \x27<linearGradient id="tblGrad" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#6b4423"/><stop offset="0.4" stop-color="#4a2e12"/><stop offset="1" stop-color="#2e1a08"/></linearGradient>\x27+
4714
+ \x27<linearGradient id="tblSheen" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="rgba(255,255,255,0)"/><stop offset="0.3" stop-color="rgba(255,255,255,.04)"/><stop offset="0.7" stop-color="rgba(255,255,255,.06)"/><stop offset="1" stop-color="rgba(255,255,255,0)"/></linearGradient>\x27+
4715
+ \x27</defs>\x27+
4716
+ // Shadow
4717
+ \x27<rect x="6" y="10" width="988" height="178" rx="22" fill="rgba(0,0,0,.28)"/>\x27+
4718
+ // Table body
4719
+ \x27<rect x="0" y="2" width="1000" height="178" rx="20" fill="url(#tblGrad)"/>\x27+
4720
+ // Wood grain lines
4721
+ \x27<line x1="0" y1="48" x2="1000" y2="48" stroke="rgba(0,0,0,.07)" stroke-width="2"/>\x27+
4722
+ \x27<line x1="0" y1="96" x2="1000" y2="96" stroke="rgba(0,0,0,.09)" stroke-width="2.5"/>\x27+
4723
+ \x27<line x1="0" y1="144" x2="1000" y2="144" stroke="rgba(0,0,0,.07)" stroke-width="2"/>\x27+
4724
+ // Sheen
4725
+ \x27<rect x="0" y="2" width="1000" height="178" rx="20" fill="url(#tblSheen)"/>\x27+
4726
+ // Top edge highlight
4727
+ \x27<rect x="0" y="2" width="1000" height="6" rx="4" fill="rgba(255,255,255,.1)"/>\x27+
4728
+ // NHA monogram
4729
+ \x27<text x="500" y="118" text-anchor="middle" font-family="system-ui" font-size="58" font-weight="900" fill="rgba(160,140,255,.09)" letter-spacing="10">NHA</text>\x27+
4730
+ // Laptop 1
4731
+ \x27<rect x="155" y="55" width="68" height="46" rx="5" fill="#1a1a2e" stroke="#4a4a8a" stroke-width="2"/>\x27+
4732
+ \x27<rect x="160" y="59" width="58" height="34" rx="3" fill="#0d0d1a"/>\x27+
4733
+ \x27<rect x="162" y="61" width="54" height="30" rx="2" fill="#1e3a5f"/>\x27+
4734
+ \x27<rect x="148" y="101" width="82" height="5" rx="2" fill="#333"/>\x27+
4735
+ // Laptop 2
4736
+ \x27<rect x="770" y="55" width="68" height="46" rx="5" fill="#1a1a2e" stroke="#4a4a8a" stroke-width="2"/>\x27+
4737
+ \x27<rect x="775" y="59" width="58" height="34" rx="3" fill="#0d0d1a"/>\x27+
4738
+ \x27<rect x="777" y="61" width="54" height="30" rx="2" fill="#1e3a5f"/>\x27+
4739
+ \x27<rect x="763" y="101" width="82" height="5" rx="2" fill="#333"/>\x27+
4740
+ // Telephone with handset (center-left)
4741
+ \x27<rect x="360" y="62" width="52" height="38" rx="4" fill="#2a2a2a" stroke="#555" stroke-width="1.5"/>\x27+
4742
+ \x27<ellipse cx="366" cy="72" rx="6" ry="8" fill="#1a1a1a" stroke="#888" stroke-width="1"/>\x27+
4743
+ \x27<ellipse cx="406" cy="72" rx="6" ry="8" fill="#1a1a1a" stroke="#888" stroke-width="1"/>\x27+
4744
+ \x27<line x1="366" y1="72" x2="406" y2="72" stroke="#666" stroke-width="3" stroke-linecap="round"/>\x27+
4745
+ \x27<rect x="372" y="76" width="40" height="18" rx="2" fill="#333"/>\x27+
4746
+ // Coffee cup 1
4747
+ \x27<rect x="570" y="68" width="28" height="28" rx="4" fill="#f5f0e8" stroke="#c8b08a" stroke-width="1.5"/>\x27+
4748
+ \x27<rect x="575" y="73" width="18" height="15" rx="2" fill="#6b3a1f" opacity=".8"/>\x27+
4749
+ \x27<path d="M598 78 Q608 83 598 88" stroke="#c8b08a" stroke-width="2" fill="none"/>\x27+
4750
+ // Mini plant on table
4751
+ \x27<rect x="470" y="72" width="14" height="16" rx="2" fill="#8B6914"/>\x27+
4752
+ \x27<ellipse cx="477" cy="68" rx="10" ry="12" fill="#2d6a2d"/>\x27+
4753
+ \x27<ellipse cx="470" cy="65" rx="7" ry="9" fill="#3a8a3a"/>\x27+
4754
+ \x27<ellipse cx="484" cy="65" rx="7" ry="9" fill="#2d7a2d"/>\x27+
4705
4755
  \x27</svg>\x27;
4706
4756
 
4707
- // Background: parquet floor + white wall + windows + chandelier + door
4757
+ // ── Background SVG enhanced room with art, detailed door, windows
4708
4758
  var bgSvg = \x27<svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none">\x27+
4709
4759
  \x27<defs>\x27+
4710
4760
  \x27<filter id="brGlow" x="-100%" y="-100%" width="300%" height="300%"><feGaussianBlur stdDeviation="5" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>\x27+
4711
- \x27<linearGradient id="brWall" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#faf7f2"/><stop offset="1" stop-color="#ede8e0"/></linearGradient>\x27+
4761
+ \x27<filter id="brShadow" x="-20%" y="-20%" width="140%" height="140%"><feDropShadow dx="2" dy="2" stdDeviation="3" flood-color="rgba(0,0,0,.2)"/></filter>\x27+
4762
+ \x27<linearGradient id="brWall" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f5f0e8"/><stop offset="1" stop-color="#e8e0d0"/></linearGradient>\x27+
4763
+ \x27<linearGradient id="winGrad" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#b8dff8"/><stop offset="1" stop-color="#d8f0ff"/></linearGradient>\x27+
4764
+ \x27<linearGradient id="doorGrad" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="#a0724a"/><stop offset="0.5" stop-color="#c8905c"/><stop offset="1" stop-color="#a0724a"/></linearGradient>\x27+
4712
4765
  \x27</defs>\x27+
4713
- \x27<rect x="0" y="0" width="1000" height="210" fill="url(#brWall)"/>\x27+
4714
- \x27<rect x="0" y="205" width="1000" height="7" fill="#d4c4a8" rx="2"/>\x27+
4766
+ // Wall
4767
+ \x27<rect x="0" y="0" width="1000" height="215" fill="url(#brWall)"/>\x27+
4768
+ // Crown molding
4769
+ \x27<rect x="0" y="0" width="1000" height="8" fill="#d8cfc0"/>\x27+
4770
+ \x27<rect x="0" y="8" width="1000" height="4" fill="#e8e0d0"/>\x27+
4771
+ // Baseboard
4772
+ \x27<rect x="0" y="206" width="1000" height="9" fill="#c8b898" rx="1"/>\x27+
4773
+ \x27<rect x="0" y="210" width="1000" height="3" fill="#b0a080"/>\x27+
4774
+ // Parquet floor
4715
4775
  function() {
4716
- var s2 = \x27\x27;
4717
- var pColors = [\x27#c8a06a\x27,\x27#bf9860\x27,\x27#d4aa72\x27,\x27#ba9458\x27,\x27#caa86e\x27];
4718
- var pH2 = 32; var pW2 = 120;
4719
- for (var fy2 = 212; fy2 < 600+pH2; fy2 += pH2) {
4720
- var ro3 = (Math.floor((fy2-212)/pH2) % 2) * (pW2/2);
4721
- for (var fx2 = -pW2+ro3; fx2 < 1000+pW2; fx2 += pW2) {
4722
- var pc3 = pColors[Math.abs(Math.round(fx2/pW2+fy2/pH2*1.3)) % pColors.length];
4723
- s2 += \x27<rect x="\x27+Math.round(fx2)+\x27" y="\x27+fy2+\x27" width="\x27+(pW2-2)+\x27" height="\x27+(pH2-2)+\x27" fill="\x27+pc3+\x27" rx="2"/>\x27;
4724
- s2 += \x27<line x1="\x27+Math.round(fx2+pW2*.4)+\x27" y1="\x27+fy2+\x27" x2="\x27+Math.round(fx2+pW2*.4)+\x27" y2="\x27+(fy2+pH2-2)+\x27" stroke="rgba(0,0,0,.04)" stroke-width="1.5"/>\x27;
4776
+ var s3 = \x27\x27;
4777
+ var pC = [\x27#c8a06a\x27,\x27#bf9860\x27,\x27#d4aa72\x27,\x27#ba9458\x27,\x27#caa86e\x27];
4778
+ var pH3 = 32; var pW3 = 120;
4779
+ for (var fy3 = 215; fy3 < 600+pH3; fy3 += pH3) {
4780
+ var ro4 = (Math.floor((fy3-215)/pH3) % 2) * (pW3/2);
4781
+ for (var fx3 = -pW3+ro4; fx3 < 1000+pW3; fx3 += pW3) {
4782
+ var pc4 = pC[Math.abs(Math.round(fx3/pW3+fy3/pH3*1.3)) % pC.length];
4783
+ s3 += \x27<rect x="\x27+Math.round(fx3)+\x27" y="\x27+fy3+\x27" width="\x27+(pW3-2)+\x27" height="\x27+(pH3-2)+\x27" fill="\x27+pc4+\x27" rx="2"/>\x27;
4784
+ s3 += \x27<line x1="\x27+Math.round(fx3+pW3*.4)+\x27" y1="\x27+fy3+\x27" x2="\x27+Math.round(fx3+pW3*.4)+\x27" y2="\x27+(fy3+pH3-2)+\x27" stroke="rgba(0,0,0,.04)" stroke-width="1.5"/>\x27;
4725
4785
  }
4726
4786
  }
4727
- return s2;
4787
+ return s3;
4728
4788
  }()+
4729
- \x27<rect x="40" y="22" width="110" height="88" rx="5" fill="#c8e6f8" stroke="#a8cce0" stroke-width="3"/>\x27+
4730
- \x27<line x1="95" y1="22" x2="95" y2="110" stroke="#a8cce0" stroke-width="2"/>\x27+
4731
- \x27<line x1="40" y1="66" x2="150" y2="66" stroke="#a8cce0" stroke-width="2"/>\x27+
4732
- \x27<rect x="40" y="22" width="110" height="88" rx="5" fill="rgba(255,255,255,.18)"/>\x27+
4733
- \x27<rect x="850" y="22" width="110" height="88" rx="5" fill="#c8e6f8" stroke="#a8cce0" stroke-width="3"/>\x27+
4734
- \x27<line x1="905" y1="22" x2="905" y2="110" stroke="#a8cce0" stroke-width="2"/>\x27+
4735
- \x27<line x1="850" y1="66" x2="960" y2="66" stroke="#a8cce0" stroke-width="2"/>\x27+
4736
- \x27<rect x="850" y="22" width="110" height="88" rx="5" fill="rgba(255,255,255,.18)"/>\x27+
4737
- \x27<rect x="463" y="0" width="74" height="210" fill="#c8a87a" stroke="#a07848" stroke-width="2"/>\x27+
4738
- \x27<rect x="472" y="10" width="56" height="50" rx="5" fill="rgba(255,255,255,.18)"/>\x27+
4739
- \x27<circle cx="524" cy="108" r="6" fill="#8a6028"/>\x27+
4740
- \x27<line x1="500" y1="0" x2="500" y2="32" stroke="#bbb" stroke-width="3"/>\x27+
4741
- \x27<ellipse cx="500" cy="42" rx="60" ry="16" fill="#e8d960" stroke="#c8b030" stroke-width="2"/>\x27+
4742
- \x27<circle cx="468" cy="52" r="10" fill="#fffde0" filter="url(#brGlow)"/>\x27+
4743
- \x27<circle cx="500" cy="56" r="10" fill="#fffde0" filter="url(#brGlow)"/>\x27+
4744
- \x27<circle cx="532" cy="52" r="10" fill="#fffde0" filter="url(#brGlow)"/>\x27+
4745
- \x27<polygon points="440,66 560,66 680,600 320,600" fill="rgba(255,252,200,.05)"/>\x27+
4789
+ // Window LEFT thick frame, venetian blinds effect
4790
+ \x27<rect x="28" y="18" width="126" height="96" rx="3" fill="#4a6080" stroke="#2a3a50" stroke-width="4"/>\x27+
4791
+ \x27<rect x="34" y="24" width="114" height="84" rx="2" fill="url(#winGrad)"/>\x27+
4792
+ \x27<rect x="34" y="24" width="114" height="84" rx="2" fill="rgba(255,255,255,.12)"/>\x27+
4793
+ \x27<line x1="91" y1="24" x2="91" y2="108" stroke="#3a5070" stroke-width="3"/>\x27+
4794
+ \x27<line x1="34" y1="66" x2="148" y2="66" stroke="#3a5070" stroke-width="3"/>\x27+
4795
+ // window sill
4796
+ \x27<rect x="22" y="114" width="138" height="8" rx="2" fill="#c8b898"/>\x27+
4797
+ // Window RIGHT
4798
+ \x27<rect x="846" y="18" width="126" height="96" rx="3" fill="#4a6080" stroke="#2a3a50" stroke-width="4"/>\x27+
4799
+ \x27<rect x="852" y="24" width="114" height="84" rx="2" fill="url(#winGrad)"/>\x27+
4800
+ \x27<rect x="852" y="24" width="114" height="84" rx="2" fill="rgba(255,255,255,.12)"/>\x27+
4801
+ \x27<line x1="909" y1="24" x2="909" y2="108" stroke="#3a5070" stroke-width="3"/>\x27+
4802
+ \x27<line x1="852" y1="66" x2="966" y2="66" stroke="#3a5070" stroke-width="3"/>\x27+
4803
+ \x27<rect x="840" y="114" width="138" height="8" rx="2" fill="#c8b898"/>\x27+
4804
+ // Door CENTER detailed with panels and handle
4805
+ \x27<rect x="455" y="0" width="90" height="215" fill="url(#doorGrad)" stroke="#7a5030" stroke-width="3"/>\x27+
4806
+ // Door panels
4807
+ \x27<rect x="463" y="10" width="74" height="55" rx="3" fill="rgba(0,0,0,.08)" stroke="rgba(0,0,0,.15)" stroke-width="1.5"/>\x27+
4808
+ \x27<rect x="463" y="72" width="74" height="55" rx="3" fill="rgba(0,0,0,.08)" stroke="rgba(0,0,0,.15)" stroke-width="1.5"/>\x27+
4809
+ \x27<rect x="463" y="134" width="74" height="55" rx="3" fill="rgba(0,0,0,.06)" stroke="rgba(0,0,0,.12)" stroke-width="1.5"/>\x27+
4810
+ // Door handle
4811
+ \x27<circle cx="533" cy="118" r="7" fill="#c8a040" stroke="#a07828" stroke-width="2"/>\x27+
4812
+ \x27<rect x="530" y="118" width="14" height="4" rx="2" fill="#c8a040" stroke="#a07828" stroke-width="1"/>\x27+
4813
+ // Door frame
4814
+ \x27<rect x="451" y="0" width="5" height="215" fill="#7a5030"/>\x27+
4815
+ \x27<rect x="544" y="0" width="5" height="215" fill="#7a5030"/>\x27+
4816
+ // Painting LEFT — abstract NHA art
4817
+ \x27<rect x="190" y="18" width="100" height="76" rx="3" fill="#f8f4ee" stroke="#8a7050" stroke-width="4" filter="url(#brShadow)"/>\x27+
4818
+ \x27<rect x="196" y="24" width="88" height="64" rx="1" fill="#1a1060"/>\x27+
4819
+ \x27<circle cx="240" cy="56" r="22" fill="#6366f1" opacity=".7"/>\x27+
4820
+ \x27<circle cx="240" cy="56" r="14" fill="#818cf8" opacity=".8"/>\x27+
4821
+ \x27<circle cx="240" cy="56" r="6" fill="#c7d2fe"/>\x27+
4822
+ \x27<text x="240" y="60" text-anchor="middle" font-size="10" font-weight="900" fill="white" font-family="system-ui">NHA</text>\x27+
4823
+ // Painting RIGHT — landscape
4824
+ \x27<rect x="710" y="18" width="110" height="76" rx="3" fill="#f8f4ee" stroke="#8a7050" stroke-width="4" filter="url(#brShadow)"/>\x27+
4825
+ \x27<rect x="716" y="24" width="98" height="64" rx="1" fill="#87CEEB"/>\x27+
4826
+ \x27<ellipse cx="765" cy="65" rx="48" ry="20" fill="#228B22"/>\x27+
4827
+ \x27<circle cx="748" cy="52" rx="14" ry="14" fill="#ffed4a" opacity=".9"/>\x27+
4828
+ \x27<ellipse cx="730" cy="62" rx="12" ry="16" fill="#1a5c1a"/>\x27+
4829
+ \x27<ellipse cx="780" cy="60" rx="10" ry="14" fill="#2a7a2a"/>\x27+
4830
+ // Chandelier
4831
+ \x27<line x1="500" y1="0" x2="500" y2="28" stroke="#aaa" stroke-width="4"/>\x27+
4832
+ \x27<ellipse cx="500" cy="36" rx="52" ry="14" fill="#f0d830" stroke="#c8a820" stroke-width="3"/>\x27+
4833
+ \x27<ellipse cx="500" cy="36" rx="45" ry="10" fill="#ffe850" opacity=".5"/>\x27+
4834
+ \x27<circle cx="470" cy="47" r="9" fill="#fffce0" filter="url(#brGlow)"/>\x27+
4835
+ \x27<circle cx="500" cy="50" r="9" fill="#fffce0" filter="url(#brGlow)"/>\x27+
4836
+ \x27<circle cx="530" cy="47" r="9" fill="#fffce0" filter="url(#brGlow)"/>\x27+
4837
+ \x27<circle cx="455" cy="40" r="6" fill="#ffe060" filter="url(#brGlow)"/>\x27+
4838
+ \x27<circle cx="545" cy="40" r="6" fill="#ffe060" filter="url(#brGlow)"/>\x27+
4839
+ \x27<polygon points="448,62 552,62 680,600 320,600" fill="rgba(255,252,180,.07)"/>\x27+
4840
+ // Cross-agent conversation lines overlay placeholder (updated dynamically)
4841
+ \x27<g id="brConvLines"></g>\x27+
4746
4842
  \x27</svg>\x27;
4747
4843
 
4748
4844
  var headerHtml = \x27<div class="br-header">\x27+
@@ -4757,8 +4853,14 @@ async function runStudio() {
4757
4853
  headerHtml+
4758
4854
  \x27<div class="br-room">\x27+
4759
4855
  bgSvg+
4760
- \x27<div style="position:absolute;bottom:10px;left:12px;font-size:36px;z-index:5;filter:drop-shadow(0 3px 6px rgba(0,0,0,.2))">\x27+String.fromCodePoint(0x1FAB4)+\x27</div>\x27+
4761
- \x27<div style="position:absolute;bottom:10px;right:12px;font-size:36px;z-index:5;filter:drop-shadow(0 3px 6px rgba(0,0,0,.2))">\x27+String.fromCodePoint(0x1FAB4)+\x27</div>\x27+
4856
+ // Decorative plants at corners
4857
+ \x27<div style="position:absolute;bottom:8px;left:10px;font-size:44px;z-index:5;filter:drop-shadow(0 3px 8px rgba(0,0,0,.3))">\x27+String.fromCodePoint(0x1FAB4)+\x27</div>\x27+
4858
+ \x27<div style="position:absolute;bottom:8px;right:10px;font-size:44px;z-index:5;filter:drop-shadow(0 3px 8px rgba(0,0,0,.3))">\x27+String.fromCodePoint(0x1FAB4)+\x27</div>\x27+
4859
+ // Small plants on window sills
4860
+ \x27<div style="position:absolute;top:112px;left:58px;font-size:20px;z-index:5">\x27+String.fromCodePoint(0x1F331)+\x27</div>\x27+
4861
+ \x27<div style="position:absolute;top:112px;right:58px;font-size:20px;z-index:5">\x27+String.fromCodePoint(0x1F331)+\x27</div>\x27+
4862
+ // Cross-agent communication SVG overlay (dynamic, updated per call)
4863
+ \x27<svg id="brCommSvg" style="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:8;overflow:visible"></svg>\x27+
4762
4864
  \x27<div style="position:relative;z-index:10;display:flex;flex-direction:column;justify-content:center;min-height:480px;padding:20px 16px;gap:0;box-sizing:border-box">\x27+
4763
4865
  \x27<div class="br-seats-row">\x27+topHtml+\x27</div>\x27+
4764
4866
  \x27<div style="position:relative;display:flex;align-items:center;width:100%;min-height:160px">\x27+
@@ -4820,49 +4922,14 @@ async function runStudio() {
4820
4922
  orchEl.style.setProperty(\x27--oc\x27, phaseColor);
4821
4923
  }
4822
4924
 
4823
- // Draw attention line from orchestrator to active agent seat
4824
- var orchEl2 = document.getElementById(\x27brOrch\x27);
4825
- var brRoom2 = orchEl2 ? orchEl2.closest(\x27.br-room\x27) : null;
4826
- if (brRoom2) {
4827
- var existLine = brRoom2.querySelector(\x27.br-attention-line\x27);
4828
- if (existLine) existLine.parentNode.removeChild(existLine);
4829
- if (activeLabel && phase !== \x27done\x27) {
4830
- var aLblSafe = activeLabel.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27);
4831
- var activeSeatEl = document.getElementById(\x27brseat_\x27+aLblSafe);
4832
- if (activeSeatEl && orchEl2) {
4833
- var roomRect = brRoom2.getBoundingClientRect();
4834
- var orchRect = orchEl2.getBoundingClientRect();
4835
- var seatRect = activeSeatEl.getBoundingClientRect();
4836
- var x1 = orchRect.left + orchRect.width/2 - roomRect.left;
4837
- var y1 = orchRect.top + orchRect.height/2 - roomRect.top;
4838
- var x2 = seatRect.left + seatRect.width/2 - roomRect.left;
4839
- var y2 = seatRect.top + seatRect.height/2 - roomRect.top;
4840
- var lineSvg = document.createElementNS(\x27http://www.w3.org/2000/svg\x27, \x27svg\x27);
4841
- lineSvg.setAttribute(\x27class\x27, \x27br-attention-line\x27);
4842
- lineSvg.style.cssText = \x27position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:10;overflow:visible\x27;
4843
- var lineEl = document.createElementNS(\x27http://www.w3.org/2000/svg\x27, \x27line\x27);
4844
- lineEl.setAttribute(\x27x1\x27, String(Math.round(x1)));
4845
- lineEl.setAttribute(\x27y1\x27, String(Math.round(y1)));
4846
- lineEl.setAttribute(\x27x2\x27, String(Math.round(x2)));
4847
- lineEl.setAttribute(\x27y2\x27, String(Math.round(y2)));
4848
- lineEl.setAttribute(\x27stroke\x27, \x27#6366f1\x27);
4849
- lineEl.setAttribute(\x27stroke-width\x27, \x272\x27);
4850
- lineEl.setAttribute(\x27stroke-dasharray\x27, \x276 4\x27);
4851
- lineEl.setAttribute(\x27opacity\x27, \x27.6\x27);
4852
- lineEl.style.animation = \x27brDashFlow 1s linear infinite\x27;
4853
- lineSvg.appendChild(lineEl);
4854
- brRoom2.appendChild(lineSvg);
4855
- }
4856
- }
4857
- }
4858
-
4859
- // Update each agent seat state
4925
+ // ── Update each agent seat state ────────────────────────────────────
4860
4926
  proposals.forEach(function(prop) {
4861
4927
  var lbl = prop.label || prop.agent;
4862
4928
  var safeLbl = lbl.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27);
4863
4929
  var seatEl = document.getElementById(\x27brseat_\x27+safeLbl);
4864
4930
  var bubbleEl = document.getElementById(\x27brbubble_\x27+safeLbl);
4865
4931
  var nameEl = document.getElementById(\x27brname_\x27+safeLbl);
4932
+ var charEl = document.getElementById(\x27brchar_\x27+safeLbl);
4866
4933
  var isDone = !!parlDoneAgents[lbl];
4867
4934
  var isActive = lbl === activeLabel;
4868
4935
 
@@ -4872,27 +4939,122 @@ async function runStudio() {
4872
4939
  (isDone ? \x27 br-seat--done\x27 : \x27\x27);
4873
4940
  seatEl.style.setProperty(\x27--sc\x27, phaseColor);
4874
4941
  }
4942
+ // Character animation: bob when active
4943
+ if (charEl) {
4944
+ charEl.style.animation = isActive ? \x27brCharBob .8s ease-in-out infinite\x27 : \x27\x27;
4945
+ charEl.style.filter = isActive
4946
+ ? (\x27drop-shadow(0 0 12px \x27+phaseColor+\x27)\x27)
4947
+ : (isDone ? \x27none\x27 : \x27grayscale(.4)\x27);
4948
+ }
4875
4949
  if (bubbleEl) {
4876
4950
  var actionStr2 = \x27\x27;
4877
4951
  if (phase===\x27r1\x27 && isActive) actionStr2 = \x27...analizza\x27;
4878
4952
  else if (phase===\x27r1\x27 && isDone) actionStr2 = \x27\u2714 bozza pronta\x27;
4879
- else if (phase===\x27r2\x27 && isActive) actionStr2 = \x27...legge + raffina\x27;
4953
+ else if (phase===\x27r2\x27 && isActive) actionStr2 = \x27...legge proposte\x27;
4880
4954
  else if (phase===\x27r2\x27 && isDone) actionStr2 = \x27\u2714 raffinato\x27;
4881
4955
  else if (phase===\x27r3\x27 && isActive) actionStr2 = \x27...media\x27;
4882
4956
  else if (phase===\x27done\x27) actionStr2 = \x27\u2714 consenso\x27;
4957
+ else if (!isActive && !isDone && phase===\x27r2\x27) actionStr2 = \x27legge...\x27;
4883
4958
  else if (!isActive && !isDone) actionStr2 = \x27in attesa\x27;
4884
4959
  bubbleEl.textContent = actionStr2;
4885
4960
  bubbleEl.style.display = actionStr2 ? \x27\x27 : \x27none\x27;
4886
4961
  bubbleEl.style.borderColor = isActive ? phaseColor : (isDone ? \x27rgba(0,0,0,.25)\x27 : \x27rgba(0,0,0,.15)\x27);
4887
4962
  bubbleEl.style.color = isActive ? phaseColor : (isDone ? \x27#111827\x27 : \x27#6b7280\x27);
4963
+ bubbleEl.style.background = isActive ? \x27rgba(255,255,255,.95)\x27 : \x27rgba(255,255,255,.82)\x27;
4964
+ bubbleEl.style.fontWeight = isActive ? \x27700\x27 : \x27500\x27;
4888
4965
  }
4889
4966
  if (nameEl) {
4890
4967
  nameEl.style.color = isDone ? \x27#111827\x27 : (isActive ? phaseColor : \x27#374151\x27);
4968
+ nameEl.style.fontWeight = isActive ? \x27800\x27 : \x27600\x27;
4891
4969
  }
4892
4970
  });
4893
4971
 
4972
+ // ── Cross-agent communication lines (R2/R3 only) ────────────────────
4973
+ // In R2: show lines from active agent to all done agents (cross-reading)
4974
+ // In R1/done: show orch→active line only
4975
+ var commSvg = document.getElementById(\x27brCommSvg\x27);
4976
+ var brRoom2 = commSvg ? commSvg.closest(\x27.br-room\x27) : null;
4977
+ if (commSvg && brRoom2) {
4978
+ commSvg.innerHTML = \x27\x27;
4979
+ var roomRect2 = brRoom2.getBoundingClientRect();
4980
+ var ns = \x27http://www.w3.org/2000/svg\x27;
4981
+
4982
+ function addCommLine(fromEl, toEl, color, dash, width, opacity, anim) {
4983
+ if (!fromEl || !toEl) return;
4984
+ var fr = fromEl.getBoundingClientRect();
4985
+ var tr = toEl.getBoundingClientRect();
4986
+ var lx1 = Math.round(fr.left + fr.width/2 - roomRect2.left);
4987
+ var ly1 = Math.round(fr.top + fr.height/2 - roomRect2.top);
4988
+ var lx2 = Math.round(tr.left + tr.width/2 - roomRect2.left);
4989
+ var ly2 = Math.round(tr.top + tr.height/2 - roomRect2.top);
4990
+ // Bezier curve for elegance
4991
+ var mx = (lx1+lx2)/2; var my = Math.min(ly1,ly2) - 30;
4992
+ var path = document.createElementNS(ns, \x27path\x27);
4993
+ path.setAttribute(\x27d\x27, \x27M\x27+lx1+\x27,\x27+ly1+\x27 Q\x27+mx+\x27,\x27+my+\x27 \x27+lx2+\x27,\x27+ly2);
4994
+ path.setAttribute(\x27stroke\x27, color);
4995
+ path.setAttribute(\x27stroke-width\x27, String(width||2));
4996
+ path.setAttribute(\x27stroke-dasharray\x27, dash||\x27\x27);
4997
+ path.setAttribute(\x27fill\x27, \x27none\x27);
4998
+ path.setAttribute(\x27opacity\x27, String(opacity||.6));
4999
+ if (anim) path.style.animation = anim;
5000
+ // Arrow marker at end
5001
+ var mk = document.createElementNS(ns, \x27marker\x27);
5002
+ var mkId = \x27arr\x27+Math.random().toString(36).slice(2,6);
5003
+ mk.setAttribute(\x27id\x27, mkId);
5004
+ mk.setAttribute(\x27markerWidth\x27,\x278\x27);mk.setAttribute(\x27markerHeight\x27,\x276\x27);
5005
+ mk.setAttribute(\x27refX\x27,\x278\x27);mk.setAttribute(\x27refY\x27,\x273\x27);
5006
+ mk.setAttribute(\x27orient\x27,\x27auto\x27);
5007
+ var poly = document.createElementNS(ns,\x27polygon\x27);
5008
+ poly.setAttribute(\x27points\x27,\x270 0, 8 3, 0 6\x27);
5009
+ poly.setAttribute(\x27fill\x27, color);
5010
+ mk.appendChild(poly);
5011
+ var defs2 = document.createElementNS(ns,\x27defs\x27);
5012
+ defs2.appendChild(mk);
5013
+ commSvg.appendChild(defs2);
5014
+ path.setAttribute(\x27marker-end\x27,\x27url(#\x27+mkId+\x27)\x27);
5015
+ commSvg.appendChild(path);
5016
+ // Floating emoji dot on path midpoint
5017
+ var dot = document.createElementNS(ns, \x27text\x27);
5018
+ dot.setAttribute(\x27x\x27, String(Math.round(mx)));
5019
+ dot.setAttribute(\x27y\x27, String(Math.round(my-8)));
5020
+ dot.setAttribute(\x27text-anchor\x27, \x27middle\x27);
5021
+ dot.setAttribute(\x27font-size\x27, \x2714\x27);
5022
+ dot.textContent = phase===\x27r2\x27 ? String.fromCodePoint(0x1F4AC) : (phase===\x27r3\x27 ? String.fromCodePoint(0x1F91D) : String.fromCodePoint(0x1F4E8));
5023
+ dot.style.animation = \x27brDotFloat 1.5s ease-in-out infinite\x27;
5024
+ commSvg.appendChild(dot);
5025
+ }
5026
+
5027
+ var orchEl2 = document.getElementById(\x27brOrch\x27);
5028
+ if (phase === \x27done\x27) {
5029
+ // All done: show web of connections between all agents
5030
+ proposals.forEach(function(pa, ia) {
5031
+ proposals.forEach(function(pb2, ib2) {
5032
+ if (ib2 <= ia) return;
5033
+ var ea = document.getElementById(\x27brseat_\x27+pa.label.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27));
5034
+ var eb = document.getElementById(\x27brseat_\x27+pb2.label.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27));
5035
+ addCommLine(ea, eb, \x27#22c55e\x27, \x273 3\x27, 1.5, 0.3, \x27\x27);
5036
+ });
5037
+ });
5038
+ } else if (activeLabel) {
5039
+ var aLblSafe2 = activeLabel.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27);
5040
+ var activeSeatEl2 = document.getElementById(\x27brseat_\x27+aLblSafe2);
5041
+ // Orch → active agent (always)
5042
+ addCommLine(orchEl2, activeSeatEl2, phaseColor, \x275 4\x27, 2.5, 0.75, \x27brDashFlow 1.2s linear infinite\x27);
5043
+ // R2/R3: active reads all done agents → show reading lines
5044
+ if (phase === \x27r2\x27 || phase === \x27r3\x27) {
5045
+ proposals.forEach(function(pp2) {
5046
+ var doneL = pp2.label || pp2.agent;
5047
+ if (doneL === activeLabel) return;
5048
+ if (!parlDoneAgents[doneL]) return;
5049
+ var doneSeat = document.getElementById(\x27brseat_\x27+doneL.replace(/[^a-zA-Z0-9_-]/g,\x27_\x27));
5050
+ addCommLine(doneSeat, activeSeatEl2, \x27#f59e0b\x27, \x274 3\x27, 1.5, 0.5, \x27brDashFlow 1.8s linear infinite\x27);
5051
+ });
5052
+ }
5053
+ }
5054
+ }
5055
+
4894
5056
  // Persist across tab navigations
4895
- if (pb.innerHTML && pb.innerHTML.length < 60000) { _parlPersistHtml = pb.innerHTML; }
5057
+ if (pb.innerHTML && pb.innerHTML.length < 60000) { _parlPersistHtml = _PARL_STAMP + pb.innerHTML; }
4896
5058
 
4897
5059
  }
4898
5060
 
@@ -5060,7 +5222,7 @@ async function runStudio() {
5060
5222
  }
5061
5223
  }
5062
5224
  if (parlFinal && parlFinal.style.display !== 'none' && parlFinal.innerHTML) {
5063
- _parlPersistHtml = parlFinal.innerHTML; // persist so tab nav doesn't lose it
5225
+ _parlPersistHtml = _PARL_STAMP + parlFinal.innerHTML; // persist so tab nav doesn't lose it
5064
5226
  doScroll(parlFinal);
5065
5227
  setTimeout(function(){ doScroll(resEl); }, 2200);
5066
5228
  } else if (resEl) {
@@ -5435,7 +5597,7 @@ function renderStudio(el) {
5435
5597
  // Persist parliament block across tab navigations
5436
5598
  var existingParl = document.getElementById('studioParliamentBlock');
5437
5599
  if (existingParl && existingParl.innerHTML.trim()) {
5438
- _parlPersistHtml = existingParl.innerHTML;
5600
+ _parlPersistHtml = _PARL_STAMP + existingParl.innerHTML;
5439
5601
  }
5440
5602
 
5441
5603
  var examplesHtml = STUDIO_EXAMPLES.map(function(ex) {
@@ -5607,12 +5769,15 @@ function renderStudio(el) {
5607
5769
  // Restore pipeline from state
5608
5770
  renderBuilderPipeline();
5609
5771
  // Restore parliament block if it was visible before tab navigation
5610
- if (_parlPersistHtml) {
5772
+ // Version stamp check: discard stale HTML from older versions
5773
+ if (_parlPersistHtml && _parlPersistHtml.indexOf(_PARL_STAMP) === 0) {
5611
5774
  var parlRestoreEl = document.getElementById('studioParliamentBlock');
5612
5775
  if (parlRestoreEl) {
5613
- parlRestoreEl.innerHTML = _parlPersistHtml;
5776
+ parlRestoreEl.innerHTML = _parlPersistHtml.slice(_PARL_STAMP.length);
5614
5777
  parlRestoreEl.style.display = 'block';
5615
5778
  }
5779
+ } else {
5780
+ _parlPersistHtml = null;
5616
5781
  }
5617
5782
  }
5618
5783
 
@@ -6159,23 +6324,32 @@ input:focus,textarea:focus{border-color:var(--green3)}
6159
6324
  .br-room{position:relative;width:100%;min-height:480px;overflow:hidden;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.18)}
6160
6325
  /* Seats rows above/below table */
6161
6326
  .br-seats-row{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;justify-content:space-around}
6162
- /* Individual seat card light theme matching workflow stations */
6163
- .br-seat{display:flex;flex-direction:column;align-items:center;gap:2px;transition:transform .3s;padding:6px 8px;border-radius:12px;background:rgba(255,255,255,.82);border:1.5px solid rgba(200,180,160,.5);box-shadow:0 2px 8px rgba(0,0,0,.12);backdrop-filter:blur(4px);min-width:64px;cursor:default}
6164
- .br-seat--active{transform:scale(1.07);border-color:var(--sc,#6366f1);box-shadow:0 0 16px rgba(99,102,241,.35),0 2px 8px rgba(0,0,0,.12)}
6165
- .br-seat--done{border-color:rgba(0,0,0,.25);background:rgba(0,0,0,.06)}
6327
+ /* Agent seat — FREE (no box, just emoji + name floating) */
6328
+ .br-seat{display:flex;flex-direction:column;align-items:center;gap:2px;transition:transform .35s;padding:4px 6px;border-radius:10px;background:transparent;border:none;box-shadow:none;min-width:56px;cursor:default}
6329
+ .br-seat--active{transform:scale(1.14) translateY(-5px)}
6330
+ .br-seat--done{opacity:.88}
6166
6331
  /* Emoji character */
6167
- .br-char{font-size:36px;line-height:1;user-select:none;filter:drop-shadow(0 2px 5px rgba(0,0,0,.2))}
6168
- /* Orchestrator head seat */
6169
- .br-orch{display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 10px;flex-shrink:0}
6170
- .br-orch-speech{font-size:9px;font-weight:800;font-family:var(--mono);padding:3px 8px;border:1.5px solid #6366f1;border-radius:8px;background:rgba(255,255,255,.95);color:#4338ca;white-space:nowrap;animation:brSpeechPop .6s ease-in-out infinite alternate;pointer-events:none;margin-bottom:2px}
6171
- @keyframes brSpeechPop{0%{transform:scale(1) rotate(-1deg)}100%{transform:scale(1.05) rotate(1deg)}}
6172
- .br-orch-label{font-size:9px;font-family:var(--mono);font-weight:800;color:#6366f1;background:rgba(99,102,241,.12);border-radius:6px;padding:2px 7px}
6173
- /* Bubble above seat */
6174
- .br-bubble{font-size:8px;font-family:var(--mono);padding:3px 8px;border-radius:8px 8px 8px 2px;border:1px solid rgba(99,102,241,.35);background:rgba(255,255,255,.95);color:#1e1b4b;line-height:1.45;word-break:break-word;max-width:140px;white-space:normal;box-shadow:0 2px 6px rgba(0,0,0,.1);margin-bottom:2px}
6332
+ .br-char{font-size:40px;line-height:1;user-select:none;transition:filter .4s}
6333
+ @keyframes brCharBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
6334
+ /* Orchestrator head — large, no box */
6335
+ .br-orch{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 10px;flex-shrink:0;position:relative}
6336
+ .br-orch-inner{display:flex;flex-direction:column;align-items:center;gap:0;position:relative}
6337
+ .br-orch-crown{font-size:22px;line-height:1;display:block;text-align:center;animation:brCrownFloat 2s ease-in-out infinite}
6338
+ @keyframes brCrownFloat{0%,100%{transform:translateY(0) rotate(-4deg)}50%{transform:translateY(-3px) rotate(4deg)}}
6339
+ .br-orch-emoji{font-size:54px;line-height:1;display:block;filter:drop-shadow(0 0 16px #818cf8AA);transition:filter .3s}
6340
+ .br-orch--active .br-orch-emoji{animation:brOrchWalk 1.4s ease-in-out infinite alternate;filter:drop-shadow(0 0 20px #6366f1CC)}
6341
+ @keyframes brOrchWalk{0%{transform:translateX(0) scale(1)}100%{transform:translateX(14px) scale(1.07)}}
6342
+ .br-orch--done .br-orch-emoji{animation:orchBounce .7s ease forwards}
6343
+ .br-orch-speech{font-size:10px;font-weight:800;font-family:var(--mono);padding:4px 10px;border:2px solid #6366f1;border-radius:10px;background:rgba(255,255,255,.95);color:#4338ca;white-space:nowrap;animation:brSpeechPop .7s ease-in-out infinite alternate;pointer-events:none;margin-bottom:4px;box-shadow:0 2px 8px rgba(99,102,241,.25)}
6344
+ @keyframes brSpeechPop{0%{transform:scale(1) rotate(-1deg)}100%{transform:scale(1.06) rotate(1deg)}}
6345
+ .br-orch-label{font-size:9px;font-family:var(--mono);font-weight:800;color:#6366f1;background:rgba(99,102,241,.15);border-radius:6px;padding:2px 8px;margin-top:2px}
6346
+ /* Bubble above agent */
6347
+ .br-bubble{font-size:9px;font-family:var(--mono);padding:4px 9px;border-radius:10px 10px 10px 2px;border:1.5px solid rgba(99,102,241,.35);background:rgba(255,255,255,.96);color:#1e1b4b;line-height:1.4;word-break:break-word;max-width:120px;white-space:normal;box-shadow:0 2px 8px rgba(0,0,0,.12);margin-bottom:3px}
6175
6348
  /* Agent name pill */
6176
- .br-seat-name{font-size:9px;font-family:var(--mono);font-weight:700;color:#374151;text-align:center;white-space:normal;word-break:break-word;max-width:140px;line-height:1.3;margin-top:1px;background:rgba(0,0,0,.04);border-radius:4px;padding:1px 4px;transition:color .3s}
6177
- .br-seat--active .br-seat-name{color:#4338ca}
6349
+ .br-seat-name{font-size:9px;font-family:var(--mono);font-weight:600;color:#374151;text-align:center;white-space:normal;word-break:break-word;max-width:100px;line-height:1.3;margin-top:1px;background:rgba(255,255,255,.75);border-radius:4px;padding:1px 4px;transition:color .3s,font-weight .2s;backdrop-filter:blur(2px)}
6350
+ .br-seat--active .br-seat-name{color:#4338ca;font-weight:800}
6178
6351
  .br-seat--done .br-seat-name{color:#111827}
6352
+ @keyframes brDotFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
6179
6353
  /* Convergence */
6180
6354
  .br-convergence{margin-top:10px;padding:8px 12px;background:rgba(99,102,241,.06);border:1px solid rgba(99,102,241,.2);border-radius:8px}
6181
6355
  .br-conv-bar-outer{height:4px;background:rgba(99,102,241,.15);border-radius:4px;overflow:hidden;margin-bottom:6px}
@@ -6233,10 +6407,13 @@ input:focus,textarea:focus{border-color:var(--green3)}
6233
6407
  .iso-scene{background:#f0ede6;cursor:default;max-width:100%;overflow-x:auto;box-shadow:0 4px 24px rgba(0,0,0,.18);border-radius:12px;overflow:hidden}
6234
6408
  .iso-station{display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer;transition:filter .2s,transform .2s;padding:8px 4px;border-radius:12px;border:1.5px solid transparent;position:relative}
6235
6409
  .iso-station:hover{filter:brightness(1.06);transform:translateY(-2px)}
6236
- .iso-station.iso-orch-walking{animation:orchWalkGrid 1.8s ease-in-out infinite alternate}
6237
- @keyframes orchWalkGrid{0%{transform:translateX(0) scale(1)}100%{transform:translateX(24px) scale(1.04)}}
6238
- .iso-station.iso-orch-done{animation:orchBounce .7s ease forwards}
6410
+ .iso-char-mover{position:relative;transition:transform .3s}
6411
+ .iso-char-mover.iso-orch-walking{animation:orchWalkGrid 1.8s ease-in-out infinite alternate}
6412
+ @keyframes orchWalkGrid{0%{transform:translateX(0) scale(1)}100%{transform:translateX(18px) scale(1.05)}}
6413
+ .iso-char-mover.iso-orch-done{animation:orchBounce .7s ease forwards}
6239
6414
  @keyframes orchBounce{0%{transform:scale(1)}40%{transform:scale(1.18) translateY(-6px)}100%{transform:scale(1)}}
6415
+ .iso-fly-doc{position:absolute;top:-8px;right:-4px;font-size:16px;animation:flyDocSpin 1.2s ease-in-out infinite;z-index:20;pointer-events:none}
6416
+ @keyframes flyDocSpin{0%{transform:rotate(-15deg) translateY(0);opacity:.7}50%{transform:rotate(10deg) translateY(-8px);opacity:1}100%{transform:rotate(-15deg) translateY(0);opacity:.7}}
6240
6417
  .iso-desk{width:90%;height:16px;background:linear-gradient(180deg,#d4a448 0%,#b8832a 100%);border-radius:4px 4px 2px 2px;box-shadow:0 4px 0 #8a5e18,0 6px 10px rgba(0,0,0,.3);border-top:2px solid #e8c060;position:relative;margin-top:4px}
6241
6418
  .iso-desk::after{content:'';position:absolute;bottom:-4px;left:8px;right:8px;height:4px;background:#7a5010;border-radius:0 0 3px 3px}
6242
6419
  .iso-monitor{width:56px;height:40px;background:#12101e;border:2px solid #3a3070;border-radius:5px;display:flex;align-items:center;justify-content:center;position:relative;margin-bottom:-2px}