// solar.jsx — 2D animated solar-system visualization of a concept.
// Hierarchy of gravity (per brief):
//   Essences orbit the nucleus.
//   Patterns orbit their Essence (satellites of Essences).
//   Perspectives orbit their Pattern (satellites of Patterns).
// Consist and Function are STILL constants on the innermost orbit, opposite sides.
// Solo perspectives / solo patterns orbit the nucleus directly, dashed + faded.
//
// MiniSolar — tiny non-interactive preview used in the sidebar of edit mode.
// SolarScreen — full-screen interactive viz with pause, hover labels, legend.

const { useEffect: _s_ue, useRef: _s_ur, useState: _s_us, useMemo: _s_um } = React;

// hash a string to a small float in [0,1) — for stable per-id randomness
function hash01(str) {
  let h = 2166136261;
  for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
  return ((h >>> 0) % 100000) / 100000;
}

// Compute positions of every body at time t (seconds). Returns flat list with type info.
function computeOrbits(concept, t, opts = {}) {
  const { scale = 1, mini = false, nodeScale = 1, distScale = 1 } = opts;
  t = -t; // counter-clockwise in SVG screen coords (y-axis points down)
  const ns = mini ? 1 : nodeScale;
  const ds = mini ? 1 : distScale;

  const NUC_R = 18 * scale;
  const O1 = 56 * scale;
  const O1_C = mini ? 38 : 46 * ds;
  const O1_F = mini ? 56 : 66 * ds;
  const ESS_R = mini ? 88 : 200 * ds;
  const PAT_R = mini ? 28 : 58 * ds;
  const PER_R = mini ? 14 : 30 * ds;
  const SOLO_PAT_R = mini ? 70 : 165 * ds;
  const SOLO_PER_R = mini ? 60 : 140 * ds;

  // Per-level angular speeds — uniform within each level, different between levels
  const W_ESS      = 0.07;
  const W_PAT      = 0.25;
  const W_PER      = 0.55;
  const W_SOLO_PAT = 0.10;
  const W_SOLO_PER = 0.15;

  const bodies = [];
  bodies.push({
    id: "nucleus", kind: "nucleus", x: 0, y: 0,
    r: mini ? 11 : 22 * ns, color: "var(--c-nucleus)",
    label: concept.title, sub: concept.category
  });

  const CONST_W = 0.12;
  if (concept.consist) bodies.push({
    id: "consist", kind: "constant",
    x: Math.cos(t * CONST_W) * O1_C, y: Math.sin(t * CONST_W) * O1_C,
    r: mini ? 5 : 9 * ns, color: "var(--c-consist)",
    label: "Consist", sub: concept.consist.slice(0, 80),
    orbitR: O1_C, orbitCx: 0, orbitCy: 0
  });
  const _fnList = (concept.functions && concept.functions.length > 0)
    ? concept.functions.filter(f => f.body)
    : (concept.function ? [{ id: "fn0", body: concept.function }] : []);
  _fnList.forEach((fn, i) => {
    const spread = Math.PI / Math.max(_fnList.length, 1);
    const baseAngle = Math.PI - ((_fnList.length - 1) / 2) * spread;
    const angle = t * CONST_W + baseAngle + i * spread;
    bodies.push({
      id: "function_" + fn.id, kind: "constant",
      x: Math.cos(angle) * O1_F, y: Math.sin(angle) * O1_F,
      r: mini ? 5 : 9 * ns, color: "var(--c-function)",
      label: i === 0 ? "Function" : `Function ${i + 1}`, sub: fn.body.slice(0, 80),
      orbitR: O1_F, orbitCx: 0, orbitCy: 0
    });
  });

  const essences = concept.essences || [];
  const patterns = concept.patterns || [];
  const perspectives = concept.perspectives || [];

  essences.forEach((e, i) => {
    const baseAngle = (i / Math.max(essences.length, 1)) * Math.PI * 2 + hash01(e.id) * 0.4;
    const angle = baseAngle + t * W_ESS;
    const ex = Math.cos(angle) * ESS_R;
    const ey = Math.sin(angle) * ESS_R;
    bodies.push({
      id: e.id, kind: "essence", x: ex, y: ey,
      r: mini ? 6 : 12 * ns, color: "var(--c-essence)",
      label: "Essence", sub: (e.body || "").slice(0, 80),
      orbitR: ESS_R, orbitCx: 0, orbitCy: 0
    });

    const linkedPats = patterns.filter(p => p.essenceId === e.id);
    linkedPats.forEach((p, j) => {
      const ba = (j / Math.max(linkedPats.length, 1)) * Math.PI * 2 + hash01(p.id) * 0.7;
      const pa = ba + t * W_PAT;
      const px = ex + Math.cos(pa) * PAT_R;
      const py = ey + Math.sin(pa) * PAT_R;
      bodies.push({
        id: p.id, kind: "pattern", x: px, y: py,
        r: mini ? 4 : 8 * ns, color: "var(--c-pattern)",
        label: p.title || "Pattern", sub: (p.body || "").slice(0, 80),
        orbitR: PAT_R, orbitCx: ex, orbitCy: ey, parentId: e.id
      });

      const linkedPers = perspectives.filter(x => x.patternId === p.id);
      linkedPers.forEach((per, k) => {
        const pba = (k / Math.max(linkedPers.length, 1)) * Math.PI * 2 + hash01(per.id) * 0.5;
        const pera = pba + t * W_PER;
        const perx = px + Math.cos(pera) * PER_R;
        const pery = py + Math.sin(pera) * PER_R;
        bodies.push({
          id: per.id, kind: "perspective", x: perx, y: pery,
          r: mini ? 3 : 6 * ns, color: "var(--c-perspective)",
          label: per.title || "Perspective", sub: (per.body || "").slice(0, 70),
          orbitR: PER_R, orbitCx: px, orbitCy: py, parentId: p.id
        });
      });
    });
  });

  const soloPatterns = patterns.filter(p => !p.essenceId);
  soloPatterns.forEach((p, i) => {
    const ba = (i / Math.max(soloPatterns.length, 1)) * Math.PI * 2 + hash01(p.id);
    const a = ba + t * W_SOLO_PAT;
    const px = Math.cos(a) * SOLO_PAT_R;
    const py = Math.sin(a) * SOLO_PAT_R;
    bodies.push({
      id: p.id, kind: "pattern", x: px, y: py,
      r: mini ? 4 : 8 * ns, color: "var(--c-pattern)",
      label: p.title || "Pattern", sub: (p.body || "").slice(0, 80),
      orbitR: SOLO_PAT_R, orbitCx: 0, orbitCy: 0, solo: true
    });

    const linkedPers = perspectives.filter(x => x.patternId === p.id);
    linkedPers.forEach((per, k) => {
      const pba = (k / Math.max(linkedPers.length, 1)) * Math.PI * 2 + hash01(per.id);
      const pera = pba + t * W_PER;
      const perx = px + Math.cos(pera) * PER_R;
      const pery = py + Math.sin(pera) * PER_R;
      bodies.push({
        id: per.id, kind: "perspective", x: perx, y: pery,
        r: mini ? 3 : 6 * ns, color: "var(--c-perspective)",
        label: per.title || "Perspective", sub: (per.body || "").slice(0, 70),
        orbitR: PER_R, orbitCx: px, orbitCy: py, parentId: p.id
      });
    });
  });

  const soloPerspectives = perspectives.filter(p => !p.patternId);
  soloPerspectives.forEach((per, i) => {
    const ba = (i / Math.max(soloPerspectives.length, 1)) * Math.PI * 2 + hash01(per.id);
    const a = ba + t * W_SOLO_PER;
    const px = Math.cos(a) * SOLO_PER_R;
    const py = Math.sin(a) * SOLO_PER_R;
    bodies.push({
      id: per.id, kind: "perspective", x: px, y: py,
      r: mini ? 3 : 6 * ns, color: "var(--c-perspective)",
      label: per.title || "Perspective", sub: (per.body || "").slice(0, 70),
      orbitR: SOLO_PER_R, orbitCx: 0, orbitCy: 0, solo: true
    });
  });

  return bodies;
}


function _SolarSlider({ label, value, min, max, step, onChange }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
      <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--fg-3)", fontFamily: "var(--font-mono)", letterSpacing: ".04em" }}>
        <span>{label}</span><span>{value.toFixed(1)}</span>
      </div>
      <input type="range" min={min} max={max} step={step} value={value}
        onChange={e => onChange(Number(e.target.value))}
        style={{ width: "100%", accentColor: "var(--c-nucleus)", cursor: "pointer" }}
      />
    </div>
  );
}

// Gradient stops for nucleus sphere — from filled levels center→edge
// Returns the outermost filled orbit color — used as the nucleus gradient edge
function nucEdgeColor(concept) {
  if ((concept.perspectives||[]).length > 0) return "var(--c-perspective)";
  if ((concept.patterns||[]).length > 0)     return "var(--c-pattern)";
  if ((concept.essences||[]).length > 0)     return "var(--c-essence)";
  if ((concept.functions && concept.functions.some(f => f.body)) || concept.function) return "var(--c-function)";
  if (concept.consist)                        return "var(--c-consist)";
  return "var(--c-nucleus)";
}

// ───────────────────────────────────────────────────────────────────
// MiniSolar — small, ambient preview used in the right rail
function MiniSolar({ concept }) {
  const ref = _s_ur(null);
  const svgRef = _s_ur(null);
  const [t, setT] = _s_us(0);
  const [zoom, setZoom] = _s_us(1);
  _s_ue(() => {
    let raf, start = performance.now();
    const tick = (now) => { setT((now - start) / 1000); raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  _s_ue(() => {
    const el = svgRef.current;
    if (!el) return;
    const handler = (e) => {
      e.preventDefault();
      setZoom(z => Math.max(0.3, Math.min(5, z * Math.exp(-e.deltaY * 0.001))));
    };
    el.addEventListener("wheel", handler, { passive: false });
    return () => el.removeEventListener("wheel", handler);
  }, []);

  const bodies = computeOrbits(concept, t, { mini: true });
  // Unique orbit circles
  const orbitKeys = new Set();
  const orbits = bodies.filter(b => {
    if (!b.orbitR) return false;
    const k = `${b.orbitCx}|${b.orbitCy}|${Math.round(b.orbitR)}|${b.solo ? 1 : 0}`;
    if (orbitKeys.has(k)) return false;
    orbitKeys.add(k);
    return true;
  });

  const VB = 220;
  const mgId = `mg-${concept.id || 'x'}`;
  const mgEdge = nucEdgeColor(concept);
  return (
    <div className="surface" style={{ padding: 12, display: "flex", flexDirection: "column", gap: 8 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
        <div className="tag-flat">System view</div>
        <span style={{ fontSize: 11, color: "var(--fg-3)", fontFamily: "var(--font-mono)" }}>live</span>
      </div>
      <svg ref={svgRef} viewBox={`-${VB / 2 / zoom} -${VB / 2 / zoom} ${VB / zoom} ${VB / zoom}`} style={{ width: "100%", height: 220 }}>
        <defs>
          <linearGradient id={mgId} x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" stopColor="var(--c-nucleus)" />
            <stop offset="100%" stopColor={mgEdge} />
          </linearGradient>
        </defs>
        {/* orbit rings */}
        {orbits.map((b, i) => (
          <circle key={i} cx={b.orbitCx} cy={b.orbitCy} r={b.orbitR}
            fill="none" stroke="var(--border)"
            strokeDasharray={b.solo ? "2 3" : null}
            strokeOpacity={b.solo ? 0.85 : 0.65} strokeWidth={0.8}
          />
        ))}
        {bodies.map((b) => (
          <circle key={b.id + b.kind}
            cx={b.x} cy={b.y} r={b.r}
            fill={b.kind === "nucleus" ? "var(--bg-elev)" : b.color}
            stroke={b.kind === "nucleus" ? `url(#${mgId})` : "none"}
            strokeWidth={b.kind === "nucleus" ? 1.5 : 0}
            opacity={b.solo ? 0.6 : 1}
          />
        ))}
      </svg>
      <div style={{ fontSize: 11, color: "var(--fg-3)", lineHeight: 1.5 }}>
        {(concept.essences?.length || 0)} essence · {(concept.patterns?.length || 0)} pattern · {(concept.perspectives?.length || 0)} perspective
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────
// SolarScreen — full screen interactive
function SolarScreen({ concept, allConcepts = [], onBack, onSwitchMode, styleVariant = "planets", themeDark = true, onChange, onOpenConcept, onUpdateConcept, onOpenSolar, openPanel }) {
  const _ss = (() => { try { return JSON.parse(localStorage.getItem('disene_solar_settings')) || {}; } catch { return {}; } })();

  const ref = _s_ur(null);
  const [t, setT] = _s_us(0);
  const [paused, setPaused] = _s_us(false);
  const [hover, setHover] = _s_us(null);
  const [hover3D, setHover3D] = _s_us(null);
  const [zoom, setZoom] = _s_us(1);
  const [dim, setDim] = _s_us("2d"); // '2d' | '3d' | 'graph'
  const [selected, setSelected] = _s_us(openPanel ? "nucleus" : null);
  const [nodeScale, setNodeScale] = _s_us(_ss.nodeScale   ?? 1);
  const [distScale, setDistScale] = _s_us(_ss.distScale   ?? 1);
  const [orbitWidth, setOrbitWidth] = _s_us(_ss.orbitWidth ?? 1.2);
  const [showSettings, setShowSettings] = _s_us(false);
  const [panelW, setPanelW] = _s_us(460);
  const canvasRef = _s_ur(null);

  _s_ue(() => {
    localStorage.setItem('disene_solar_settings', JSON.stringify({ nodeScale, distScale, orbitWidth }));
  }, [nodeScale, distScale, orbitWidth]);
  const resizingRef = _s_ur(null);

  function onResizeStart(e) {
    e.preventDefault();
    resizingRef.current = { startX: e.clientX, startW: panelW };
    const onMove = (ev) => {
      const delta = resizingRef.current.startX - ev.clientX;
      setPanelW(Math.max(280, Math.min(820, resizingRef.current.startW + delta)));
    };
    const onUp = () => {
      document.removeEventListener('pointermove', onMove);
      document.removeEventListener('pointerup', onUp);
      resizingRef.current = null;
    };
    document.addEventListener('pointermove', onMove);
    document.addEventListener('pointerup', onUp);
  }

  // Wheel-to-zoom for 2D mode (non-passive so we can preventDefault)
  _s_ue(() => {
    if (dim !== "2d") return;
    const el = canvasRef.current;
    if (!el) return;
    const handler = (e) => {
      e.preventDefault();
      setZoom(z => Math.max(0.3, Math.min(5, z * Math.exp(-e.deltaY * 0.001))));
    };
    el.addEventListener("wheel", handler, { passive: false });
    return () => el.removeEventListener("wheel", handler);
  }, [dim]);

  _s_ue(() => {
    if (paused) return;
    let raf, last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      setT(prev => prev + dt);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [paused]);

  const bodies = computeOrbits(concept, t, { mini: false, nodeScale, distScale });
  const sgId = `sg-${concept.id || 'x'}`;
  const sgEdge = nucEdgeColor(concept);

  // Build unique orbits for rendering rings.
  const orbitKeys = new Set();
  const orbits = bodies.filter(b => {
    if (!b.orbitR) return false;
    const k = `${b.orbitCx}|${b.orbitCy}|${Math.round(b.orbitR)}|${b.solo ? 1 : 0}|${b.kind}`;
    if (orbitKeys.has(k)) return false;
    orbitKeys.add(k);
    return true;
  });

  // style variant: "planets" (filled circles), "rings" (filled w/ subtle ring), "atoms" (rings + tiny core)
  function renderBody(b) {
    const isHover = hover === b.id;
    const isSel = selected === b.id;
    const r = b.r;
    const op = b.solo ? 0.62 : 1;
    const dimmed = hover && !isHover && b.kind !== "nucleus" ? 0.45 : 1;
    const finalOp = op * dimmed;
    const isNucleus = b.kind === "nucleus";
    const onClick = isNucleus ? undefined : () => setSelected(isSel ? null : b.id);
    const onMouseEnter = isNucleus ? undefined : () => setHover(b.id);
    const onMouseLeave = isNucleus ? undefined : () => setHover(null);

    const bodyFill = isNucleus ? "var(--bg-elev)" : b.color;
    const nucStroke = isNucleus ? { stroke: `url(#${sgId})`, strokeWidth: 2 } : {};
    if (styleVariant === "rings") {
      return (
        <g key={b.id + b.kind} opacity={finalOp} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onClick={onClick} style={{ cursor: isNucleus ? "default" : "pointer" }}>
          {isSel && !isNucleus && <circle cx={b.x} cy={b.y} r={r + 6} fill="none" stroke={b.color} strokeWidth="2.5" opacity="0.9" />}
          <circle cx={b.x} cy={b.y} r={r * 1.6} fill={bodyFill} opacity="0.13" />
          <circle cx={b.x} cy={b.y} r={r} fill={bodyFill} {...nucStroke} />
        </g>
      );
    }
    if (styleVariant === "atoms") {
      return (
        <g key={b.id + b.kind} opacity={finalOp} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onClick={onClick} style={{ cursor: isNucleus ? "default" : "pointer" }}>
          {isSel && !isNucleus && <circle cx={b.x} cy={b.y} r={r + 6} fill="none" stroke={b.color} strokeWidth="2.5" opacity="0.9" />}
          {!isNucleus && <circle cx={b.x} cy={b.y} r={r} fill="none" stroke={b.color} strokeWidth="1.6" />}
          <circle cx={b.x} cy={b.y} r={isNucleus ? r : r * 0.42} fill={bodyFill} {...nucStroke} />
        </g>
      );
    }
    // planets (default)
    return (
      <g key={b.id + b.kind} opacity={finalOp} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onClick={onClick} style={{ cursor: isNucleus ? "default" : "pointer" }}>
        {isSel && !isNucleus && <circle cx={b.x} cy={b.y} r={r + 6} fill="none" stroke={b.color} strokeWidth="2.5" opacity="0.9" />}
        <circle cx={b.x} cy={b.y} r={r} fill={bodyFill} {...nucStroke} />
        {isHover && !isSel && <circle cx={b.x} cy={b.y} r={r + 4} fill="none" stroke={b.color} strokeWidth="1" opacity="0.6" />}
      </g>
    );
  }

  if (dim === "graph") {
    return (
      <window.Graph
        concepts={allConcepts}
        onUpdate={onUpdateConcept}
        onBack={() => setDim("2d")}
        onOpenSolar={(id) => {
          if (id === concept.id) {
            setSelected(s => s || "nucleus");
            setDim("2d");
          } else {
            onOpenSolar && onOpenSolar(id, true);
          }
        }}
        onOpenConcept={onOpenConcept}
        density="regular"
        backLabel="Solar"
        initialSelectedId={selected ? concept.id : null}
        panelW={panelW}
        onPanelWChange={setPanelW}
      />
    );
  }

  const VB = 700;
  const activeHover = dim === "3d" ? hover3D : (hover ? bodies.find(b => b.id === hover) : null);

  return (
    <div data-screen-label="Concept · System" style={{ display: "flex", height: "calc(100vh - 60px)", overflow: "hidden" }}>
      <div ref={canvasRef} style={{ flex: 1, position: "relative", overflow: "hidden", background: "var(--bg-sunk)" }}>
      {dim === "3d" && window.Solar3D && (
        <window.Solar3D
          key={concept.id + "|" + (themeDark ? "d" : "l")}
          concept={concept}
          paused={paused}
          themeDark={themeDark}
          setHover={setHover3D}
          onSelect={id => setSelected(prev => prev === id ? null : id)}
          nodeScale={nodeScale}
          distScale={distScale}
          orbitWidth={orbitWidth}
        />
      )}

      {dim === "2d" && (
      <svg
        viewBox={`-${VB / 2 / zoom} -${VB / 2 / zoom} ${VB / zoom} ${VB / zoom}`}
        style={{ width: "100%", height: "100%", display: "block" }}
      >
        <defs>
          <linearGradient id={sgId} x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" stopColor="var(--c-nucleus)" />
            <stop offset="100%" stopColor={sgEdge} />
          </linearGradient>
        </defs>
        {/* orbit rings */}
        {orbits.map((b, i) => (
          <circle key={i}
            cx={b.orbitCx} cy={b.orbitCy} r={b.orbitR}
            fill="none"
            stroke="var(--border)"
            strokeOpacity={b.solo ? 0.85 : 0.65}
            strokeDasharray={b.solo ? "3 5" : null}
            strokeWidth={orbitWidth}
          />
        ))}
        {/* Render in order: nucleus, constants, essences (largest), patterns, perspectives */}
        {bodies.filter(b => b.kind === "nucleus").map(renderBody)}
        {bodies.filter(b => b.kind === "constant").map(renderBody)}
        {bodies.filter(b => b.kind === "essence").map(renderBody)}
        {bodies.filter(b => b.kind === "pattern").map(renderBody)}
        {bodies.filter(b => b.kind === "perspective").map(renderBody)}

        {/* Nucleus label — always visible */}
        <g>
          <text x="0" y="42" textAnchor="middle"
            fontFamily="var(--font-serif)" fontSize="26" fontStyle="italic"
            fill="var(--fg)">{concept.title}</text>
          <text x="0" y="60" textAnchor="middle"
            fontFamily="var(--font-mono)" fontSize="10" letterSpacing="2"
            fill="var(--fg-3)">{concept.category.toUpperCase()}</text>
        </g>
      </svg>
      )}

      {/* 3D mode — nucleus label overlay (HTML so type is crisp) */}
      {dim === "3d" && (
        <div style={{
          position: "absolute", left: 28, top: 64, pointerEvents: "none",
          maxWidth: 360
        }}>
          <div className="tag-flat" style={{ marginBottom: 6 }}>{concept.category}</div>
          <div className="concept-title" style={{ fontSize: 56, lineHeight: 1, color: "var(--fg)" }}>{concept.title}</div>
          <div style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", color: "var(--fg-3)", fontSize: 15, marginTop: 10, lineHeight: 1.5 }}>
            {(concept.essences?.[0]?.body) || "system view — drag to rotate, scroll to zoom"}
          </div>
        </div>
      )}

      {/* Top toolbar */}
      <div className="view-toolbar" style={{ position: "absolute", top: 14, left: 14, right: 14, display: "flex", alignItems: "center", gap: 12 }}>
        <button className="btn ghost tiny" onClick={onBack}>{window.Icons.ArrowL(13)} Library</button>
        <div className="tag-flat solar-bc">{concept.title} · system view</div>
        <div style={{ flex: 1 }} />
        <div className="view-mode-sw" style={{ display: "flex", gap: 4, background: "var(--bg-sunk)", padding: 3, borderRadius: 9, border: "1px solid var(--border-soft)" }}>
          <button className="btn tiny" style={modeStyle(false)} onClick={() => onSwitchMode("edit")}>{window.Icons.Edit(13)} Edit</button>
          <button className="btn tiny" style={modeStyle(false)} onClick={() => onSwitchMode("read")}>{window.Icons.Eye(13)} Read</button>
          <button className="btn tiny" style={modeStyle(true)}>{window.Icons.Planet(13)} System</button>
          <button className="btn tiny" style={modeStyle(false)} onClick={() => setDim("graph")}>{window.Icons.Graph(13)} Graph</button>
        </div>
      </div>

      {/* Hover tooltip */}
      {activeHover && activeHover.kind !== "nucleus" && (
        <HoverTip h={activeHover} />
      )}

      {/* Bottom controls + legend */}
      <div style={{ position: "absolute", bottom: 16, left: 16, right: 16, display: "flex", alignItems: "center", gap: 16, pointerEvents: "none" }}>
        <span className="solar-legend"><Legend /></span>
        <div style={{ flex: 1 }} />



        <div className="surface" style={{ pointerEvents: "auto", padding: 6, display: "flex", gap: 4, alignItems: "center" }}>
          <button className="btn ghost icon" onClick={() => setPaused(p => !p)} title={paused ? "Play" : "Pause"}>
            {paused ? window.Icons.Play(14) : window.Icons.Pause(14)}
          </button>
          <span style={{ paddingLeft: 6, paddingRight: 4, fontFamily: "var(--font-mono)", fontSize: 10.5, color: "var(--fg-3)", letterSpacing: ".08em", textTransform: "uppercase" }}>
            {dim === "3d" ? "drag · scroll" : "scroll"}
          </span>
          <span style={{ width: 1, height: 18, background: "var(--border)" }} />
          <button className="btn icon" onClick={() => setShowSettings(s => !s)} title="Solar settings" style={{ borderRadius: 4, padding: 7, width: 32, height: 32, justifyContent: "center" }}>{window.Icons.Sliders(16)}</button>
        </div>
      </div>

      {/* Settings panel */}
      {showSettings && (
        <div style={{
          position: "absolute", bottom: 60, right: 14, zIndex: 20,
          background: "var(--bg-elev)", border: "1px solid var(--border)",
          borderRadius: 10, padding: "14px 16px",
          boxShadow: "var(--shadow-lift)", minWidth: 220, display: "flex", flexDirection: "column", gap: 12,
        }}>
          <_SolarSlider label="Node size" value={nodeScale} min={0.4} max={2.5} step={0.1} onChange={setNodeScale} />
          <_SolarSlider label="Orbit width" value={orbitWidth} min={0.3} max={4} step={0.1} onChange={setOrbitWidth} />
          <_SolarSlider label="Distance" value={distScale} min={0.4} max={2} step={0.1} onChange={setDistScale} />
        </div>
      )}
      </div>
      {selected && onChange && (
        <div className="solar-side-panel" style={{
          position: "relative",
          width: panelW, flexShrink: 0,
          height: "100%",
          borderLeft: "1px solid var(--border-soft)",
          background: "var(--bg)",
          overflowY: "auto",
        }}>
          {/* resize handle */}
          <div
            onPointerDown={onResizeStart}
            style={{
              position: "absolute", left: 0, top: 0, bottom: 0, width: 6,
              cursor: "col-resize", zIndex: 10,
            }}
          />
          <window.ConceptEdit
            concept={concept}
            allConcepts={allConcepts}
            onChange={onChange}
            onBack={() => setSelected(null)}
            onSwitchMode={onSwitchMode}
            onOpenSolar={() => {}}
            onOpenConcept={onOpenConcept || (() => {})}
            density="regular"
            panelMode={true}
          />
        </div>
      )}
    </div>
  );
}

function modeStyle(active) {
  return {
    border: 0,
    background: active ? "var(--bg-elev)" : "transparent",
    boxShadow: active ? "0 1px 2px rgba(0,0,0,.05)" : "none",
    color: active ? "var(--fg)" : "var(--fg-3)",
  };
}

function dimBtnStyle(active) {
  return {
    border: 0,
    background: active ? "var(--bg-elev)" : "transparent",
    boxShadow: active ? "0 1px 2px rgba(0,0,0,.05)" : "none",
    color: active ? "var(--fg)" : "var(--fg-3)",
    padding: "4px 12px",
    fontFamily: "var(--font-mono)",
    fontSize: 11,
    letterSpacing: ".08em"
  };
}

function HoverTip({ h }) {
  return (
    <div style={{
      position: "absolute", left: "50%", bottom: 96, transform: "translateX(-50%)",
      background: "var(--bg-elev)",
      border: "1px solid var(--border)",
      borderRadius: 10,
      padding: "10px 14px",
      maxWidth: 380,
      boxShadow: "var(--shadow-lift)",
      pointerEvents: "none"
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 6, fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--fg-3)", marginBottom: 6 }}>
        <span style={{ width: 6, height: 6, borderRadius: 999, background: h.color || "var(--fg-3)" }} />
        {h.kind}{h.solo ? " · solo" : ""}
      </div>
      <div style={{ fontWeight: 500, fontSize: 14, marginBottom: 2 }}>{h.label}</div>
      {h.sub && (
        <div style={{
          fontSize: h.kind === "essence" ? 16 : 12.5,
          color: "var(--fg-2)",
          fontFamily: "var(--font-serif)",
          fontStyle: h.kind === "essence" ? "normal" : "italic",
          lineHeight: 1.5,
        }}>{h.sub}</div>
      )}
    </div>
  );
}

function Legend() {
  const items = [
    ["nucleus", "var(--c-nucleus)", "concept"],
    ["consist", "var(--c-consist)", "consist"],
    ["function", "var(--c-function)", "function"],
    ["essence", "var(--c-essence)", "essence"],
    ["pattern", "var(--c-pattern)", "pattern"],
    ["perspective", "var(--c-perspective)", "perspective"],
  ];
  return (
    <div className="surface" style={{ pointerEvents: "auto", padding: "8px 12px", display: "flex", flexDirection: "column", gap: 4 }}>
      <div style={{ display: "flex", gap: 14, fontSize: 11.5, color: "var(--fg-2)" }}>
        {items.map(([k, col, label]) => (
          <span key={k} style={{ display: "inline-flex", alignItems: "center", gap: 5 }}>
            <span style={{ width: 8, height: 8, borderRadius: 999, background: col }} />
            {label}
          </span>
        ))}
      </div>
    </div>
  );
}


Object.assign(window, { MiniSolar, SolarScreen });
