// studio-editor.jsx — Full Studio editor mockup (Blender/Unity-style dark IDE)

const EDITOR_TOKENS = (dark) => ({
  // Editor uses its own dark palette regardless of site theme
  bg:      'oklch(14% 0.005 250)',
  panel:   'oklch(17% 0.005 250)',
  panel2:  'oklch(20% 0.005 250)',
  border:  'oklch(26% 0.005 250)',
  borderS: 'oklch(22% 0.005 250)',
  text:    'oklch(92% 0.005 250)',
  muted:   'oklch(60% 0.008 250)',
  faint:   'oklch(42% 0.005 250)',
  accent:  'oklch(78% 0.17 130)',
  accentInk: 'oklch(15% 0.05 130)',
  selected:  'oklch(72% 0.16 230)',
  selectedBg: 'oklch(28% 0.08 230)',
  error:   'oklch(70% 0.18 25)',
  warn:    'oklch(78% 0.16 80)',
  ok:      'oklch(72% 0.16 145)',
  viewport: 'oklch(20% 0.008 250)',
});

// Collaborator avatars (no real users — just initials with hue)
const COLLAB = [
  { id: 'a', initial: 'M', hue: 18,  cursor: { x: 62, y: 38 } },
  { id: 'b', initial: 'K', hue: 285, cursor: { x: 28, y: 70 } },
];

// Scene tree — game scene (character, obby)
const SCENE_TREE = [
  { id: 'world', label: 'Workspace',     icon: '◈', depth: 0, expanded: true, children: [
    { id: 'spawn',     label: 'SpawnLocation',  icon: '◇', depth: 1 },
    { id: 'baseplate', label: 'Baseplate',      icon: '▣', depth: 1 },
    { id: 'obby',      label: 'ObbyCourse',     icon: '◈', depth: 1, expanded: true, children: [
      { id: 'p1', label: 'Platform_01', icon: '▢', depth: 2 },
      { id: 'p2', label: 'Platform_02', icon: '▢', depth: 2, selected: true },
      { id: 'p3', label: 'Platform_03', icon: '▢', depth: 2 },
      { id: 'p4', label: 'KillBrick',   icon: '▢', depth: 2, badge: 'script' },
      { id: 'goal', label: 'GoalPad',   icon: '◉', depth: 2 },
    ]},
    { id: 'char', label: 'Character',  icon: '◉', depth: 1, badge: 'rig' },
  ]},
  { id: 'players',  label: 'Players',    icon: '◇', depth: 0 },
  { id: 'lighting', label: 'Lighting',   icon: '✦', depth: 0, expanded: true, children: [
    { id: 'sun',  label: 'Sun',     icon: '☼', depth: 1 },
    { id: 'amb',  label: 'Ambient', icon: '○', depth: 1 },
    { id: 'sky',  label: 'Sky',     icon: '◐', depth: 1 },
  ]},
  { id: 'sound',    label: 'SoundService', icon: '♪', depth: 0 },
  { id: 'replic',   label: 'ReplicatedStorage', icon: '⇄', depth: 0 },
  { id: 'scripts',  label: 'ServerScriptService', icon: '▤', depth: 0, expanded: true, children: [
    { id: 'jump_lua', label: 'PlayerJump.lua', icon: '⟨⟩', depth: 1, badge: 'lua' },
    { id: 'kill_lua', label: 'KillBrick.lua',  icon: '⟨⟩', depth: 1, badge: 'lua' },
  ]},
];

// Inspector for the selected Platform_02
const INSPECTOR_PROPS = {
  Transform: [
    ['Position', 'vec3', '12.40, 4.50, -8.00'],
    ['Rotation', 'vec3', '0.00, 45.00, 0.00'],
    ['Size',     'vec3', '8.00, 1.00, 8.00'],
  ],
  Appearance: [
    ['Material', 'enum',  'Plastic'],
    ['Color',    'color', 'oklch(72% 0.16 30)'],
    ['Transparency', 'num', '0.00'],
    ['CastShadow',   'bool', true],
  ],
  Physics: [
    ['Anchored',   'bool', true],
    ['CanCollide', 'bool', true],
    ['Mass',       'num',  '128.00'],
  ],
  Behavior: [
    ['Tag',  'str', 'platform'],
    ['Layer', 'enum', 'Default'],
  ],
};

// Lua sample (jump)
const LUA_SOURCE = [
  ['c', '-- PlayerJump.lua  ·  server'],
  ['c', '-- handles jump input + sound feedback'],
  [' ', ''],
  ['k', 'local', ' Players ', '=', ' craftix.', 'i', 'Players'],
  ['k', 'local', ' Input   ', '=', ' craftix.', 'i', 'Input'],
  ['k', 'local', ' Sound   ', '=', ' craftix.', 'i', 'Sound'],
  [' ', ''],
  ['i', 'Input', ':', 'f', 'OnAction', '(', 's', '"jump"', ',', ' ', 'k', 'function', '(', 'a', 'player', ')'],
  ['  ', 'k', 'local', ' char ', '=', ' ', 'a', 'player', '.', 'p', 'character'],
  ['  ', 'k', 'if', ' char.', 'p', 'grounded', ' ', 'k', 'then'],
  ['    ', 'i', 'char', ':', 'f', 'ApplyImpulse', '(', 'i', 'Vector3', '.', 'p', 'up', ' * ', 'n', '32', ')'],
  ['    ', 'i', 'Sound', ':', 'f', 'Play', '(', 's', '"woosh"', ',', ' { ', 'p', 'pitch', ' = ', 'n', '1.05', ' })'],
  ['  ', 'k', 'end'],
  ['k', 'end', ')'],
];
const LUA_COLORS = (e) => ({
  c: e.muted, k: 'oklch(75% 0.13 320)', f: 'oklch(82% 0.15 80)',
  s: 'oklch(78% 0.14 145)', n: 'oklch(80% 0.13 30)',
  i: 'oklch(78% 0.13 230)', p: e.text, a: 'oklch(85% 0.04 250)', ' ': e.text,
});

const CONSOLE_LOG = [
  { t: 'info', time: '14:02:18', msg: 'Build started · target: Win64' },
  { t: 'info', time: '14:02:19', msg: 'Compiled 4 scripts · 0 errors' },
  { t: 'ok',   time: '14:02:21', msg: 'Server up · port 50327 · 1 client' },
  { t: 'info', time: '14:02:21', msg: 'Player_M joined as @marina' },
  { t: 'warn', time: '14:02:34', msg: 'KillBrick.lua:18  unused variable "_dt"' },
  { t: 'info', time: '14:02:40', msg: 'Selection: Workspace.ObbyCourse.Platform_02' },
];

function StudioEditor({ t: siteT, str, dark, lang, setLang, navigate, embedded }) {
  const e = EDITOR_TOKENS(true);
  const [tool, setTool] = React.useState('move');
  const [tab, setTab] = React.useState('output'); // output | errors | lua
  const [bottomTab, setBottomTab] = React.useState('console'); // console | assets
  const [running, setRunning] = React.useState(false);
  const [tick, setTick] = React.useState(0);
  const [tree, setTree] = React.useState(SCENE_TREE);
  const [selectedId, setSelectedId] = React.useState('p2');
  const [logs, setLogs] = React.useState(CONSOLE_LOG);
  const [cmd, setCmd] = React.useState('');

  React.useEffect(() => {
    const id = setInterval(() => setTick(x => x + 1), 50);
    return () => clearInterval(id);
  }, []);

  const toggleNode = (id) => {
    const walk = (nodes) => nodes.map(n => {
      if (n.id === id) return { ...n, expanded: !n.expanded };
      if (n.children) return { ...n, children: walk(n.children) };
      return n;
    });
    setTree(walk(tree));
  };
  const findNode = (nodes, id) => {
    for (const n of nodes) {
      if (n.id === id) return n;
      if (n.children) {
        const r = findNode(n.children, id);
        if (r) return r;
      }
    }
    return null;
  };
  const selected = findNode(tree, selectedId) || { label: '—' };

  const runCmd = () => {
    if (!cmd.trim()) return;
    setLogs(l => [...l, { t: 'cmd', time: new Date().toTimeString().slice(0, 8), msg: '> ' + cmd }, { t: 'info', time: new Date().toTimeString().slice(0, 8), msg: 'nil' }]);
    setCmd('');
  };

  const wrapper = embedded
    ? { width: '100%', height: 720, borderRadius: 14, overflow: 'hidden', border: `1px solid ${e.border}` }
    : { width: '100%', minHeight: '100vh' };

  return (
    <div style={{ ...wrapper, background: e.bg, color: e.text, fontFamily: '"Inter", system-ui, sans-serif', display: 'flex', flexDirection: 'column' }}>
      {/* ── Top menu bar ─────────────────────────────── */}
      <div style={{
        height: 36, display: 'flex', alignItems: 'center', gap: 0,
        background: e.panel, borderBottom: `1px solid ${e.border}`, padding: '0 8px',
        fontSize: 12,
      }}>
        {!embedded && (
          <div onClick={() => navigate && navigate('home')} style={{ cursor: 'pointer', marginRight: 12, paddingLeft: 4 }}>
            <span style={{ fontFamily: '"Inter Tight", sans-serif', fontWeight: 700, fontSize: 13, letterSpacing: '-0.01em', color: e.text }}>craftix</span>
            <span style={{ color: e.muted, fontSize: 11, marginLeft: 6 }}>Studio</span>
          </div>
        )}
        {['File', 'Edit', 'View', 'Insert', 'Build', 'Plugins', 'Help'].map(m => (
          <button key={m} style={{
            background: 'transparent', border: 'none', color: e.text,
            padding: '6px 10px', fontSize: 12, cursor: 'pointer', fontFamily: 'inherit',
            borderRadius: 4,
          }} onMouseEnter={(ev) => ev.currentTarget.style.background = e.panel2} onMouseLeave={(ev) => ev.currentTarget.style.background = 'transparent'}>{m}</button>
        ))}
        <div style={{ flex: 1 }} />
        {/* Project name */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, color: e.muted, fontSize: 11, fontFamily: 'ui-monospace, monospace', padding: '0 12px' }}>
          <span>obby_demo</span>
          <span style={{ color: e.faint }}>/</span>
          <span style={{ color: e.text }}>main.craft</span>
          <span style={{ color: e.warn, marginLeft: 4 }}>●</span>
        </div>
        {/* Collaborators */}
        <div style={{ display: 'flex', alignItems: 'center', gap: -6, marginRight: 12 }}>
          {COLLAB.map((c, i) => (
            <div key={c.id} style={{
              width: 24, height: 24, borderRadius: 12,
              background: `oklch(70% 0.16 ${c.hue})`,
              color: 'oklch(15% 0.05 ' + c.hue + ')',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 10, fontWeight: 700, fontFamily: 'ui-monospace, monospace',
              border: `2px solid ${e.panel}`,
              marginLeft: i === 0 ? 0 : -6,
            }}>{c.initial}</div>
          ))}
          <div style={{
            marginLeft: 8, fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace',
            textTransform: 'uppercase', letterSpacing: '0.06em',
          }}>2 online</div>
        </div>
        {/* Run / publish */}
        <div style={{ display: 'flex', gap: 4 }}>
          <button onClick={() => setRunning(!running)} style={{
            height: 24, padding: '0 10px', borderRadius: 5, border: 'none',
            background: running ? e.error : e.accent,
            color: running ? 'oklch(95% 0.02 25)' : e.accentInk,
            fontSize: 11, fontWeight: 600, cursor: 'pointer', fontFamily: 'inherit',
            display: 'flex', alignItems: 'center', gap: 5,
          }}>{running ? '■ Stop' : '▶ Play'}</button>
          <button style={{
            height: 24, padding: '0 10px', borderRadius: 5,
            border: `1px solid ${e.border}`, background: 'transparent',
            color: e.text, fontSize: 11, fontWeight: 500, cursor: 'pointer', fontFamily: 'inherit',
          }}>↑ Publish</button>
        </div>
      </div>

      {/* ── Toolbar ───────────────────────────────────── */}
      <div style={{
        height: 40, display: 'flex', alignItems: 'center', gap: 4,
        background: e.bg, borderBottom: `1px solid ${e.border}`, padding: '0 10px',
      }}>
        {[
          { k: 'select', i: '⌘', label: 'Select' },
          { k: 'move',   i: '⇕', label: 'Move (W)' },
          { k: 'rotate', i: '↻', label: 'Rotate (E)' },
          { k: 'scale',  i: '⤢', label: 'Scale (R)' },
        ].map(it => (
          <ToolBtn key={it.k} e={e} active={tool === it.k} onClick={() => setTool(it.k)} icon={it.i} label={it.label} />
        ))}
        <div style={{ width: 1, height: 22, background: e.border, margin: '0 6px' }} />
        {[
          { k: 'brush',   i: '◐', label: 'Terrain brush' },
          { k: 'paint',   i: '◉', label: 'Paint material' },
          { k: 'snap',    i: '⌗', label: 'Snap to grid' },
        ].map(it => <ToolBtn key={it.k} e={e} icon={it.i} label={it.label} />)}
        <div style={{ flex: 1 }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, color: e.muted, fontSize: 11, fontFamily: 'ui-monospace, monospace' }}>
          <span>grid: 1.0u</span>
          <span style={{ color: e.faint }}>·</span>
          <span>snap: on</span>
        </div>
      </div>

      {/* ── Body: Hierarchy | Viewport | Inspector ─── */}
      <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '240px 1fr 280px', minHeight: 0 }}>
        {/* Hierarchy */}
        <div style={{ background: e.panel, borderRight: `1px solid ${e.border}`, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
          <PanelHeader e={e} title="Explorer" actions={[['+', 'Add'], ['⚙', 'Filter']]} />
          <div style={{ padding: '6px 4px', flex: 1, overflowY: 'auto', minHeight: 0 }}>
            <TreeRender nodes={tree} e={e} onToggle={toggleNode} selectedId={selectedId} onSelect={setSelectedId} />
          </div>
          {/* Properties tree at bottom */}
          <div style={{ borderTop: `1px solid ${e.border}` }}>
            <PanelHeader e={e} title="Toolbox" small />
            <div style={{ padding: 8, display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4 }}>
              {[18, 50, 200, 285, 130, 30, 270, 340].map((hue, i) => (
                <div key={i} style={{
                  aspectRatio: '1', borderRadius: 5, overflow: 'hidden',
                  border: `1px solid ${e.borderS}`, cursor: 'grab',
                }}>
                  <Placeholder hue={hue} pattern={['stripes','grid','dots','noise','stripes','grid','dots','noise'][i]} dark={true} radius={4} />
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Viewport */}
        <div style={{ position: 'relative', background: e.viewport, overflow: 'hidden', minHeight: 0 }}>
          <Viewport e={e} tick={tick} running={running} />
          {/* Top-left HUD: scene info */}
          <div style={{
            position: 'absolute', top: 10, left: 10, padding: '5px 9px',
            background: 'rgba(0,0,0,0.55)', borderRadius: 5, backdropFilter: 'blur(6px)',
            fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace',
            display: 'flex', gap: 10,
          }}>
            <span>persp</span>
            <span>·</span>
            <span>1920×1080</span>
            <span>·</span>
            <span style={{ color: e.ok }}>{(60 - (tick % 7)).toString()} fps</span>
          </div>
          {/* Top-right HUD: gizmo */}
          <div style={{ position: 'absolute', top: 10, right: 10, padding: 6, background: 'rgba(0,0,0,0.55)', borderRadius: 6, backdropFilter: 'blur(6px)' }}>
            <AxisGizmo e={e} tick={tick} />
          </div>
          {/* Bottom-right: zoom controls */}
          <div style={{ position: 'absolute', bottom: 10, right: 10, display: 'flex', flexDirection: 'column', gap: 2, background: 'rgba(0,0,0,0.55)', borderRadius: 6, padding: 4, backdropFilter: 'blur(6px)' }}>
            {['+', '⌘', '−'].map((s, i) => (
              <button key={i} style={{
                width: 26, height: 26, background: 'transparent', border: 'none',
                color: e.text, cursor: 'pointer', fontSize: 13, borderRadius: 4,
              }} onMouseEnter={(ev) => ev.currentTarget.style.background = 'rgba(255,255,255,0.08)'} onMouseLeave={(ev) => ev.currentTarget.style.background = 'transparent'}>{s}</button>
            ))}
          </div>
          {/* Collaborator cursors */}
          {COLLAB.map((c, i) => (
            <div key={c.id} style={{
              position: 'absolute',
              left: `${c.cursor.x + Math.sin((tick + i * 50) / 30) * 3}%`,
              top: `${c.cursor.y + Math.cos((tick + i * 50) / 35) * 2}%`,
              transition: 'all 0.05s linear',
              pointerEvents: 'none',
            }}>
              <svg width="18" height="18" viewBox="0 0 18 18" fill={`oklch(70% 0.16 ${c.hue})`}>
                <path d="M2 2 L2 14 L6 10 L9 16 L11 15 L8 9 L14 9 Z" stroke="white" strokeWidth="1" />
              </svg>
              <div style={{
                marginLeft: 12, marginTop: -4, padding: '2px 6px',
                background: `oklch(70% 0.16 ${c.hue})`, color: 'oklch(15% 0.05 ' + c.hue + ')',
                fontSize: 9, borderRadius: 3, fontWeight: 600, fontFamily: 'ui-monospace, monospace',
                display: 'inline-block',
              }}>{c.initial === 'M' ? 'marina' : 'kostya'}</div>
            </div>
          ))}
        </div>

        {/* Inspector */}
        <div style={{ background: e.panel, borderLeft: `1px solid ${e.border}`, display: 'flex', flexDirection: 'column', minHeight: 0, overflowY: 'auto' }}>
          <PanelHeader e={e} title="Properties" />
          <div style={{ padding: 12, borderBottom: `1px solid ${e.border}` }}>
            <div style={{ fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace', textTransform: 'uppercase', letterSpacing: '0.06em' }}>selected</div>
            <div style={{ marginTop: 4, fontSize: 14, fontWeight: 600, color: e.text, fontFamily: 'ui-monospace, monospace' }}>{selected.label}</div>
            <div style={{ marginTop: 2, fontSize: 11, color: e.faint, fontFamily: 'ui-monospace, monospace' }}>part · #a3f2c1</div>
          </div>
          {Object.entries(INSPECTOR_PROPS).map(([group, rows]) => (
            <PropGroup key={group} e={e} title={group} rows={rows} />
          ))}
        </div>
      </div>

      {/* ── Bottom panel: Console / Assets ─────────────── */}
      <div style={{ height: 220, background: e.panel, borderTop: `1px solid ${e.border}`, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
        <div style={{ height: 30, display: 'flex', alignItems: 'center', borderBottom: `1px solid ${e.borderS}`, padding: '0 4px' }}>
          {[
            { k: 'console', label: 'Console',     count: 0 },
            { k: 'errors',  label: 'Errors',      count: 0, color: e.error },
            { k: 'warn',    label: 'Warnings',    count: 1, color: e.warn },
            { k: 'lua',     label: 'PlayerJump.lua' },
          ].map(tb => (
            <button key={tb.k} onClick={() => setBottomTab(tb.k === 'lua' ? 'lua' : 'console') || setTab(tb.k)} style={{
              height: 30, padding: '0 12px', border: 'none', background: 'transparent',
              color: tab === tb.k ? e.text : e.muted, fontSize: 11, cursor: 'pointer', fontFamily: 'inherit',
              borderBottom: `2px solid ${tab === tb.k ? e.accent : 'transparent'}`,
              display: 'flex', alignItems: 'center', gap: 6, fontWeight: 500,
            }}>
              {tb.label}
              {tb.count != null && tb.count > 0 && (
                <span style={{ background: tb.color || e.muted, color: e.bg, fontSize: 9, padding: '1px 5px', borderRadius: 3, fontWeight: 600 }}>{tb.count}</span>
              )}
            </button>
          ))}
          <div style={{ flex: 1 }} />
          <button onClick={() => setBottomTab('assets')} style={{
            height: 24, padding: '0 8px', border: `1px solid ${bottomTab === 'assets' ? e.accent : e.border}`,
            background: bottomTab === 'assets' ? e.accent : 'transparent',
            color: bottomTab === 'assets' ? e.accentInk : e.muted, fontSize: 10, cursor: 'pointer', fontFamily: 'inherit',
            borderRadius: 4, marginRight: 6, fontWeight: 500,
          }}>▦ Asset Library</button>
          <button onClick={() => setLogs([])} style={{
            height: 24, padding: '0 8px', border: 'none', background: 'transparent',
            color: e.muted, fontSize: 10, cursor: 'pointer', fontFamily: 'inherit',
          }}>clear</button>
        </div>

        {/* Bottom content */}
        {bottomTab === 'assets' ? (
          <AssetLibrary e={e} />
        ) : tab === 'lua' ? (
          <LuaEditor e={e} />
        ) : (
          <ConsolePanel e={e} logs={logs} cmd={cmd} setCmd={setCmd} runCmd={runCmd} filter={tab} />
        )}
      </div>

      {/* ── Status bar ─────────────────────────────── */}
      <div style={{
        height: 22, background: e.panel2, borderTop: `1px solid ${e.border}`,
        display: 'flex', alignItems: 'center', padding: '0 10px',
        fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace', gap: 14,
      }}>
        <span style={{ color: e.ok }}>● ready</span>
        <span>{(60 - (tick % 7)).toString()} fps</span>
        <span>cpu 12%</span>
        <span>mem 248mb</span>
        <span>tris 4,820</span>
        <div style={{ flex: 1 }} />
        <span>tool: <span style={{ color: e.text }}>{tool}</span></span>
        <span>selection: <span style={{ color: e.text }}>{selected.label}</span></span>
        <span>mode: <span style={{ color: e.text }}>edit</span></span>
        <span>Lua 5.4</span>
        <span style={{ color: e.faint }}>v0.7.2</span>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
function PanelHeader({ e, title, actions, small }) {
  return (
    <div style={{
      height: small ? 26 : 30, padding: '0 10px',
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      borderBottom: `1px solid ${e.borderS}`,
      fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace',
      textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 600,
    }}>
      <span>{title}</span>
      {actions && (
        <div style={{ display: 'flex', gap: 2 }}>
          {actions.map(([icon, lbl], i) => (
            <button key={i} title={lbl} style={{
              width: 18, height: 18, background: 'transparent', border: 'none',
              color: e.muted, cursor: 'pointer', fontSize: 11, borderRadius: 3,
            }}>{icon}</button>
          ))}
        </div>
      )}
    </div>
  );
}

function ToolBtn({ e, active, onClick, icon, label }) {
  return (
    <button onClick={onClick} title={label} style={{
      width: 28, height: 28, borderRadius: 5, border: 'none',
      background: active ? e.accent : 'transparent',
      color: active ? e.accentInk : e.text,
      fontSize: 14, cursor: 'pointer', fontFamily: 'inherit',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }} onMouseEnter={(ev) => { if (!active) ev.currentTarget.style.background = e.panel2; }}
       onMouseLeave={(ev) => { if (!active) ev.currentTarget.style.background = 'transparent'; }}>
      {icon}
    </button>
  );
}

function TreeRender({ nodes, e, onToggle, selectedId, onSelect }) {
  const flatten = (nodes, out = []) => {
    for (const n of nodes) {
      out.push(n);
      if (n.expanded && n.children) flatten(n.children, out);
    }
    return out;
  };
  const flat = flatten(nodes);
  return (
    <div>
      {flat.map(n => (
        <div key={n.id} onClick={() => onSelect(n.id)} style={{
          display: 'flex', alignItems: 'center', gap: 4,
          height: 22, padding: `0 6px 0 ${6 + n.depth * 12}px`,
          background: selectedId === n.id ? e.selectedBg : 'transparent',
          color: selectedId === n.id ? e.selected : e.text,
          fontSize: 12, fontFamily: 'ui-monospace, monospace', cursor: 'pointer',
          borderLeft: selectedId === n.id ? `2px solid ${e.selected}` : '2px solid transparent',
        }}>
          <span onClick={(ev) => { if (n.children) { ev.stopPropagation(); onToggle(n.id); } }} style={{
            width: 12, color: e.muted, fontSize: 9, cursor: n.children ? 'pointer' : 'default',
          }}>{n.children ? (n.expanded ? '▾' : '▸') : ''}</span>
          <span style={{ color: e.muted, fontSize: 11, width: 14 }}>{n.icon}</span>
          <span style={{ flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{n.label}</span>
          {n.badge && (
            <span style={{
              fontSize: 8, padding: '1px 4px', borderRadius: 2,
              background: e.borderS, color: e.muted, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em',
            }}>{n.badge}</span>
          )}
        </div>
      ))}
    </div>
  );
}

function PropGroup({ e, title, rows }) {
  const [open, setOpen] = React.useState(true);
  return (
    <div style={{ borderBottom: `1px solid ${e.borderS}` }}>
      <div onClick={() => setOpen(!open)} style={{
        height: 26, padding: '0 10px',
        display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer',
        fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace',
        textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600,
      }}>
        <span style={{ width: 10, fontSize: 9 }}>{open ? '▾' : '▸'}</span>
        <span>{title}</span>
      </div>
      {open && (
        <div style={{ padding: '4px 10px 10px' }}>
          {rows.map(([label, type, val]) => (
            <PropRow key={label} e={e} label={label} type={type} val={val} />
          ))}
        </div>
      )}
    </div>
  );
}
function PropRow({ e, label, type, val }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '90px 1fr', gap: 6, alignItems: 'center', padding: '3px 0' }}>
      <span style={{ fontSize: 11, color: e.muted, fontFamily: 'ui-monospace, monospace' }}>{label}</span>
      {type === 'bool' ? (
        <div style={{
          width: 26, height: 14, borderRadius: 7, background: val ? e.accent : e.border, position: 'relative',
        }}>
          <div style={{ position: 'absolute', top: 1, left: val ? 13 : 1, width: 12, height: 12, borderRadius: 6, background: e.bg }} />
        </div>
      ) : type === 'color' ? (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ width: 16, height: 16, borderRadius: 3, background: val, border: `1px solid ${e.border}` }} />
          <span style={{ fontSize: 11, color: e.text, fontFamily: 'ui-monospace, monospace' }}>#a85b32</span>
        </div>
      ) : (
        <div style={{
          height: 22, padding: '0 7px', background: e.bg, border: `1px solid ${e.borderS}`, borderRadius: 4,
          display: 'flex', alignItems: 'center', fontSize: 11, fontFamily: 'ui-monospace, monospace',
          color: type === 'enum' ? e.selected : e.text,
        }}>{String(val)}</div>
      )}
    </div>
  );
}

// ── 3D viewport (fake isometric scene) ───────────
function Viewport({ e, tick, running }) {
  const sway = Math.sin(tick / 60) * 2;
  return (
    <svg viewBox="0 0 1200 600" style={{ width: '100%', height: '100%', display: 'block' }} preserveAspectRatio="xMidYMid slice">
      <defs>
        <pattern id="grid-fine" width="20" height="20" patternUnits="userSpaceOnUse" patternTransform="skewX(-30) scale(1, 0.55)">
          <path d="M 20 0 L 0 0 0 20" fill="none" stroke={e.borderS} strokeWidth="0.5" />
        </pattern>
        <pattern id="grid-thick" width="100" height="100" patternUnits="userSpaceOnUse" patternTransform="skewX(-30) scale(1, 0.55)">
          <path d="M 100 0 L 0 0 0 100" fill="none" stroke={e.border} strokeWidth="1" />
        </pattern>
        <linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="oklch(28% 0.02 250)" />
          <stop offset="100%" stopColor="oklch(20% 0.008 250)" />
        </linearGradient>
        <linearGradient id="plat-top" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="oklch(72% 0.16 30)" />
          <stop offset="100%" stopColor="oklch(60% 0.16 30)" />
        </linearGradient>
        <linearGradient id="plat-side" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="oklch(50% 0.14 30)" />
          <stop offset="100%" stopColor="oklch(38% 0.12 30)" />
        </linearGradient>
        <linearGradient id="char-body" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="oklch(78% 0.14 145)" />
          <stop offset="100%" stopColor="oklch(58% 0.14 145)" />
        </linearGradient>
      </defs>

      <rect width="1200" height="600" fill="url(#sky)" />
      {/* Ground grid */}
      <g transform="translate(0, 320)">
        <rect width="1200" height="280" fill="url(#grid-fine)" />
        <rect width="1200" height="280" fill="url(#grid-thick)" />
      </g>

      {/* Baseplate */}
      <IsoBox x={600} y={420} w={900} h={20} d={500} topFill="oklch(28% 0.008 250)" sideFill="oklch(20% 0.008 250)" e={e} />

      {/* Obby platforms */}
      <IsoBox x={380} y={360} w={120} d={120} h={40} topFill="url(#plat-top)" sideFill="url(#plat-side)" e={e} />
      <IsoBox x={560} y={310} w={120} d={120} h={40} topFill="url(#plat-top)" sideFill="url(#plat-side)" e={e} selected />
      <IsoBox x={740} y={260} w={120} d={120} h={40} topFill="url(#plat-top)" sideFill="url(#plat-side)" e={e} />
      <IsoBox x={920} y={210} w={120} d={120} h={40} topFill="oklch(60% 0.18 25)" sideFill="oklch(42% 0.15 25)" e={e} />
      {/* Goal pad */}
      <IsoBox x={1080} y={170} w={140} d={140} h={20} topFill="oklch(78% 0.16 130)" sideFill="oklch(55% 0.15 130)" e={e} />

      {/* Character (simple stack) */}
      <g transform={`translate(${380 + sway}, ${280})`}>
        {/* shadow */}
        <ellipse cx="0" cy="80" rx="34" ry="9" fill="black" opacity="0.4" />
        {/* legs */}
        <rect x="-14" y="40" width="12" height="40" fill="oklch(40% 0.06 250)" />
        <rect x="2"   y="40" width="12" height="40" fill="oklch(40% 0.06 250)" />
        {/* body */}
        <rect x="-20" y="0" width="40" height="44" fill="url(#char-body)" rx="2" />
        {/* head */}
        <rect x="-16" y="-32" width="32" height="32" fill="oklch(82% 0.1 60)" rx="3" />
        {/* eyes */}
        <rect x="-9" y="-22" width="3" height="5" fill="black" />
        <rect x="6" y="-22" width="3" height="5" fill="black" />
        {/* arms (sway with running) */}
        <rect x="-26" y="2" width="8" height="32" fill="url(#char-body)" transform={`rotate(${running ? Math.sin(tick / 5) * 25 : 0} -22 4)`} />
        <rect x="18" y="2" width="8" height="32" fill="url(#char-body)" transform={`rotate(${running ? -Math.sin(tick / 5) * 25 : 0} 22 4)`} />
      </g>

      {/* Selection gizmo on Platform_02 */}
      <g transform="translate(560, 310)">
        {/* selection outline */}
        <polygon points="-60,0 0,-30 60,0 0,30" fill="none" stroke={e.selected} strokeWidth="2" strokeDasharray="6 3" />
        {/* move arrows */}
        <line x1="0" y1="0" x2="0" y2="-80" stroke="oklch(72% 0.18 145)" strokeWidth="3" />
        <polygon points="0,-86 -5,-76 5,-76" fill="oklch(72% 0.18 145)" />
        <line x1="0" y1="0" x2="80" y2="-30" stroke="oklch(72% 0.18 25)" strokeWidth="3" />
        <polygon points="86,-32 76,-38 76,-26" fill="oklch(72% 0.18 25)" />
        <line x1="0" y1="0" x2="-80" y2="-30" stroke="oklch(72% 0.18 230)" strokeWidth="3" />
        <polygon points="-86,-32 -76,-38 -76,-26" fill="oklch(72% 0.18 230)" />
        {/* dimensions label */}
        <g transform="translate(80, -90)">
          <rect x="-4" y="-12" width="80" height="18" fill="oklch(15% 0.005 250)" rx="2" />
          <text x="36" y="0" fill={e.selected} fontSize="10" fontFamily="ui-monospace, monospace" textAnchor="middle">8 × 1 × 8</text>
        </g>
      </g>

      {/* Origin marker */}
      <g transform="translate(60, 540)">
        <line x1="0" y1="0" x2="40" y2="-15" stroke="oklch(72% 0.18 25)" strokeWidth="2" />
        <line x1="0" y1="0" x2="-40" y2="-15" stroke="oklch(72% 0.18 230)" strokeWidth="2" />
        <line x1="0" y1="0" x2="0" y2="-40" stroke="oklch(72% 0.18 145)" strokeWidth="2" />
        <text x="46" y="-12" fill="oklch(72% 0.18 25)" fontSize="10" fontFamily="ui-monospace, monospace">x</text>
        <text x="-52" y="-12" fill="oklch(72% 0.18 230)" fontSize="10" fontFamily="ui-monospace, monospace">z</text>
        <text x="4" y="-44" fill="oklch(72% 0.18 145)" fontSize="10" fontFamily="ui-monospace, monospace">y</text>
      </g>

      {/* Sun light icon */}
      <g transform="translate(1100, 80)">
        <circle r="14" fill="oklch(82% 0.13 90)" opacity="0.9" />
        {[0, 60, 120, 180, 240, 300].map(d => (
          <line key={d} x1={Math.cos(d * Math.PI / 180) * 20} y1={Math.sin(d * Math.PI / 180) * 20}
                       x2={Math.cos(d * Math.PI / 180) * 28} y2={Math.sin(d * Math.PI / 180) * 28}
            stroke="oklch(82% 0.13 90)" strokeWidth="2" opacity="0.7" />
        ))}
      </g>
    </svg>
  );
}

function IsoBox({ x, y, w, d, h, topFill, sideFill, e, selected }) {
  // Cabinet projection: x' = x + z*cos30, y' = y - z*sin30 - height
  const dx = d * 0.866 / 2;
  const dy = d * 0.5 / 2;
  // Top diamond
  const top = `M ${x - w/2 - dx} ${y - dy}
               L ${x + w/2 - dx} ${y - dy}
               L ${x + w/2 + dx} ${y + dy}
               L ${x - w/2 + dx} ${y + dy} Z`;
  // Front face (right side)
  const front = `M ${x + w/2 - dx} ${y - dy}
                 L ${x + w/2 + dx} ${y + dy}
                 L ${x + w/2 + dx} ${y + dy + h}
                 L ${x + w/2 - dx} ${y - dy + h} Z`;
  // Side face (left side)
  const side = `M ${x - w/2 + dx} ${y + dy}
                L ${x + w/2 + dx} ${y + dy}
                L ${x + w/2 + dx} ${y + dy + h}
                L ${x - w/2 + dx} ${y + dy + h} Z`;
  return (
    <g>
      <path d={side}  fill={sideFill} stroke="rgba(0,0,0,0.4)" strokeWidth="0.5" />
      <path d={front} fill={sideFill} opacity="0.8" stroke="rgba(0,0,0,0.4)" strokeWidth="0.5" />
      <path d={top}   fill={topFill} stroke="rgba(0,0,0,0.5)" strokeWidth="0.5" />
      {selected && <path d={top} fill="none" stroke={e.selected} strokeWidth="2" />}
    </g>
  );
}

function AxisGizmo({ e, tick }) {
  const r = (tick * 0.3) * Math.PI / 180; // slow rotation
  const proj = (x, y, z) => {
    // crude rotation around Y
    const cx = Math.cos(r), sx = Math.sin(r);
    const x1 = x * cx - z * sx;
    const z1 = x * sx + z * cx;
    return [16 + x1 * 14, 16 - y * 14 + z1 * 4];
  };
  const [px, py] = proj(1, 0, 0);
  const [nx, ny] = proj(-1, 0, 0);
  const [yx, yy] = proj(0, 1, 0);
  const [nyx, nyy] = proj(0, -1, 0);
  const [zx, zy] = proj(0, 0, 1);
  const [nzx, nzy] = proj(0, 0, -1);
  return (
    <svg width="32" height="32" viewBox="0 0 32 32">
      <line x1="16" y1="16" x2={px} y2={py} stroke="oklch(72% 0.18 25)" strokeWidth="2" />
      <circle cx={px} cy={py} r="2.5" fill="oklch(72% 0.18 25)" />
      <line x1="16" y1="16" x2={yx} y2={yy} stroke="oklch(72% 0.18 145)" strokeWidth="2" />
      <circle cx={yx} cy={yy} r="2.5" fill="oklch(72% 0.18 145)" />
      <line x1="16" y1="16" x2={zx} y2={zy} stroke="oklch(72% 0.18 230)" strokeWidth="2" />
      <circle cx={zx} cy={zy} r="2.5" fill="oklch(72% 0.18 230)" />
    </svg>
  );
}

// ── Console ─────────────────────────────
function ConsolePanel({ e, logs, cmd, setCmd, runCmd, filter }) {
  let shown = logs;
  if (filter === 'errors') shown = logs.filter(l => l.t === 'error');
  if (filter === 'warn')   shown = logs.filter(l => l.t === 'warn');
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
      <div style={{
        flex: 1, overflowY: 'auto', padding: '6px 0',
        fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 11.5,
      }}>
        {shown.map((l, i) => (
          <div key={i} style={{
            padding: '2px 14px', display: 'grid', gridTemplateColumns: '70px 60px 1fr', gap: 10,
            color: l.t === 'error' ? e.error : l.t === 'warn' ? e.warn : l.t === 'ok' ? e.ok : l.t === 'cmd' ? e.text : e.muted,
          }}>
            <span style={{ color: e.faint }}>{l.time}</span>
            <span style={{
              fontSize: 9, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600,
              color: l.t === 'error' ? e.error : l.t === 'warn' ? e.warn : l.t === 'ok' ? e.ok : e.faint,
            }}>{l.t === 'cmd' ? '' : l.t}</span>
            <span style={{ color: l.t === 'cmd' ? e.accent : 'inherit' }}>{l.msg}</span>
          </div>
        ))}
      </div>
      <div style={{ borderTop: `1px solid ${e.borderS}`, padding: '6px 10px', display: 'flex', gap: 8, alignItems: 'center' }}>
        <span style={{ color: e.accent, fontFamily: 'ui-monospace, monospace', fontSize: 12 }}>&gt;</span>
        <input
          value={cmd}
          onChange={(ev) => setCmd(ev.target.value)}
          onKeyDown={(ev) => { if (ev.key === 'Enter') runCmd(); }}
          placeholder="run lua… try `print(workspace:GetChildren())`"
          style={{
            flex: 1, height: 24, padding: 0, background: 'transparent', border: 'none',
            color: e.text, outline: 'none', fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 12,
          }}
        />
      </div>
    </div>
  );
}

// ── Lua editor (read-only highlight) ──────────────
function LuaEditor({ e }) {
  const colorMap = LUA_COLORS(e);
  return (
    <div style={{
      flex: 1, overflowY: 'auto', display: 'flex',
      fontFamily: '"JetBrains Mono", ui-monospace, monospace', fontSize: 12, lineHeight: 1.55,
    }}>
      <div style={{ padding: '8px 0', background: e.bg, borderRight: `1px solid ${e.borderS}`, minWidth: 40 }}>
        {LUA_SOURCE.map((_, i) => (
          <div key={i} style={{ padding: '0 10px', textAlign: 'right', color: e.faint }}>{i + 1}</div>
        ))}
      </div>
      <div style={{ padding: '8px 14px', flex: 1 }}>
        {LUA_SOURCE.map((line, i) => (
          <div key={i} style={{ minHeight: 18 }}>
            {line.length === 1 && line[0] === ' ' ? '\u00A0' :
              renderLuaLine(line, colorMap)}
          </div>
        ))}
      </div>
    </div>
  );
}
function renderLuaLine(tokens, colorMap) {
  // Tokens come as alternating type+text pairs sometimes preceded by raw indentation strings.
  // Simplest: walk and treat any 1-char key in colorMap as a type marker for the next string.
  const out = [];
  for (let i = 0; i < tokens.length; i++) {
    const tk = tokens[i];
    if (tk in colorMap && i + 1 < tokens.length && !(tokens[i + 1] in colorMap)) {
      out.push(<span key={i} style={{ color: colorMap[tk] }}>{tokens[i + 1]}</span>);
      i++;
    } else {
      out.push(<span key={i}>{tk}</span>);
    }
  }
  return out;
}

// ── Asset library ──────────────────────────
function AssetLibrary({ e }) {
  const cats = ['Models', 'Materials', 'Sounds', 'Decals', 'Animations'];
  const [cat, setCat] = React.useState('Models');
  const items = Array.from({ length: 18 }, (_, i) => ({
    id: i,
    label: ['Tree_Pine', 'Crate_Wood', 'Lantern_01', 'Stone_Mossy', 'Door_Iron', 'Barrel', 'Banner', 'Torch', 'Coin', 'Chest', 'Mushroom', 'Cloud', 'Bridge', 'Anvil', 'Bench', 'Stairs_A', 'Pillar', 'Fence'][i],
    hue: [120, 30, 50, 145, 250, 30, 0, 50, 60, 30, 320, 230, 30, 250, 30, 145, 250, 145][i],
    pat: ['stripes','grid','dots','noise'][i % 4],
  }));
  return (
    <div style={{ flex: 1, display: 'flex', minHeight: 0 }}>
      <div style={{ width: 140, borderRight: `1px solid ${e.borderS}`, padding: 6 }}>
        {cats.map(c => (
          <button key={c} onClick={() => setCat(c)} style={{
            display: 'block', width: '100%', textAlign: 'left',
            padding: '6px 10px', borderRadius: 4, border: 'none',
            background: cat === c ? e.panel2 : 'transparent',
            color: cat === c ? e.text : e.muted,
            fontSize: 11, cursor: 'pointer', fontFamily: 'inherit', marginBottom: 2,
          }}>{c}</button>
        ))}
      </div>
      <div style={{ flex: 1, padding: 8, overflowY: 'auto' }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(96px, 1fr))', gap: 6 }}>
          {items.map(it => (
            <div key={it.id} style={{
              borderRadius: 5, overflow: 'hidden', cursor: 'grab',
              border: `1px solid ${e.borderS}`,
            }}>
              <div style={{ aspectRatio: '1' }}>
                <Placeholder hue={it.hue} pattern={it.pat} dark={true} radius={4} />
              </div>
              <div style={{ padding: '4px 6px', fontSize: 10, color: e.muted, fontFamily: 'ui-monospace, monospace', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{it.label}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { StudioEditor });
