nothumanallowed 13.5.10 → 13.5.12

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.10",
3
+ "version": "13.5.12",
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.10';
8
+ export const VERSION = '13.5.12';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -3798,8 +3798,11 @@ function isoCharSvg(opts) {
3798
3798
  var isDone = opts.isDone;
3799
3799
  var scale = opts.scale || 1;
3800
3800
  var accentColor = opts.accentColor || \x27#6366f1\x27;
3801
- var idx = opts.emojiIdx || 0;
3802
- var emoji = AGENT_EMOJIS[idx % AGENT_EMOJIS.length];
3801
+ var idx = opts.emojiIdx;
3802
+ // idx === 99 means orchestrator — use crown+suit combo
3803
+ var emoji = (idx === 99)
3804
+ ? (String.fromCodePoint(0x1F451) + String.fromCodePoint(0x1F9D1,0x200D,0x1F4BC))
3805
+ : AGENT_EMOJIS[(idx || 0) % AGENT_EMOJIS.length];
3803
3806
  var sz = Math.round(52 * scale);
3804
3807
  var glowColor = isActive ? accentColor : (isDone ? \x27#22c55e\x27 : \x27transparent\x27);
3805
3808
  var glowFilter = (isActive || isDone) ? (\x27filter:drop-shadow(0 0 8px \x27+glowColor+\x27aa)\x27) : \x27\x27;
@@ -3841,6 +3844,61 @@ function agentPalette(lbl) {
3841
3844
  return {skin: skins[i], shirt: shirts[i], hair: hairs[i]};
3842
3845
  }
3843
3846
 
3847
+ // Tool emoji map — specific icon per agent/tool type, fallback to default
3848
+ var TOOL_EMOJI_MAP = {
3849
+ websearch: String.fromCodePoint(0x1F50D),
3850
+ search: String.fromCodePoint(0x1F50D),
3851
+ browser: String.fromCodePoint(0x1F310),
3852
+ email: String.fromCodePoint(0x1F4E7),
3853
+ gmail: String.fromCodePoint(0x1F4E7),
3854
+ calendar: String.fromCodePoint(0x1F4C5),
3855
+ github: String.fromCodePoint(0x1F431),
3856
+ notion: String.fromCodePoint(0x1F4D3),
3857
+ slack: String.fromCodePoint(0x1F4AC),
3858
+ data: String.fromCodePoint(0x1F4CA),
3859
+ analyst: String.fromCodePoint(0x1F4CA),
3860
+ writer: String.fromCodePoint(0x270F,0xFE0F),
3861
+ summary: String.fromCodePoint(0x1F4CB),
3862
+ research: String.fromCodePoint(0x1F52C),
3863
+ canvas: String.fromCodePoint(0x1F3A8),
3864
+ security: String.fromCodePoint(0x1F6E1,0xFE0F),
3865
+ devops: String.fromCodePoint(0x2699,0xFE0F),
3866
+ code: String.fromCodePoint(0x1F4BB),
3867
+ file: String.fromCodePoint(0x1F4C2),
3868
+ drive: String.fromCodePoint(0x1F4BE),
3869
+ maps: String.fromCodePoint(0x1F5FA,0xFE0F),
3870
+ voice: String.fromCodePoint(0x1F3A4),
3871
+ pdf: String.fromCodePoint(0x1F4DC),
3872
+ document: String.fromCodePoint(0x1F4DC),
3873
+ task: String.fromCodePoint(0x2705),
3874
+ contacts: String.fromCodePoint(0x1F4F1),
3875
+ reminder: String.fromCodePoint(0x23F0),
3876
+ news: String.fromCodePoint(0x1F4F0),
3877
+ image: String.fromCodePoint(0x1F5BC,0xFE0F),
3878
+ video: String.fromCodePoint(0x1F3AC),
3879
+ music: String.fromCodePoint(0x1F3B5),
3880
+ translate: String.fromCodePoint(0x1F30D),
3881
+ math: String.fromCodePoint(0x1F9EE),
3882
+ sql: String.fromCodePoint(0x1F5C4,0xFE0F),
3883
+ api: String.fromCodePoint(0x1F517),
3884
+ test: String.fromCodePoint(0x1F9EA),
3885
+ monitor: String.fromCodePoint(0x1F4F6),
3886
+ _default: String.fromCodePoint(0x1F527) // wrench as fallback
3887
+ };
3888
+
3889
+ function getNodeEmoji(n) {
3890
+ var lbl = (n.label || n.agent || '').toLowerCase();
3891
+ var icon = n.icon || '';
3892
+ // Check by label keywords
3893
+ var keys = Object.keys(TOOL_EMOJI_MAP);
3894
+ for (var ki = 0; ki < keys.length; ki++) {
3895
+ if (keys[ki] !== '_default' && lbl.indexOf(keys[ki]) >= 0) return TOOL_EMOJI_MAP[keys[ki]];
3896
+ }
3897
+ // If icon looks like an emoji code point (not HTML entity), use it
3898
+ if (icon && icon.length > 0 && icon.charCodeAt(0) > 127) return icon;
3899
+ return TOOL_EMOJI_MAP._default;
3900
+ }
3901
+
3844
3902
  function renderStudioNodes() {
3845
3903
  var el = document.getElementById('studioNodes');
3846
3904
  if (!el) return;
@@ -3855,78 +3913,142 @@ function renderStudioNodes() {
3855
3913
  var totalCount = nodes.length;
3856
3914
  var showMaster = hasActive || doneCount > 0;
3857
3915
 
3858
- // ── ISO scene dimensions ───────────────────────────────────────────────────
3859
- var ISO_COLS = Math.max(nodes.length, 1);
3860
- var SCENE_W = 640; var SCENE_H = 420;
3916
+ var phaseLabel2 = hasActive
3917
+ ? (\x27Workflow in esecuzione \u2014 \x27+doneCount+\x27/\x27+totalCount)
3918
+ : (doneCount===totalCount && totalCount>0 ? \x27Workflow completato\x27 : \x27Workflow pianificato\x27);
3919
+ var phaseColor2 = hasActive ? \x27#6366f1\x27 : (doneCount===totalCount && totalCount>0 ? \x27#22c55e\x27 : \x27#6b7280\x27);
3920
+
3921
+ // ── CSS Grid layout: 100% width, responsive ──────────────────────────────
3922
+ // All stations (orch + agents) in a grid, max 4 per row
3923
+ var totalStations = totalCount + 1; // +1 for orchestrator
3924
+ var cols = totalStations <= 2 ? totalStations : (totalStations <= 4 ? 2 : (totalStations <= 6 ? 3 : 4));
3925
+ var rows = Math.ceil(totalStations / cols);
3926
+
3927
+ // Each station rendered as a grid cell — 100% fills parent
3928
+ // No pixel-based absolute positioning: use CSS grid % cells
3929
+ var gridTpl = \x27repeat(\x27+cols+\x27,1fr)\x27;
3930
+
3931
+ // Plant/deco emojis
3932
+ var bigPlant = String.fromCodePoint(0x1FAB4);
3933
+ var plantEmoji = String.fromCodePoint(0x1F331);
3934
+
3935
+ // Build all station cards (orchestrator first, then agents)
3936
+ function buildStation2(label, toolEmoji, isOrch, isActive, isDone, isErr, emojiIdx) {
3937
+ var accentColor = isOrch ? \x27#818cf8\x27 : (isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#9ca3af\x27)));
3938
+ var nameBg = isDone ? \x27#dcfce7\x27 : (isActive ? \x27#ede9fe\x27 : (isOrch ? \x27#e0e7ff\x27 : \x27rgba(255,255,255,.85)\x27));
3939
+ var nameColor = isDone ? \x27#16a34a\x27 : (isActive ? \x27#4f46e5\x27 : (isOrch ? \x27#4338ca\x27 : (isErr ? \x27#dc2626\x27 : \x27#374151\x27)));
3940
+ var monScreen = isOrch
3941
+ ? \x27<span style="font-size:11px">&#128269;</span>\x27
3942
+ : (isDone ? \x27<span style="color:#22c55e;font-size:13px">&#10003;</span>\x27
3943
+ : (isActive ? \x27<span class="iso-monitor-blink"></span>\x27
3944
+ : \x27<span style="font-size:8px;opacity:.35;color:#818cf8">&#9632;</span>\x27));
3945
+ var bubbleText = isOrch
3946
+ ? (hasActive ? (\x27Step \x27+doneCount+\x27/\x27+totalCount) : (doneCount===totalCount&&totalCount>0 ? \x27\u2714 Fatto!\x27 : \x27In attesa\x27))
3947
+ : (isActive ? \x27\u2022\u2022\u2022 lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27)));
3948
+ var bubbleBg = isOrch ? \x27rgba(99,102,241,.15)\x27 : (isActive ? \x27rgba(99,102,241,.12)\x27 : (isDone ? \x27rgba(34,197,94,.12)\x27 : \x27rgba(239,68,68,.12)\x27));
3949
+ var glowBox = isActive ? (\x270 0 0 3px \x27+accentColor+\x2744,0 8px 24px \x27+accentColor+\x2733\x27) : (isDone ? (\x270 0 0 2px #22c55e44\x27) : \x27none\x27);
3950
+ var orchWalkClass = (isOrch && hasActive) ? \x27 iso-orch-walking\x27 : (isOrch && doneCount===totalCount&&totalCount>0 ? \x27 iso-orch-done\x27 : \x27\x27);
3951
+ var charHtml = isoCharSvg({emojiIdx: isOrch ? 99 : emojiIdx, isActive: isActive, isDone: isDone, scale: 1.1, accentColor: accentColor});
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);
3953
+ 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+
3954
+ // bubble
3955
+ \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+
3956
+ // tool badge above character
3957
+ \x27<div class="iso-tool-badge">\x27+toolEmoji+\x27</div>\x27+
3958
+ // character emoji
3959
+ charHtml+
3960
+ // desk
3961
+ \x27<div class="iso-desk" style="width:85%;border-top-color:\x27+accentColor+\x2733"></div>\x27+
3962
+ // monitor on desk
3963
+ \x27<div class="iso-monitor" style="border-color:\x27+accentColor+\x2777"><div class="iso-monitor-screen">\x27+monScreen+\x27</div></div>\x27+
3964
+ // name
3965
+ \x27<div class="iso-name" style="color:\x27+nameColor+\x27;background:\x27+nameBg+\x27">\x27+(isOrch?\x27\u2666\xA0\x27:\x27\x27)+esc(label.slice(0,16))+\x27</div>\x27+
3966
+ \x27</div>\x27;
3967
+ }
3861
3968
 
3862
- var agentsHtml = \x27\x27;
3969
+ var stationsHtml = \x27\x27;
3970
+ // Orchestrator
3971
+ var orchDone2 = !hasActive && doneCount===totalCount && totalCount>0;
3972
+ stationsHtml += buildStation2(\x27Orchestratore\x27, String.fromCodePoint(0x1F4CB), true, hasActive, orchDone2, false, 99);
3973
+ // Agents
3863
3974
  nodes.forEach(function(n, idx) {
3864
- var isActive = n.status === \x27running\x27;
3865
- var isDone = n.status === \x27done\x27;
3866
- var isErr = n.status === \x27error\x27;
3867
- var lbl = n.label || n.agent;
3868
- var pal = agentPalette(lbl);
3869
- var accentColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#999\x27));
3870
-
3871
- // Spread agents evenly across front rows
3872
- var colStep = ISO_COLS > 1 ? 5 / (ISO_COLS - 1) : 2;
3873
- var col = (ISO_COLS === 1) ? 2.5 : idx * colStep;
3874
- var row = 3.5;
3875
- var pos = isoProject(col, row);
3876
- var px = pos.x; var py = pos.y + 80;
3877
- var charScale = 1.1;
3878
- var zIdx = Math.round(py + 100);
3879
-
3880
- // Bubble: only show when active (live text) or done (checkmark)
3881
- var bubbleText = isActive ? \x27...lavora\x27 : (isDone ? \x27\u2714 fatto\x27 : (isErr ? \x27\u2715 errore\x27 : \x27\x27));
3882
- var bubbleColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : \x27#ef4444\x27);
3883
- void pal; // palette no longer used for character rendering
3884
- var charSvg = isoCharSvg({emojiIdx: idx, isActive: isActive, isDone: isDone, scale: charScale, accentColor: accentColor});
3885
- var charW = Math.round(52 * charScale); var charH = Math.round(52 * charScale);
3886
-
3887
- agentsHtml += \x27<div class="iso-agent" data-agent-label="\x27+esc(lbl)+\x27" style="position:absolute;left:\x27+(px-charW/2)+\x27px;top:\x27+(py-charH)+\x27px;z-index:\x27+zIdx+\x27;display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))">\x27;
3888
- // Bubble — bigger, left-aligned (no mirror issue)
3889
- if (bubbleText || isActive) {
3890
- agentsHtml += \x27<div class="iso-bubble\x27+(isActive?\x27 iso-bubble--active\x27:\x27\x27)+\x27" id="isobubble_\x27+idx+\x27" style="border-color:\x27+bubbleColor+\x27;color:\x27+bubbleColor+\x27;min-width:80px;max-width:140px;font-size:10px;padding:4px 10px;text-align:left">\x27+esc(bubbleText)+\x27</div>\x27;
3891
- } else {
3892
- agentsHtml += \x27<div class="iso-bubble" id="isobubble_\x27+idx+\x27" style="visibility:hidden;min-width:80px"></div>\x27;
3893
- }
3894
- agentsHtml += charSvg;
3895
- agentsHtml += \x27<div class="iso-name" style="font-size:10px;font-weight:700;color:\x27+(isDone?\x27#22c55e\x27:(isActive?\x27#818cf8\x27:(isErr?\x27#ef4444\x27:\x27#555\x27)))+\x27;text-shadow:0 1px 3px rgba(255,255,255,.8)">\x27+esc(lbl)+\x27</div>\x27;
3896
- // Desk — lighter wood color for bright office
3897
- agentsHtml += isoDeskSvg(px, py - charH/2, accentColor);
3898
- agentsHtml += \x27</div>\x27;
3975
+ stationsHtml += buildStation2(
3976
+ n.label || n.agent,
3977
+ getNodeEmoji(n),
3978
+ false,
3979
+ n.status===\x27running\x27,
3980
+ n.status===\x27done\x27,
3981
+ n.status===\x27error\x27,
3982
+ idx
3983
+ );
3899
3984
  });
3900
3985
 
3901
- // ── Orchestrator ──────────────────────────────────────────────────────────
3902
- var orchHtml = \x27\x27;
3903
- if (showMaster) {
3904
- var oPh = isoProject(ISO_COLS / 2 + 0.5, 1.2);
3905
- var orchDone = !hasActive && doneCount === totalCount;
3906
- var orchAnim = hasActive ? \x27prl-master-walk\x27 : (orchDone ? \x27prl-master-done\x27 : \x27\x27);
3907
- var orchStatus = hasActive
3908
- ? (\x27Step \x27+doneCount+\x27/\x27+totalCount)
3909
- : (orchDone ? \x27Completato!\x27 : \x27Pianificazione\x27);
3910
- orchHtml = \x27<div class="prl-master \x27+orchAnim+\x27" id="wfOrch" style="position:absolute;left:\x27+(oPh.x-28)+\x27px;top:\x27+(oPh.y+60)+\x27px;z-index:50;display:flex;flex-direction:column;align-items:center;gap:3px">\x27+
3911
- \x27<div class="iso-bubble iso-bubble--orch" id="wfOrchBubble" style="border-color:#6366f1;color:#6366f1;max-width:140px;font-size:10px;padding:4px 10px">\x27+esc(orchStatus)+\x27</div>\x27+
3912
- isoOrchSvg(hasActive, doneCount / Math.max(totalCount,1))+
3913
- \x27<div class="prl-master-label" style="color:#6366f1;font-size:9px;font-weight:700;text-shadow:0 1px 3px rgba(255,255,255,.8)">Orchestratore</div>\x27+
3914
- \x27</div>\x27;
3986
+ // Floor SVG — uses a fixed coordinate space (1000×600) scaled 100% via CSS
3987
+ var FW = 1000; var FH = 600;
3988
+ var wallH = Math.round(FH * 0.30);
3989
+ var floorSvg = \x27<svg viewBox="0 0 \x27+FW+\x27 \x27+FH+\x27" 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;
3990
+ floorSvg += \x27<defs>\x27;
3991
+ floorSvg += \x27<filter id="bGlow2" x="-100%" y="-100%" width="300%" height="300%"><feGaussianBlur stdDeviation="5" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>\x27;
3992
+ floorSvg += \x27<linearGradient id="wallGrad" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#faf7f2"/><stop offset="1" stop-color="#ede8e0"/></linearGradient>\x27;
3993
+ floorSvg += \x27</defs>\x27;
3994
+ // Wall
3995
+ floorSvg += \x27<rect x="0" y="0" width="\x27+FW+\x27" height="\x27+wallH+\x27" fill="url(#wallGrad)"/>\x27;
3996
+ // Baseboard
3997
+ floorSvg += \x27<rect x="0" y="\x27+(wallH-5)+\x27" width="\x27+FW+\x27" height="7" fill="#d4c4a8" rx="2"/>\x27;
3998
+ // Floor parquet
3999
+ var plankColors2 = [\x27#c8a06a\x27,\x27#bf9860\x27,\x27#d4aa72\x27,\x27#ba9458\x27,\x27#caa86e\x27];
4000
+ var pH = 32; var pW = 120;
4001
+ for (var fy = wallH; fy < FH+pH; fy += pH) {
4002
+ var ro2 = (Math.floor((fy-wallH)/pH) % 2) * (pW/2);
4003
+ for (var fx = -pW+ro2; fx < FW+pW; fx += pW) {
4004
+ var pc2 = plankColors2[Math.abs(Math.round(fx/pW+fy/pH*1.3)) % plankColors2.length];
4005
+ floorSvg += \x27<rect x="\x27+Math.round(fx)+\x27" y="\x27+fy+\x27" width="\x27+(pW-2)+\x27" height="\x27+(pH-2)+\x27" fill="\x27+pc2+\x27" rx="2"/>\x27;
4006
+ floorSvg += \x27<line x1="\x27+Math.round(fx+pW*0.4)+\x27" y1="\x27+fy+\x27" x2="\x27+Math.round(fx+pW*0.4)+\x27" y2="\x27+(fy+pH-2)+\x27" stroke="rgba(0,0,0,.04)" stroke-width="1.5"/>\x27;
4007
+ }
3915
4008
  }
3916
-
3917
- var phaseLabel2 = hasActive
3918
- ? (\x27Workflow in esecuzione \u2014 \x27+doneCount+\x27/\x27+totalCount)
3919
- : (doneCount===totalCount && totalCount>0 ? \x27Workflow completato\x27 : \x27Workflow pianificato\x27);
3920
- var phaseColor2 = hasActive ? \x27#6366f1\x27 : (doneCount===totalCount && totalCount>0 ? \x27#22c55e\x27 : \x27#6b7280\x27);
4009
+ // Windows — 2 on left, 1 center-right
4010
+ function svgWindow(wx, wy, ww, wh) {
4011
+ var r = \x27<rect x="\x27+wx+\x27" y="\x27+wy+\x27" width="\x27+ww+\x27" height="\x27+wh+\x27" rx="4" fill="#c8e6f8" stroke="#a8cce0" stroke-width="3"/>\x27;
4012
+ r += \x27<rect x="\x27+wx+\x27" y="\x27+wy+\x27" width="\x27+ww+\x27" height="\x27+wh+\x27" rx="4" fill="rgba(255,255,255,.2)"/>\x27;
4013
+ r += \x27<line x1="\x27+(wx+ww/2)+\x27" y1="\x27+wy+\x27" x2="\x27+(wx+ww/2)+\x27" y2="\x27+(wy+wh)+\x27" stroke="#a8cce0" stroke-width="2"/>\x27;
4014
+ r += \x27<line x1="\x27+wx+\x27" y1="\x27+(wy+wh/2)+\x27" x2="\x27+(wx+ww)+\x27" y2="\x27+(wy+wh/2)+\x27" stroke="#a8cce0" stroke-width="2"/>\x27;
4015
+ return r;
4016
+ }
4017
+ floorSvg += svgWindow(40, 20, 100, 80);
4018
+ floorSvg += svgWindow(180, 20, 100, 80);
4019
+ floorSvg += svgWindow(FW-200, 20, 120, 80);
4020
+ // Door center
4021
+ floorSvg += \x27<rect x="\x27+(FW/2-35)+\x27" y="0" width="70" height="\x27+wallH+\x27" rx="0" fill="#c8a87a" stroke="#a07848" stroke-width="2"/>\x27;
4022
+ floorSvg += \x27<rect x="\x27+(FW/2-25)+\x27" y="8" width="50" height="36" rx="4" fill="rgba(255,255,255,.18)"/>\x27;
4023
+ floorSvg += \x27<circle cx="\x27+(FW/2+22)+\x27" cy="\x27+(wallH/2)+\x27" r="5" fill="#8a6028"/>\x27;
4024
+ // Chandelier
4025
+ floorSvg += \x27<line x1="\x27+(FW/2)+\x27" y1="0" x2="\x27+(FW/2)+\x27" y2="30" stroke="#bbb" stroke-width="3"/>\x27;
4026
+ floorSvg += \x27<ellipse cx="\x27+(FW/2)+\x27" cy="38" rx="50" ry="14" fill="#e8d960" stroke="#c8b030" stroke-width="2"/>\x27;
4027
+ floorSvg += \x27<circle cx="\x27+(FW/2-28)+\x27" cy="46" r="8" fill="#fffde0" filter="url(#bGlow2)"/>\x27;
4028
+ floorSvg += \x27<circle cx="\x27+(FW/2)+\x27" cy="50" r="8" fill="#fffde0" filter="url(#bGlow2)"/>\x27;
4029
+ floorSvg += \x27<circle cx="\x27+(FW/2+28)+\x27" cy="46" r="8" fill="#fffde0" filter="url(#bGlow2)"/>\x27;
4030
+ // Light cone
4031
+ floorSvg += \x27<polygon points="\x27+(FW/2-60)+\x27,60 \x27+(FW/2+60)+\x27,60 \x27+(FW/2+160)+\x27,\x27+FH+\x27 \x27+(FW/2-160)+\x27,\x27+FH+\x27" fill="rgba(255,252,200,.06)"/>\x27;
4032
+ floorSvg += \x27</svg>\x27;
4033
+
4034
+ // Decorations (CSS positioned, z-index above floor SVG)
4035
+ var decoHtml =
4036
+ \x27<div style="position:absolute;bottom:10px;left:12px;font-size:40px;line-height:1;filter:drop-shadow(0 3px 6px rgba(0,0,0,.25));z-index:5">\x27+bigPlant+\x27</div>\x27+
4037
+ \x27<div style="position:absolute;bottom:10px;right:12px;font-size:40px;line-height:1;filter:drop-shadow(0 3px 6px rgba(0,0,0,.25));z-index:5">\x27+bigPlant+\x27</div>\x27+
4038
+ \x27<div style="position:absolute;top:12px;left:310px;font-size:24px;line-height:1;z-index:5">\x27+plantEmoji+\x27</div>\x27+
4039
+ \x27<div style="position:absolute;top:12px;right:230px;font-size:24px;line-height:1;z-index:5">\x27+plantEmoji+\x27</div>\x27;
3921
4040
 
3922
4041
  el.innerHTML =
3923
4042
  \x27<div class="prl-wrap" style="border-color:\x27+phaseColor2+\x2744;padding-bottom:8px">\x27+
3924
4043
  \x27<div class="prl-header"><span class="prl-phase-chip" style="--pc:\x27+phaseColor2+\x27">\x27+phaseLabel2+\x27</span></div>\x27+
3925
- \x27<div style="display:flex;justify-content:center;width:100%;overflow-x:auto">\x27+
3926
- \x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;min-width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px;overflow:hidden;border-radius:12px;background:#f0ede6">\x27+
3927
- isoFloorSvg(ISO_COLS + 2, 5)+
3928
- agentsHtml+
3929
- orchHtml+
4044
+ \x27<div class="iso-scene" style="position:relative">\x27+
4045
+ floorSvg+
4046
+ decoHtml+
4047
+ // Grid overlay for stations
4048
+ \x27<div style="position:relative;z-index:10;display:grid;grid-template-columns:\x27+gridTpl+\x27;gap:0;padding:12px 16px;box-sizing:border-box;align-items:end;min-height:440px">\x27+
4049
+ // Push agents to bottom half of scene (wall takes top ~30%)
4050
+ \x27<div style="grid-column:1/-1;height:calc(30% - 12px)"></div>\x27+
4051
+ stationsHtml+
3930
4052
  \x27</div>\x27+
3931
4053
  \x27</div>\x27+
3932
4054
  \x27</div>\x27;
@@ -6533,7 +6655,26 @@ input:focus,textarea:focus{border-color:var(--green3)}
6533
6655
  /* Plant right */
6534
6656
  .prl-office-plant2{position:absolute;bottom:14px;right:8px;width:22px;height:42px;z-index:2;pointer-events:none}
6535
6657
  /* ── ISOMETRIC JRPG SCENE ── */
6536
- .iso-scene{background:#f0ede6;cursor:default;max-width:100%;overflow-x:auto;box-shadow:0 4px 24px rgba(0,0,0,.18)}
6658
+ .iso-scene-wrap{width:100%;overflow-x:auto;display:flex;justify-content:center}
6659
+ .iso-scene{background:#f0ede6;cursor:default;overflow:hidden;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.18);width:100%}
6660
+ /* ── Station grid layout ── */
6661
+ .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}
6662
+ .iso-station:hover{filter:brightness(1.06);transform:translateY(-2px)}
6663
+ .iso-station.iso-orch-walking{animation:orchWalkGrid 1.8s ease-in-out infinite alternate}
6664
+ @keyframes orchWalkGrid{0%{transform:translateX(0) scale(1)}100%{transform:translateX(var(--orch-tx,24px)) scale(1.04)}}
6665
+ .iso-station.iso-orch-done{animation:orchBounce .7s ease forwards}
6666
+ @keyframes orchBounce{0%{transform:scale(1)}40%{transform:scale(1.18) translateY(-6px)}100%{transform:scale(1)}}
6667
+ /* Desk — CSS 3D look */
6668
+ .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}
6669
+ .iso-desk::after{content:'';position:absolute;bottom:-4px;left:8px;right:8px;height:4px;background:#7a5010;border-radius:0 0 3px 3px}
6670
+ /* Monitor — sits on desk */
6671
+ .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}
6672
+ .iso-monitor::after{content:'';position:absolute;bottom:-5px;left:50%;transform:translateX(-50%);width:10px;height:5px;background:#252436;border-radius:0 0 3px 3px}
6673
+ .iso-monitor-screen{width:44px;height:28px;background:rgba(60,40,160,.35);border-radius:2px;display:flex;align-items:center;justify-content:center}
6674
+ .iso-monitor-blink{width:7px;height:7px;border-radius:50%;background:#6366f1;animation:monBlink .9s ease-in-out infinite}
6675
+ @keyframes monBlink{0%,100%{opacity:1;box-shadow:0 0 8px #6366f1}50%{opacity:.25;box-shadow:none}}
6676
+ /* Tool badge */
6677
+ .iso-tool-badge{font-size:22px;line-height:1;filter:drop-shadow(0 2px 5px rgba(0,0,0,.35));user-select:none;margin-bottom:1px}
6537
6678
  /* Animated status chip for [bracket tokens] */
6538
6679
  .iso-status-chip{display:inline-flex;align-items:center;gap:6px;background:rgba(99,102,241,.1);border:1px solid rgba(99,102,241,.3);border-radius:20px;padding:3px 10px;font-size:11px;font-family:var(--mono);color:#818cf8;animation:statusPulse 2s ease-in-out infinite}
6539
6680
  @keyframes statusPulse{0%,100%{opacity:.7;border-color:rgba(99,102,241,.3)}50%{opacity:1;border-color:rgba(99,102,241,.7);box-shadow:0 0 8px rgba(99,102,241,.3)}}
@@ -6546,7 +6687,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
6546
6687
  .iso-orch-wrap{transition:transform .2s}
6547
6688
  .iso-orch-wrap.prl-head{animation:isoCharBob 1.4s ease-in-out infinite}
6548
6689
  /* Thought bubble / speech bubble above character */
6549
- .iso-bubble{font-size:8px;font-family:var(--mono);padding:2px 7px;border-radius:10px 10px 10px 2px;border:1px solid #3a3060;background:rgba(10,8,20,.85);color:#6b7280;white-space:nowrap;max-width:110px;overflow:hidden;text-overflow:ellipsis;line-height:1.4;transition:all .25s;pointer-events:none;backdrop-filter:blur(4px)}
6690
+ .iso-bubble{font-size:9px;font-family:var(--mono);padding:3px 9px;border-radius:12px;border:1px solid #ccc;background:rgba(255,255,255,.9);color:#6b7280;white-space:nowrap;max-width:130px;overflow:hidden;text-overflow:ellipsis;line-height:1.5;transition:all .25s;pointer-events:none;backdrop-filter:blur(6px);text-align:center}
6550
6691
  .iso-bubble--active{background:rgba(30,20,60,.9);border-color:#6366f1;color:#a5b4fc;animation:isoBubblePop .35s ease;white-space:normal;max-width:110px;word-break:break-word;line-height:1.35}
6551
6692
  .iso-bubble--orch{font-size:9px;padding:3px 9px;border-radius:12px;border-color:#818cf8;color:#a5b4fc;background:rgba(20,15,50,.9)}
6552
6693
  @keyframes isoBubblePop{0%{transform:scale(.8) translateY(4px);opacity:.4}100%{transform:scale(1) translateY(0);opacity:1}}