// ui_kits/or/biblical-graph.jsx
// The full layered architecture. Each layer is its own graph that anchors
// upward to the layer above. Scripture is the root; faith in the Word gives
// meaning to the domains of inquiry (theology, philosophy, science, math,
// history, arts, government); historical exemplars in each domain serve as
// examples of those principles lived out faithfully. From there: virtues,
// frameworks, rituals, entities.
//
// Read bottom-up: an entity (David Shepherd) anchors to virtues + frameworks
// + rituals; those anchor to exemplars (the historical examples that show
// the virtues lived out); exemplars anchor to domains; domains anchor to
// scripture. Everything in Or traces back to the Word.

const { useState: useStateBG, useMemo: useMemoBG } = React;

// ---------- Canvas + tier layout ----------
const VIEW_W = 1480;
const VIEW_H = 1700;
const PAD_X = 220;
const PAD_R = 80;
const SPAN_X1 = PAD_X;
const SPAN_X2 = VIEW_W - PAD_X;

const TIER = {
  scripture: { y:   80, label: "Layer 00 · Scripture",  sub: "the root · faith in the Word gives meaning to all that follows" },
  domain:    { y:  260, label: "Layer 01 · Domains",    sub: "the principal fields of inquiry the Word illuminates" },
  exemplar:  { y:  500, label: "Layer 02 · Exemplars",  sub: "historical people who lived these domains faithfully" },
  virtue:    { y:  780, label: "Layer 03 · Virtues",    sub: "14 meta-nodes — the canon's named qualities" },
  framework: { y: 1000, label: "Layer 04a · Frameworks", sub: "16 tools the canon shaped" },
  ritual:    { y: 1200, label: "Layer 04b · Rituals",   sub: "6 rhythms the canon practices" },
  entity:    { y: 1430, label: "Layer 05 · Entities",   sub: "instances anchored to every layer above" },
};

const R_SCRIPTURE = 36;
const R_DOMAIN = 11;
const R_EXEMPLAR = 6;
const R_VIRTUE = 7;
const R_FW = 4.5;
const R_RT = 5.5;
const R_ENTITY = 18;

// ---------- Helpers ----------
function spread(n, x0 = SPAN_X1, x1 = SPAN_X2) {
  const span = x1 - x0;
  if (n <= 1) return [(x0 + x1) / 2];
  return Array.from({ length: n }, (_, i) => x0 + (i / (n - 1)) * span);
}
function canonical(s) {
  return (s || "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
}
function abbrev(name, max) {
  if (!name || name.length <= max) return name;
  return name.slice(0, max - 1) + "…";
}

// ---------- Tier label ----------
function TierLabel({ y, label, sub, color }) {
  return (
    <g>
      <text x={PAD_R} y={y - 6} fontFamily="var(--font-mono)" fontSize="11"
            letterSpacing="0.12em" fill={color || "var(--fg-4)"}>
        {label}
      </text>
      <text x={PAD_R} y={y + 12} fontFamily="var(--font-body)" fontSize="11"
            fill={color || "var(--fg-3)"} fontStyle="italic">
        {sub}
      </text>
    </g>
  );
}

// ---------- Position exemplars: cluster under their primary domain ----------
function positionExemplars(exemplars, domainsById) {
  // Group by primary (first-listed) domain
  const byDomain = {};
  for (const ex of exemplars) {
    const primary = ex.domains[0];
    (byDomain[primary] = byDomain[primary] || []).push(ex);
  }
  const positioned = [];
  for (const [domainId, group] of Object.entries(byDomain)) {
    const dx = domainsById[domainId]?.x ?? VIEW_W / 2;
    // Stagger group members horizontally near the domain x
    const groupWidth = Math.min(140, (group.length - 1) * 36);
    const startX = dx - groupWidth / 2;
    group.forEach((ex, i) => {
      const x = group.length === 1 ? dx : startX + (i / (group.length - 1)) * groupWidth;
      // Stagger y slightly to reduce label collisions (every other up/down 18px)
      const y = TIER.exemplar.y + (i % 2 === 0 ? 0 : 32);
      positioned.push({ ...ex, x, y });
    });
  }
  return positioned;
}

// ---------- The graph ----------
function BiblicalGraph() {
  const { metaNodes, frameworks, rituals, entity, domains, exemplars } = window.OR_DATA;
  const [hover, setHover] = useStateBG(null);
  const [layerVis, setLayerVis] = useStateBG({
    scripture: true, domain: true, exemplar: true,
    virtue: true, framework: true, ritual: true, entity: true,
  });

  // Positions
  const scripture = { id: "scripture", x: VIEW_W / 2, y: TIER.scripture.y };

  const domainNodes = useMemoBG(() => {
    const xs = spread(domains.length);
    return domains.map((d, i) => ({ ...d, x: xs[i], y: TIER.domain.y }));
  }, [domains]);
  const domainById = useMemoBG(() => Object.fromEntries(domainNodes.map((d) => [d.id, d])), [domainNodes]);

  const exemplarNodes = useMemoBG(() => positionExemplars(exemplars, domainById), [exemplars, domainById]);

  const virtues = useMemoBG(() => {
    const xs = spread(metaNodes.length);
    return metaNodes.map((m, i) => ({ ...m, x: xs[i], y: TIER.virtue.y }));
  }, [metaNodes]);
  const virtueById = useMemoBG(() => Object.fromEntries(virtues.map((v) => [v.id, v])), [virtues]);

  const fwNodes = useMemoBG(() => {
    const xs = spread(frameworks.length);
    return frameworks.map((fw, i) => ({ ...fw, x: xs[i], y: TIER.framework.y, canonName: canonical(fw.name) }));
  }, [frameworks]);

  const rtNodes = useMemoBG(() => {
    const xs = spread(rituals.length, SPAN_X1 + 180, SPAN_X2 - 180);
    return rituals.map((r, i) => ({
      ...r,
      id: canonical(r.name).replace(/\s+/g, "-"),
      x: xs[i], y: TIER.ritual.y,
      canonName: canonical(r.name),
    }));
  }, [rituals]);

  const entities = useMemoBG(() => {
    const list = [
      {
        id: "david", kind: "person", name: entity.name, anchor: entity.biblicalAnchor,
        attended: entity.virtuesAttended.map((v) => v.id),
        under:    entity.virtuesUnderAttended.map((v) => v.id),
        frameworks: entity.frameworksInUse.map(canonical),
        rituals:    entity.ritualsPracticed.map(canonical),
        exemplars: [], // person doesn't link forward to exemplars; conceptually they ARE one
      },
      {
        id: "israel", kind: "org", name: "Israel", anchor: "Isaiah 49:6",
        attended: ["unity", "community", "service", "purpose"],
        under: ["accountability"],
        frameworks: [canonical("Tabletop Group"), canonical("Strategy Pure & Simple"), canonical("Rule of Life")],
        rituals: ["sabbath rest", "service", "community"],
        exemplars: [],
      },
      {
        id: "yehi-project", kind: "project", name: "Yahweh OS", anchor: "Eph 2:10",
        attended: ["purpose", "stewardship", "growth", "wisdom"],
        under: ["rest"],
        frameworks: [canonical("Agile/Scrum"), canonical("Strategy Pure & Simple")],
        rituals: ["reflection review"],
        exemplars: [],
      },
    ];
    const xs = spread(list.length, SPAN_X1 + 280, SPAN_X2 - 280);
    return list.map((e, i) => ({ ...e, x: xs[i], y: TIER.entity.y }));
  }, [entity]);

  // ---------- Highlight resolution ----------
  const lit = useMemoBG(() => {
    if (!hover) return null;
    const d = new Set(), ex = new Set(), v = new Set(), fw = new Set(), rt = new Set(), en = new Set();
    let scr = false;

    if (hover.kind === "scripture") {
      scr = true;
      domainNodes.forEach((dn) => d.add(dn.id));
    } else if (hover.kind === "domain") {
      scr = true; d.add(hover.id);
      exemplarNodes.forEach((e) => { if (e.domains.includes(hover.id)) ex.add(e.id); });
    } else if (hover.kind === "exemplar") {
      scr = true; ex.add(hover.id);
      const e = exemplarNodes.find((x) => x.id === hover.id);
      if (e) e.domains.forEach((did) => d.add(did));
    } else if (hover.kind === "virtue") {
      scr = true; v.add(hover.id);
      fwNodes.forEach((f) => { if (f.virtues.includes(hover.id)) fw.add(f.id); });
      rtNodes.forEach((r) => {
        if ((r.virtues || []).some((vn) => canonical(vn) === hover.id)) rt.add(r.id);
      });
      entities.forEach((e) => { if (e.attended.includes(hover.id) || e.under.includes(hover.id)) en.add(e.id); });
    } else if (hover.kind === "framework") {
      scr = true; fw.add(hover.id);
      const f = fwNodes.find((x) => x.id === hover.id);
      if (f) {
        f.virtues.forEach((vid) => v.add(vid));
        entities.forEach((e) => { if (e.frameworks.includes(f.canonName)) en.add(e.id); });
      }
    } else if (hover.kind === "ritual") {
      scr = true; rt.add(hover.id);
      const r = rtNodes.find((x) => x.id === hover.id);
      if (r) {
        (r.virtues || []).forEach((vname) => {
          const m = virtues.find((vv) => canonical(vv.name) === canonical(vname) || vv.id === canonical(vname));
          if (m) v.add(m.id);
        });
        entities.forEach((e) => { if (e.rituals.includes(r.canonName)) en.add(e.id); });
      }
    } else if (hover.kind === "entity") {
      scr = true; en.add(hover.id);
      const e = entities.find((x) => x.id === hover.id);
      if (e) {
        e.attended.forEach((vid) => v.add(vid));
        e.under.forEach((vid) => v.add(vid));
        fwNodes.forEach((f) => { if (e.frameworks.includes(f.canonName)) fw.add(f.id); });
        rtNodes.forEach((r) => { if (e.rituals.includes(r.canonName)) rt.add(r.id); });
      }
    }
    return { d, ex, v, fw, rt, en, scr };
  }, [hover, domainNodes, exemplarNodes, virtues, fwNodes, rtNodes, entities]);

  const isLit = (set, id) => !lit || lit[set].has(id);
  const isScrLit = () => !lit || lit.scr;
  const dimOpacity = (set, id) => isLit(set, id) ? 1 : 0.28;
  const lineLit = (...checks) => !lit || checks.every(Boolean);

  return (
    <section style={{ padding: "48px 0 32px", borderBottom: "1px solid var(--hairline)" }}>
      <OContainer>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
          <OrIcon name="canon" size={16} />
          <MetaEyebrowO>The biblical graph · the full layered architecture</MetaEyebrowO>
        </div>
        <h2 style={{
          fontFamily: "var(--font-display)", fontWeight: 300,
          fontVariationSettings: '"opsz" 144',
          fontSize: "clamp(28px, 3.4vw, 40px)", letterSpacing: "-0.015em",
          lineHeight: 1.1, color: "var(--fg-1)", margin: "0 0 6px",
        }}>
          The Word gives meaning. Domains illuminate. Exemplars embody. Everything anchors.
        </h2>
        <p style={{
          margin: "0 0 22px", fontFamily: "var(--font-body)", fontSize: 15,
          color: "var(--fg-3)", lineHeight: 1.55, maxWidth: 880,
        }}>
          Scripture is the root. Faith in the Word gives meaning to the domains of inquiry — theology, philosophy, science, mathematics, history, arts, government. Historical people in those domains serve as examples of the principles lived out faithfully. From that grounded foundation, virtues, frameworks, rituals, and entities follow. Hover any node to trace its anchoring all the way up.
        </p>

        <LayerVisToggles layerVis={layerVis} setLayerVis={setLayerVis} />

        <div style={{ border: "1px solid var(--hairline)", background: "rgba(250,247,242,0.01)", marginTop: 14 }}>
          <svg viewBox={`0 0 ${VIEW_W} ${VIEW_H}`} style={{ width: "100%", height: "auto", display: "block" }}>
            {/* Tier labels */}
            {layerVis.scripture && <TierLabel y={TIER.scripture.y} label={TIER.scripture.label} sub={TIER.scripture.sub} color="var(--tehom-signal)" />}
            {layerVis.domain    && <TierLabel y={TIER.domain.y}    label={TIER.domain.label}    sub={TIER.domain.sub} />}
            {layerVis.exemplar  && <TierLabel y={TIER.exemplar.y}  label={TIER.exemplar.label}  sub={TIER.exemplar.sub} />}
            {layerVis.virtue    && <TierLabel y={TIER.virtue.y}    label={TIER.virtue.label}    sub={TIER.virtue.sub} />}
            {layerVis.framework && <TierLabel y={TIER.framework.y} label={TIER.framework.label} sub={TIER.framework.sub} />}
            {layerVis.ritual    && <TierLabel y={TIER.ritual.y}    label={TIER.ritual.label}    sub={TIER.ritual.sub} />}
            {layerVis.entity    && <TierLabel y={TIER.entity.y}    label={TIER.entity.label}    sub={TIER.entity.sub} />}

            {/* ===== ANCHORING LINES ===== */}

            {/* Entity → frameworks/rituals/virtues */}
            {layerVis.entity && entities.flatMap((e) => {
              const lh = !lit || lit.en.has(e.id);
              const links = [];
              if (layerVis.framework) fwNodes.forEach((f) => {
                if (e.frameworks.includes(f.canonName)) {
                  links.push(<line key={`e-fw-${e.id}-${f.id}`} x1={e.x} y1={e.y - R_ENTITY} x2={f.x} y2={f.y + R_FW}
                    stroke="var(--tehom-utility)" strokeWidth="1"
                    opacity={lh ? 0.7 : 0.12} strokeDasharray={lh ? "0" : "2 3"} />);
                }
              });
              if (layerVis.ritual) rtNodes.forEach((r) => {
                if (e.rituals.includes(r.canonName)) {
                  links.push(<line key={`e-rt-${e.id}-${r.id}`} x1={e.x} y1={e.y - R_ENTITY} x2={r.x} y2={r.y + R_RT}
                    stroke="var(--tehom-utility)" strokeWidth="1"
                    opacity={lh ? 0.7 : 0.12} strokeDasharray={lh ? "0" : "2 3"} />);
                }
              });
              return links;
            })}

            {/* Ritual → virtue */}
            {layerVis.ritual && rtNodes.flatMap((r) =>
              (r.virtues || []).map((vname) => {
                const v = virtues.find((vv) => canonical(vv.name) === canonical(vname) || vv.id === canonical(vname));
                if (!v) return null;
                const lh = !lit || (lit.rt.has(r.id) && lit.v.has(v.id));
                return <line key={`rt-v-${r.id}-${v.id}`} x1={r.x} y1={r.y - R_RT} x2={v.x} y2={v.y + R_VIRTUE}
                  stroke="var(--tehom-utility)" strokeWidth="1"
                  opacity={lh ? 0.6 : 0.10} strokeDasharray={lh ? "0" : "2 3"} />;
              })
            )}

            {/* Framework → virtue */}
            {layerVis.framework && fwNodes.flatMap((f) =>
              f.virtues.map((vid) => {
                const v = virtueById[vid];
                if (!v) return null;
                const lh = !lit || (lit.fw.has(f.id) && lit.v.has(v.id));
                return <line key={`fw-v-${f.id}-${v.id}`} x1={f.x} y1={f.y - R_FW} x2={v.x} y2={v.y + R_VIRTUE}
                  stroke="var(--tehom-utility)" strokeWidth="1"
                  opacity={lh ? 0.6 : 0.10} strokeDasharray={lh ? "0" : "2 3"} />;
              })
            )}

            {/* Virtue → exemplar (each virtue traces to exemplars; we show via implicit
                cross-cut: virtues are CROSS-CUTTING across exemplars rather than directly
                anchored to one). Drawn only for hovered virtue to avoid clutter. */}
            {/* (intentionally no Virtue → Exemplar direct lines; the relationship is
                cross-cutting and traced UP through scripture, not via direct anchor) */}

            {/* Exemplar → domain */}
            {layerVis.exemplar && exemplarNodes.flatMap((e) =>
              e.domains.map((did) => {
                const d = domainById[did];
                if (!d) return null;
                const lh = !lit || (lit.ex.has(e.id) && lit.d.has(d.id));
                return <line key={`ex-d-${e.id}-${did}`} x1={e.x} y1={e.y - R_EXEMPLAR} x2={d.x} y2={d.y + R_DOMAIN}
                  stroke="var(--tehom-utility)" strokeWidth="1"
                  opacity={lh ? 0.65 : 0.16} strokeDasharray={lh ? "0" : "2 3"} />;
              })
            )}

            {/* Domain → scripture (gold spokes, the "faith in the Word" connections) */}
            {layerVis.domain && domainNodes.map((d) => {
              const lh = !lit || lit.d.has(d.id);
              return <line key={`d-s-${d.id}`} x1={d.x} y1={d.y - R_DOMAIN} x2={scripture.x} y2={scripture.y + R_SCRIPTURE}
                stroke={lh ? "var(--tehom-signal)" : "var(--tehom-utility)"} strokeWidth="1"
                opacity={lh ? 0.85 : 0.30} />;
            })}

            {/* Virtue → scripture (also gold, since virtues are direct scriptural meta-nodes) */}
            {/* This jumps the domain layer because virtues are CANONICAL meta-nodes from
                Universal_Map.md — they trace directly to scripture, not via domain. */}
            {layerVis.virtue && virtues.map((v) => {
              const lh = !lit || lit.v.has(v.id);
              return <line key={`v-s-${v.id}`} x1={v.x} y1={v.y - R_VIRTUE} x2={scripture.x} y2={scripture.y + R_SCRIPTURE}
                stroke={lh ? "var(--tehom-signal)" : "var(--tehom-utility)"} strokeWidth="1"
                opacity={lh ? 0.55 : 0.10} strokeDasharray="3 4" />;
            })}

            {/* ===== NODES ===== */}

            {/* Scripture */}
            {layerVis.scripture && (
              <g style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "scripture" })}
                 onMouseLeave={() => setHover(null)}>
                <circle cx={scripture.x} cy={scripture.y} r={R_SCRIPTURE + 8} fill="none"
                        stroke="var(--tehom-signal)" strokeWidth="1" opacity={isScrLit() ? 0.5 : 0.2} />
                <circle cx={scripture.x} cy={scripture.y} r={R_SCRIPTURE}
                        fill="var(--tehom-deep-elevated)" stroke="var(--tehom-signal)" strokeWidth="1.5" />
                <text x={scripture.x} y={scripture.y - 3}
                      fontFamily="var(--font-display)" fontWeight="300"
                      fontSize="17" fill="var(--fg-1)" textAnchor="middle"
                      style={{ fontVariationSettings: '"opsz" 144' }}>Scripture</text>
                <text x={scripture.x} y={scripture.y + 13}
                      fontFamily="var(--font-mono)" fontSize="9"
                      letterSpacing="0.12em" fill="var(--tehom-signal)" textAnchor="middle">THE WORD</text>
              </g>
            )}

            {/* Domains */}
            {layerVis.domain && domainNodes.map((d) => (
              <g key={`d-${d.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "domain", id: d.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("d", d.id)}>
                <circle cx={d.x} cy={d.y} r={R_DOMAIN + 4} fill="none"
                        stroke="var(--tehom-signal)" strokeWidth="0.5" opacity="0.4" />
                <circle cx={d.x} cy={d.y} r={R_DOMAIN}
                        fill="var(--tehom-deep-elevated)" stroke="var(--tehom-signal)" strokeWidth="1.2" />
                <text x={d.x} y={d.y + R_DOMAIN + 18}
                      fontFamily="var(--font-display)" fontWeight="300"
                      fontVariationSettings='"opsz" 144'
                      fontSize="15" fill="var(--fg-1)" textAnchor="middle">{d.name}</text>
                <text x={d.x} y={d.y + R_DOMAIN + 33}
                      fontFamily="var(--font-mono)" fontSize="10"
                      letterSpacing="0.02em" fill="var(--tehom-utility)" textAnchor="middle">{d.anchor}</text>
              </g>
            ))}



            {/* Exemplars */}
            {layerVis.exemplar && exemplarNodes.map((e) => (
              <g key={`ex-${e.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "exemplar", id: e.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("ex", e.id)}>
                <circle cx={e.x} cy={e.y} r={R_EXEMPLAR}
                        fill="var(--tehom-deep)" stroke="var(--fg-2)" strokeWidth="1" />
                <text x={e.x} y={e.y + 18}
                      fontFamily="var(--font-body)" fontSize="11" fontWeight="500"
                      fill="var(--fg-1)" textAnchor="middle">{e.name}</text>
                <text x={e.x} y={e.y + 30}
                      fontFamily="var(--font-mono)" fontSize="9"
                      letterSpacing="0.02em" fill="var(--fg-4)" textAnchor="middle">{e.era}</text>
              </g>
            ))}

            {/* Virtues */}
            {layerVis.virtue && virtues.map((v) => (
              <g key={`v-${v.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "virtue", id: v.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("v", v.id)}>
                <circle cx={v.x} cy={v.y} r={R_VIRTUE}
                        fill="var(--tehom-deep-elevated)" stroke="var(--fg-1)" strokeWidth="1" />
                <text x={v.x} y={v.y + 22} fontFamily="var(--font-body)" fontSize="11"
                      fill="var(--fg-2)" textAnchor="middle">
                  {v.name.length > 14 ? v.name.split(" / ")[0].split(" ").slice(0, 2).join(" ") : v.name}
                </text>
                <text x={v.x} y={v.y + 35} fontFamily="var(--font-mono)" fontSize="9"
                      letterSpacing="0.02em" fill="var(--tehom-utility)" textAnchor="middle">{v.scripture}</text>
              </g>
            ))}

            {/* Frameworks */}
            {layerVis.framework && fwNodes.map((f) => (
              <g key={`fw-${f.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "framework", id: f.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("fw", f.id)}>
                <circle cx={f.x} cy={f.y} r={R_FW}
                        fill="var(--tehom-deep)" stroke="var(--fg-2)" strokeWidth="1" />
                <text x={f.x} y={f.y + 18} fontFamily="var(--font-mono)" fontSize="9"
                      letterSpacing="0.02em" fill="var(--fg-3)" textAnchor="middle"
                      transform={`rotate(35 ${f.x} ${f.y + 18})`}>{abbrev(f.name, 22)}</text>
              </g>
            ))}

            {/* Rituals */}
            {layerVis.ritual && rtNodes.map((r) => (
              <g key={`rt-${r.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "ritual", id: r.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("rt", r.id)}>
                <circle cx={r.x} cy={r.y} r={R_RT}
                        fill="var(--tehom-deep)" stroke="var(--fg-2)" strokeWidth="1" />
                <text x={r.x} y={r.y + 22} fontFamily="var(--font-body)" fontSize="11"
                      fill="var(--fg-2)" textAnchor="middle">{r.name}</text>
                <text x={r.x} y={r.y + 35} fontFamily="var(--font-mono)" fontSize="9"
                      letterSpacing="0.02em" fill="var(--tehom-utility)" textAnchor="middle">{r.anchor}</text>
              </g>
            ))}

            {/* Entities */}
            {layerVis.entity && entities.map((e) => (
              <g key={`e-${e.id}`} style={{ cursor: "pointer" }}
                 onMouseEnter={() => setHover({ kind: "entity", id: e.id })}
                 onMouseLeave={() => setHover(null)}
                 opacity={dimOpacity("en", e.id)}>
                <circle cx={e.x} cy={e.y} r={R_ENTITY + 4} fill="none"
                        stroke="var(--tehom-signal)" strokeWidth="1" opacity="0.35" />
                <circle cx={e.x} cy={e.y} r={R_ENTITY}
                        fill="var(--tehom-deep-elevated)" stroke="var(--fg-1)" strokeWidth="1" />
                <text x={e.x} y={e.y + 4} fontFamily="var(--font-mono)" fontSize="9"
                      fill="var(--tehom-signal)" textAnchor="middle" letterSpacing="0.08em">
                  {e.kind.toUpperCase()}
                </text>
                <text x={e.x} y={e.y + 38} fontFamily="var(--font-display)" fontWeight="300"
                      fontVariationSettings='"opsz" 144'
                      fontSize="16" fill="var(--fg-1)" textAnchor="middle">{e.name}</text>
                <text x={e.x} y={e.y + 56} fontFamily="var(--font-mono)" fontSize="10"
                      fill="var(--tehom-utility)" textAnchor="middle" letterSpacing="0.02em">{e.anchor}</text>
              </g>
            ))}
          </svg>
        </div>

        <HoverLegend hover={hover} domains={domainNodes} exemplars={exemplarNodes}
                     virtues={virtues} fwNodes={fwNodes} rtNodes={rtNodes} entities={entities} />
      </OContainer>
    </section>
  );
}

// ---------- Layer toggles ----------
function LayerVisToggles({ layerVis, setLayerVis }) {
  const layers = [
    { id: "scripture", label: "Scripture",   count: 1 },
    { id: "domain",    label: "Domains",     count: 7 },
    { id: "exemplar",  label: "Exemplars",   count: 19 },
    { id: "virtue",    label: "Virtues",     count: 14 },
    { id: "framework", label: "Frameworks",  count: 16 },
    { id: "ritual",    label: "Rituals",     count: 6 },
    { id: "entity",    label: "Entities",    count: 3 },
  ];
  return (
    <div style={{ display: "flex", gap: 6, marginTop: 10, flexWrap: "wrap" }}>
      {layers.map((l) => {
        const on = layerVis[l.id];
        return (
          <button
            key={l.id}
            onClick={() => setLayerVis({ ...layerVis, [l.id]: !on })}
            style={{
              padding: "6px 12px",
              background: on ? "rgba(201,169,97,0.10)" : "transparent",
              border: `1px solid ${on ? "var(--tehom-signal)" : "var(--hairline)"}`,
              color: on ? "var(--fg-1)" : "var(--fg-4)",
              fontFamily: "var(--font-mono)",
              fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase",
              cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 8,
              transition: "all var(--dur-quick) var(--ease-quiet)",
            }}>
            <span style={{
              width: 7, height: 7, borderRadius: 999,
              background: on ? "var(--tehom-signal)" : "transparent",
              border: on ? "none" : "1px solid var(--fg-4)",
            }} />
            {l.label}
            <span style={{ color: "var(--fg-4)", opacity: 0.7 }}>· {l.count}</span>
          </button>
        );
      })}
    </div>
  );
}

// ---------- Hover legend ----------
function HoverLegend({ hover, domains, exemplars, virtues, fwNodes, rtNodes, entities }) {
  let title = "Hover any node";
  let detail = "The Word is the root. Every domain of inquiry — every exemplar, every virtue, every framework, every ritual, every entity — ultimately answers to Scripture. Hover any node to trace its anchoring path through the layers.";
  let scripture = null;
  let extras = [];

  if (hover) {
    if (hover.kind === "scripture") {
      title = "Scripture — the Word";
      detail = "The root. Every layer below answers to it. Faith in the Word is what gives meaning to all the principles that follow.";
    } else if (hover.kind === "domain") {
      const d = domains.find((x) => x.id === hover.id);
      if (d) {
        title = d.name; scripture = d.anchor; detail = d.blurb;
        const exs = exemplars.filter((e) => e.domains.includes(d.id)).length;
        extras = [`${exs} exemplars`];
      }
    } else if (hover.kind === "exemplar") {
      const e = exemplars.find((x) => x.id === hover.id);
      if (e) {
        title = `${e.name} · ${e.era}`;
        detail = `Faithful exemplar in ${e.domains.map((did) => domains.find((d) => d.id === did)?.name).filter(Boolean).join(" + ")}. Anchored to scripture through faithful work in this domain.`;
        extras = [e.anchor];
      }
    } else if (hover.kind === "virtue") {
      const v = virtues.find((x) => x.id === hover.id);
      if (v) {
        title = v.name; scripture = v.scripture;
        const fws = fwNodes.filter((f) => f.virtues.includes(v.id)).length;
        const ens = entities.filter((e) => e.attended.includes(v.id) || e.under.includes(v.id)).length;
        extras = [`${fws} frameworks`, `${ens} entities`];
        detail = "Virtue · meta-node. A quality the canon names — anchored directly to scripture and inherited by frameworks, rituals, and entities below.";
      }
    } else if (hover.kind === "framework") {
      const f = fwNodes.find((x) => x.id === hover.id);
      if (f) { title = f.name; detail = f.summary; extras = [`${f.virtues.length} virtues`]; }
    } else if (hover.kind === "ritual") {
      const r = rtNodes.find((x) => x.id === hover.id);
      if (r) { title = r.name; scripture = r.anchor; detail = `Ritual · ${r.cadence}.`; }
    } else if (hover.kind === "entity") {
      const e = entities.find((x) => x.id === hover.id);
      if (e) {
        title = e.name; scripture = e.anchor;
        detail = `Entity · ${e.kind}. ${e.attended.length} attended virtues, ${e.under.length} under-attended, ${e.frameworks.length} frameworks in use, ${e.rituals.length} rituals practiced.`;
      }
    }
  }

  return (
    <div style={{
      marginTop: 18, padding: "16px 20px",
      border: "1px solid var(--hairline)",
      display: "grid", gridTemplateColumns: "180px 1fr auto", gap: 24,
      minHeight: 84, alignItems: "start",
    }}>
      <div>
        <MetaEyebrowO>{hover ? hover.kind : "instructions"}</MetaEyebrowO>
        <div style={{ marginTop: 6, fontFamily: "var(--font-body)", fontSize: 15, fontWeight: 500, color: "var(--fg-1)" }}>
          {title}
        </div>
        {scripture && <div style={{ marginTop: 4 }}><ScripturePin>{scripture}</ScripturePin></div>}
      </div>
      <div style={{ fontFamily: "var(--font-body)", fontSize: 13, color: "var(--fg-3)", lineHeight: 1.55 }}>
        {detail}
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-end" }}>
        {extras.map((x, i) => <MonoTagO key={i}>{x}</MonoTagO>)}
      </div>
    </div>
  );
}

Object.assign(window, { BiblicalGraph, LayerVisToggles, HoverLegend });
