<script>// Standalone Relationship Ecosystem Visualization
// No external dependencies - pure JavaScript with embedded React via CDN

const { useState, useRef, useCallback, createElement: h } = React;

// ─── DIMENSIONS from Self Assessment for Visualization survey ───
const DIMENSIONS = [
  {
    id: "relational",
    label: "Relational Style",
    color: "#E07B9A",
    desc: "Openness to entanglement, commitment needs, multiple connections (Q24–29, Q38–39)",
    questions: "How open to multiple partners, new situations, life intertwining, romantic experiences?",
  },
  {
    id: "erotic",
    label: "Erotic & Sensual",
    color: "#C97BE0",
    desc: "Desire, kink, power dynamics, pain, sensuality, frequency (Q18–23, Q32, Q34–36)",
    questions: "Importance of kink, power dynamics, pain, sensual touch, erotic exploration, taboo, play frequency.",
  },
  {
    id: "emotional",
    label: "Emotional Nurturance",
    color: "#7EB8C9",
    desc: "Need for co-regulation, emotional processing, calming experiences (Q27–28, Q48–49)",
    questions: "Importance of emotional processing, co-regulation, calming, empathy differences in relationship.",
  },
  {
    id: "neurodivergence",
    label: "Neurodivergence",
    color: "#A8C47B",
    desc: "Sensory, attention, communication, memory, stimming, time perception (Q44–59)",
    questions: "Altered states, attention differences, communication style, sensory needs, memory, stimming, plurality.",
  },
  {
    id: "mentalhealth",
    label: "Mental Health Load",
    color: "#E0C47B",
    desc: "Current intensity: anxiety, depression, dissociation, hypervigilance (Q7–17)",
    questions: "Intrusive thoughts, hypervigilance, dissociation, depression, anxiety, OCD, psychotic symptoms, substance use.",
  },
  {
    id: "identity",
    label: "Identity & Body",
    color: "#FF9F7A",
    desc: "Gender expansiveness, somatic awareness, identity & religious trauma (Q10–11, Q40–43)",
    questions: "Identity-related trauma, gender identity, somatic/body awareness, breathwork, physical disabilities.",
  },
  {
    id: "needs",
    label: "Unmet Needs",
    color: "#7BE0D0",
    desc: "Gaps in community, basic needs, self-regulation, sex ed (Q1–4)",
    questions: "Community gaps, basic needs (food/housing), difficulty with self-regulation, sex & relationship education gaps.",
  },
  {
    id: "somatic",
    label: "Somatic & Spiritual",
    color: "#D4A0FF",
    desc: "Body-based awareness, breathwork, spirituality and ritual in play (Q33, Q40–41)",
    questions: "Importance of spirituality/sacredness, breath regulation, somatic body awareness in play and relationships.",
  },
];

const DEFAULT_PEOPLE = [
  { id: 1, name: "You", x: 420, y: 310, color: "#FFFFFF" },
  { id: 2, name: "Alex", x: 210, y: 155, color: "#E07B9A" },
  { id: 3, name: "River", x: 630, y: 175, color: "#C97BE0" },
  { id: 4, name: "Sam", x: 640, y: 430, color: "#A8C47B" },
];

const DEFAULT_RELATIONSHIPS = [
  {
    id: 1, from: 1, to: 2,
    dims: { relational: 0.8, erotic: 0.85, emotional: 0.7, neurodivergence: 0.5, mentalhealth: 0.4, identity: 0.6, needs: 0.3, somatic: 0.5 }
  },
  {
    id: 2, from: 1, to: 3,
    dims: { relational: 0.2, erotic: 0.65, emotional: 0.3, neurodivergence: 0.2, mentalhealth: 0.2, identity: 0.15, needs: 0.1, somatic: 0.4 }
  },
  {
    id: 3, from: 1, to: 4,
    dims: { relational: 0.5, erotic: 0.0, emotional: 0.95, neurodivergence: 0.75, mentalhealth: 0.6, identity: 0.8, needs: 0.7, somatic: 0.3 }
  },
];

function hexToRgb(hex) {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return { r, g, b };
}

function RadarChart({ dims, size = 130 }) {
  const cx = size / 2, cy = size / 2;
  const r = size * 0.34;
  const n = DIMENSIONS.length;
  const angles = DIMENSIONS.map((_, i) => (i / n) * Math.PI * 2 - Math.PI / 2);

  const getPoint = (angle, val) => ({
    x: cx + Math.cos(angle) * r * val,
    y: cy + Math.sin(angle) * r * val,
  });

  const dataPoints = DIMENSIONS.map((d, i) => getPoint(angles[i], dims[d.id] || 0));
  const dataPath = dataPoints.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ") + " Z";

  return h('svg', { width: size, height: size, style: { overflow: "visible" } }, [
    // Grid lines
    ...([0.25, 0.5, 0.75, 1.0].map((lv, idx) => {
      const pts = angles.map(a => getPoint(a, lv));
      const path = pts.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ") + " Z";
      return h('path', { key: `grid-${idx}`, d: path, fill: "none", stroke: "rgba(255,255,255,0.07)", strokeWidth: "1" });
    })),
    // Radial lines
    ...angles.map((a, i) => {
      const end = getPoint(a, 1);
      return h('line', { key: `radial-${i}`, x1: cx, y1: cy, x2: end.x, y2: end.y, stroke: "rgba(255,255,255,0.08)", strokeWidth: "1" });
    }),
    // Data area
    h('path', { d: dataPath, fill: "rgba(180,140,255,0.15)", stroke: "rgba(180,140,255,0.55)", strokeWidth: "1.5" }),
    // Data points
    ...DIMENSIONS.map((d, i) => {
      const val = dims[d.id] || 0;
      const pt = getPoint(angles[i], val);
      return h('circle', { key: `point-${d.id}`, cx: pt.x, cy: pt.y, r: 3, fill: d.color, opacity: 0.9 });
    }),
    // Labels
    ...DIMENSIONS.map((d, i) => {
      const labelPt = getPoint(angles[i], 1.38);
      return h('text', {
        key: `label-${d.id}`,
        x: labelPt.x,
        y: labelPt.y,
        textAnchor: "middle",
        dominantBaseline: "middle",
        fontSize: "6",
        fill: d.color,
        fontFamily: "'DM Mono', monospace",
        opacity: 0.82
      }, d.label.split(" ")[0]);
    })
  ]);
}

function ConnectionLine({ from, to, rel }) {
  const mx = (from.x + to.x) / 2;
  const my = (from.y + to.y) / 2;
  const dx = to.x - from.x;
  const dy = to.y - from.y;
  const dist = Math.sqrt(dx * dx + dy * dy);
  const norm = { x: -dy / dist, y: dx / dist };
  const curve = dist * 0.18;
  const cp = { x: mx + norm.x * curve, y: my + norm.y * curve };

  return h('g', {}, DIMENSIONS.map((dim, i) => {
    const val = rel.dims[dim.id] || 0;
    if (val < 0.05) return null;
    const offset = (i - (DIMENSIONS.length - 1) / 2) * 2.6;
    const fx = from.x + norm.x * offset;
    const fy = from.y + norm.y * offset;
    const tx = to.x + norm.x * offset;
    const ty = to.y + norm.y * offset;
    const cpx = cp.x + norm.x * offset;
    const cpy = cp.y + norm.y * offset;
    const rgb = hexToRgb(dim.color);
    return h('path', {
      key: dim.id,
      d: `M${fx},${fy} Q${cpx},${cpy} ${tx},${ty}`,
      fill: "none",
      stroke: `rgba(${rgb.r},${rgb.g},${rgb.b},${0.12 + val * 0.72})`,
      strokeWidth: 0.7 + val * 3.8,
      strokeLinecap: "round"
    });
  }).filter(Boolean));
}

function PersonNode({ person, isSelected, onClick, onDrag }) {
  const isDragging = useRef(false);
  const startPos = useRef({ x: 0, y: 0, px: 0, py: 0 });

  const handleMouseDown = (e) => {
    isDragging.current = false;
    startPos.current = { x: e.clientX, y: e.clientY, px: person.x, py: person.y };
    const onMove = (ev) => {
      const moved = Math.abs(ev.clientX - startPos.current.x) + Math.abs(ev.clientY - startPos.current.y);
      if (moved > 3) isDragging.current = true;
      onDrag(person.id, startPos.current.px + ev.clientX - startPos.current.x, startPos.current.py + ev.clientY - startPos.current.y);
    };
    const onUp = () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
      if (!isDragging.current) onClick(person.id);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    e.preventDefault();
  };

  return h('g', {
    transform: `translate(${person.x},${person.y})`,
    style: { cursor: "grab" },
    onMouseDown: handleMouseDown
  }, [
    // Selection ring
    isSelected && h('circle', {
      key: 'selection',
      r: 34,
      fill: "none",
      stroke: person.color,
      strokeWidth: "1.5",
      opacity: 0.35,
      style: { animation: "pulse 2s ease-in-out infinite" }
    }),
    // Main circle
    h('circle', {
      key: 'main',
      r: 26,
      fill: "rgba(18,14,28,0.9)",
      stroke: person.color,
      strokeWidth: isSelected ? 2 : 1.2,
      opacity: isSelected ? 1 : 0.85
    }),
    // Inner circle
    h('circle', { key: 'inner', r: 10, fill: person.color, opacity: 0.22 }),
    // Center dot
    h('circle', { key: 'center', r: 4, fill: person.color }),
    // Label
    h('text', {
      key: 'label',
      y: 40,
      textAnchor: "middle",
      fontSize: "11",
      fill: person.color,
      fontFamily: "'DM Mono', monospace",
      fontWeight: "500",
      letterSpacing: "0.05em"
    }, person.name)
  ].filter(Boolean));
}

function RelationshipEcosystem() {
  const [people, setPeople] = useState(DEFAULT_PEOPLE);
  const [relationships, setRelationships] = useState(DEFAULT_RELATIONSHIPS);
  const [selectedRel, setSelectedRel] = useState(1);
  const [selectedPerson, setSelectedPerson] = useState(null);
  const [activeDim, setActiveDim] = useState(null);
  const [addingPerson, setAddingPerson] = useState(false);
  const [newName, setNewName] = useState("");
  const [view, setView] = useState("ecosystem");
  const nextId = useRef(5);

  const rel = relationships.find(r => r.id === selectedRel);
  const fromPerson = rel ? people.find(p => p.id === rel.from) : null;
  const toPerson = rel ? people.find(p => p.id === rel.to) : null;

  const handleDrag = useCallback((id, x, y) => {
    setPeople(ps => ps.map(p => p.id === id
      ? { ...p, x: Math.max(60, Math.min(780, x)), y: Math.max(60, Math.min(560, y)) }
      : p
    ));
  }, []);

  const handlePersonClick = (id) => {
    setSelectedPerson(prev => prev === id ? null : id);
    if (id === 1) return;
    const existing = relationships.find(r => (r.from === 1 && r.to === id) || (r.from === id && r.to === 1));
    if (existing) setSelectedRel(existing.id);
  };

  const updateDim = (dimId, val) => {
    setRelationships(rs => rs.map(r =>
      r.id === selectedRel ? { ...r, dims: { ...r.dims, [dimId]: val } } : r
    ));
  };

  const addPerson = () => {
    if (!newName.trim()) return;
    const id = nextId.current++;
    const angle = Math.random() * Math.PI * 2;
    const radius = 150 + Math.random() * 70;
    const colors = ["#E07B9A","#C97BE0","#7EB8C9","#A8C47B","#E0C47B","#FF9F7A","#7BE0D0","#D4A0FF"];
    const color = colors[Math.floor(Math.random() * colors.length)];
    const newPerson = { id, name: newName.trim(), x: 420 + Math.cos(angle) * radius, y: 310 + Math.sin(angle) * radius, color };
    const newRelId = nextId.current++;
    const emptyDims = Object.fromEntries(DIMENSIONS.map(d => [d.id, 0]));
    setPeople(ps => [...ps, newPerson]);
    setRelationships(rs => [...rs, { id: newRelId, from: 1, to: id, dims: emptyDims }]);
    setSelectedRel(newRelId);
    setNewName("");
    setAddingPerson(false);
  };

  const removePerson = (id) => {
    if (id === 1) return;
    setPeople(ps => ps.filter(p => p.id !== id));
    setRelationships(rs => rs.filter(r => r.from !== id && r.to !== id));
    setSelectedPerson(null);
    setSelectedRel(null);
  };

  const romanticNormDims = Object.fromEntries(DIMENSIONS.map(d => [d.id, 1]));

  // Main render
  return h('div', {
    style: {
      minHeight: "100vh",
      background: "#0d0b16",
      fontFamily: "'DM Mono', monospace",
      color: "#e8e4f0",
      display: "flex",
      flexDirection: "column",
      overflow: "hidden"
    }
  }, [
    // Styles
    h('style', {}, `
      @import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300&family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400&display=swap');
      * { box-sizing: border-box; margin: 0; padding: 0; }
      ::-webkit-scrollbar { width: 4px; }
      ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
      @keyframes pulse { 0%,100%{opacity:0.3} 50%{opacity:0.6} }
      @keyframes fadeIn { from{opacity:0;transform:translateY(5px)} to{opacity:1;transform:translateY(0)} }
      input[type=range] { -webkit-appearance:none; appearance:none; height:3px; border-radius:2px; outline:none; cursor:pointer; display:block; width:100%; }
      input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; width:13px; height:13px; border-radius:50%; cursor:pointer; border:2px solid #0d0b16; background:#e8e4f0; }
      .tab-btn { background:none; border:none; cursor:pointer; font-family:'DM Mono',monospace; font-size:10px; letter-spacing:0.12em; text-transform:uppercase; padding:6px 14px; border-radius:20px; transition:all 0.2s; }
      .node-btn { background:none; border:1px solid rgba(255,255,255,0.12); cursor:pointer; font-family:'DM Mono',monospace; font-size:10px; color:rgba(255,255,255,0.5); padding:4px 10px; border-radius:12px; transition:all 0.2s; }
      .node-btn:hover { border-color:rgba(255,255,255,0.3); color:rgba(255,255,255,0.8); }
      .dim-toggle { background:none; border:none; cursor:pointer; text-align:left; width:100%; padding:0; font-family:'DM Mono',monospace; }
      input[type=text] { background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.15); border-radius:6px; color:#e8e4f0; font-family:'DM Mono',monospace; font-size:11px; padding:5px 10px; outline:none; }
      input[type=text]:focus { border-color:rgba(255,255,255,0.35); }
    `),

    // Header
    h('div', {
      style: {
        padding: "16px 24px 12px",
        borderBottom: "1px solid rgba(255,255,255,0.06)",
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between"
      }
    }, [
      h('div', {}, [
        h('div', {
          style: {
            fontSize: 9,
            letterSpacing: "0.18em",
            color: "rgba(255,255,255,0.22)",
            textTransform: "uppercase",
            marginBottom: 3
          }
        }, "relational ecosystem"),
        h('div', {
          style: {
            fontFamily: "'Cormorant Garamond', serif",
            fontSize: 20,
            fontWeight: 300,
            fontStyle: "italic"
          }
        }, "mapping love in all its shapes")
      ]),
      h('div', {
        style: {
          display: "flex",
          gap: 4,
          background: "rgba(255,255,255,0.04)",
          borderRadius: 20,
          padding: 3
        }
      }, ["ecosystem", "compare"].map(v =>
        h('button', {
          key: v,
          className: "tab-btn",
          onClick: () => setView(v),
          style: {
            color: view === v ? "#0d0b16" : "rgba(255,255,255,0.4)",
            background: view === v ? "rgba(255,255,255,0.85)" : "transparent"
          }
        }, v)
      ))
    ]),

    // Main content based on view
    view === "ecosystem" ? 
      // Ecosystem view
      h('div', { style: { display: "flex", flex: 1, overflow: "hidden" } }, [
        // SVG Canvas
        h('div', { style: { flex: 1, position: "relative" } }, [
          h('svg', {
            width: "100%",
            height: "100%",
            viewBox: "0 0 840 620",
            style: { display: "block" }
          }, [
            // Background and grid
            h('defs', {}, [
              h('radialGradient', { id: "bg2", cx: "50%", cy: "50%" }, [
                h('stop', { offset: "0%", stopColor: "#1a1628" }),
                h('stop', { offset: "100%", stopColor: "#0d0b16" })
              ])
            ]),
            h('rect', { width: "840", height: "620", fill: "url(#bg2)" }),
            // Grid lines
            ...Array.from({ length: 17 }, (_, i) =>
              h('line', { key: `v${i}`, x1: i*52, y1: 0, x2: i*52, y2: 620, stroke: "rgba(255,255,255,0.016)", strokeWidth: "1" })
            ),
            ...Array.from({ length: 13 }, (_, i) =>
              h('line', { key: `h${i}`, x1: 0, y1: i*52, x2: 840, y2: i*52, stroke: "rgba(255,255,255,0.016)", strokeWidth: "1" })
            ),
            // Relationships
            ...relationships.map(r => {
              const from = people.find(p => p.id === r.from);
              const to = people.find(p => p.id === r.to);
              if (!from || !to) return null;
              return h('g', {
                key: r.id,
                style: { cursor: "pointer" },
                onClick: () => { setSelectedRel(r.id); setSelectedPerson(r.to); }
              }, [
                h(ConnectionLine, { from, to, rel: r }),
                h('line', { x1: from.x, y1: from.y, x2: to.x, y2: to.y, stroke: "transparent", strokeWidth: "20" })
              ]);
            }).filter(Boolean),
            // People
            ...people.map(p =>
              h(PersonNode, {
                key: p.id,
                person: p,
                isSelected: selectedPerson === p.id || (rel && (rel.from === p.id || rel.to === p.id)),
                onClick: handlePersonClick,
                onDrag: handleDrag
              })
            )
          ]),
          
          // Legend
          h('div', {
            style: {
              position: "absolute",
              bottom: 14,
              left: 14,
              display: "flex",
              flexDirection: "column",
              gap: 5
            }
          }, DIMENSIONS.map(d =>
            h('div', {
              key: d.id,
              style: { display: "flex", alignItems: "center", gap: 8 }
            }, [
              h('div', {
                style: {
                  width: 18,
                  height: 2.5,
                  borderRadius: 2,
                  background: d.color,
                  opacity: 0.7
                }
              }),
              h('span', {
                style: {
                  fontSize: 8.5,
                  color: "rgba(255,255,255,0.3)",
                  letterSpacing: "0.07em"
                }
              }, d.label)
            ])
          )),

          // Add person controls
          h('div', {
            style: { position: "absolute", top: 12, right: 12 }
          }, addingPerson ? 
            h('div', { style: { display: "flex", gap: 6 } }, [
              h('input', {
                type: "text",
                value: newName,
                onChange: e => setNewName(e.target.value),
                onKeyDown: e => {
                  if (e.key === "Enter") addPerson();
                  if (e.key === "Escape") setAddingPerson(false);
                },
                placeholder: "their name",
                autoFocus: true,
                style: { width: 110 }
              }),
              h('button', {
                className: "node-btn",
                onClick: addPerson,
                style: {
                  color: "rgba(180,255,180,0.7)",
                  borderColor: "rgba(180,255,180,0.2)"
                }
              }, "add"),
              h('button', {
                className: "node-btn",
                onClick: () => setAddingPerson(false)
              }, "×")
            ]) :
            h('button', {
              className: "node-btn",
              onClick: () => setAddingPerson(true)
            }, "+ add person")
          )
        ]),

        // Side panel with relationship editing
        h('div', {
          style: {
            width: 300,
            borderLeft: "1px solid rgba(255,255,255,0.06)",
            padding: "18px 18px",
            overflowY: "auto",
            display: "flex",
            flexDirection: "column",
            gap: 0,
            background: "rgba(10,9,18,0.65)"
          }
        }, rel && fromPerson && toPerson ? [
          // Relationship editor content would go here
          h('div', { style: { color: "rgba(255,255,255,0.6)" } }, `Editing ${fromPerson.name} & ${toPerson.name}`)
        ] : [
          h('div', {
            style: {
              color: "rgba(255,255,255,0.2)",
              fontSize: 11,
              fontStyle: "italic",
              lineHeight: 1.8,
              paddingTop: 8
            }
          }, "Click a connection to edit")
        ])
      ]) :
      
      // Compare view
      h('div', {
        style: {
          flex: 1,
          overflow: "auto",
          padding: "24px 32px"
        }
      }, [
        h('div', { style: { marginBottom: 22 } }, [
          h('div', {
            style: {
              fontFamily: "'Cormorant Garamond', serif",
              fontSize: 18,
              fontStyle: "italic",
              color: "rgba(255,255,255,0.6)",
              marginBottom: 6
            }
          }, "the myth of the one"),
          h('div', {
            style: {
              fontSize: 10,
              color: "rgba(255,255,255,0.25)",
              lineHeight: 1.75,
              maxWidth: 580
            }
          }, "romantic normativity demands one person match you at 5/5 across every dimension — relational style, erotic needs, emotional load, neurodivergent wiring, mental health, identity, unmet needs, somatic capacity. an architecture designed to fail.")
        ]),
        
        h('div', {
          style: { display: "flex", flexWrap: "wrap", gap: 20 }
        }, [
          // Myth card
          h('div', {
            style: {
              background: "rgba(255,255,255,0.02)",
              border: "1px solid rgba(255,80,80,0.15)",
              borderRadius: 12,
              padding: 20,
              minWidth: 210
            }
          }, [
            h('div', {
              style: {
                fontSize: 8.5,
                letterSpacing: "0.15em",
                color: "rgba(255,80,80,0.5)",
                textTransform: "uppercase",
                marginBottom: 8
              }
            }, "the myth"),
            h('div', {
              style: {
                fontFamily: "'Cormorant Garamond', serif",
                fontSize: 13,
                fontStyle: "italic",
                color: "rgba(255,255,255,0.4)",
                marginBottom: 14
              }
            }, '"one person, all 8 dimensions, 5/5"'),
            h(RadarChart, { dims: romanticNormDims, size: 140 })
          ])
        ])
      ])
  ]);
}

// Initialize the app
function initializeApp() {
  const container = document.getElementById('relationship-ecosystem');
  if (container) {
    const root = ReactDOM.createRoot(container);
    root.render(React.createElement(RelationshipEcosystem));
  }
}

// Auto-initialize if DOM is ready, otherwise wait for it
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initializeApp);
} else {
  initializeApp();
}

// Export for manual initialization
window.RelationshipEcosystem = {
  init: initializeApp,
  component: RelationshipEcosystem
}</script>