/* global window, React, ReactDOM */
const { useState, useEffect, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "dark",
  "accentHue": 190,
  "glassBlur": 24,
  "glassSat": 140,
  "density": "comfortable",
  "showLegend": true,
  "showPipeline": true,
  "rightRailMode": "contextual"
}/*EDITMODE-END*/;

function useTweaks(defaults) {
  const [t, setT] = useState(defaults);
  useEffect(() => {
    document.documentElement.dataset.theme = t.theme;
    document.documentElement.style.setProperty("--accent-h", t.accentHue);
    document.documentElement.style.setProperty("--glass-blur", t.glassBlur + "px");
    document.documentElement.style.setProperty("--glass-saturate", t.glassSat + "%");
  }, [t]);
  const set = (k, v) => {
    let next;
    if (typeof k === "object") next = { ...t, ...k };
    else next = { ...t, [k]: v };
    setT(next);
    try {
      window.parent.postMessage({ type: "__edit_mode_set_keys", edits: next }, "*");
    } catch (e) {}
  };
  return [t, set];
}

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [zoom, setZoom] = useState(1);
  const [filters, setFilters] = useState(new Set());
  // Tier visibility — each evidence tier is an independent toggle now,
  // not a floor. Confidence is a quality threshold (separate concept).
  // Defaults: all three tiers visible, confidence at 0.85 so the cold
  // canvas surfaces only high-confidence edges (mostly direct).
  const [visibleTiers, setVisibleTiers] = useState({ direct: true, inferred: true, predicted: true });
  const [confidence, setConfidence] = useState(0.85);
  const toggleTier = (t) => setVisibleTiers(prev => ({ ...prev, [t]: !prev[t] }));
  const [activePhase, setActivePhase] = useState(null);
  const [search, setSearch] = useState("");
  const [selectedNodeId, setSelectedNodeId] = useState(null);
  const [selectedKind, setSelectedKind] = useState(null);
  const [selectedQ, setSelectedQ] = useState(null);
  const [hopIndex, setHopIndex] = useState(0);
  const [selectedEndpoint, setSelectedEndpoint] = useState(null);
  const [selectedRouteShape, setSelectedRouteShape] = useState(null);
  const [leftCollapsed, setLeftCollapsed] = useState(false);
  const [rightCollapsed, setRightCollapsed] = useState(false);
  const [tweaksOpen, setTweaksOpen] = useState(false);
  // TweaksPanel is a designer tool; only show on URL ?tweaks=1.
  const showTweaks = typeof window !== "undefined" &&
    new URLSearchParams(window.location.search).get("tweaks") === "1";

  // Track mobile layout state — re-checks on resize so behavior stays
  // correct if the user rotates a device or resizes the window.
  const [isMobileLayout, setIsMobileLayout] = useState(
    typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches
  );
  useEffect(() => {
    if (typeof window === "undefined") return;
    const mq = window.matchMedia("(max-width: 767px)");
    const onChange = (e) => setIsMobileLayout(e.matches);
    mq.addEventListener ? mq.addEventListener("change", onChange) : mq.addListener(onChange);
    return () => {
      mq.removeEventListener ? mq.removeEventListener("change", onChange) : mq.removeListener(onChange);
    };
  }, []);
  const [apiAccessContext, setApiAccessContext] = useState(null); // null = closed; { endpoint? } = open
  const openApiAccess = (endpoint) => setApiAccessContext({ endpoint: endpoint || null });
  const closeApiAccess = () => {
    setApiAccessContext(null);
    setSearch(""); // ⌘K dropdown shouldn't re-open with stale state when modal closes
  };

  // Mobile sheets: null | "filter" | "legend" | "api". Only one open at a
  // time. The right rail's inspector sheet is independent of this.
  const [mobileSheet, setMobileSheet] = useState(null);
  const openMobileSheet = (which) => setMobileSheet(prev => prev === which ? null : which);
  const closeMobileSheet = () => setMobileSheet(null);

  const { NODES, EDGES, QUESTIONS, PIPELINE_STAGES } = window.CIQ;

  // Live counts for the FilterBar readout — updates as user drags
  // confidence or toggles tiers in the legend.
  const visibleEdgeCount = useMemo(
    () => EDGES.filter(e => visibleTiers[e.type] && (e.weight ?? 0.55) >= confidence).length,
    [EDGES, visibleTiers, confidence]
  );
  const totalEdgeCount = EDGES.length;

  // Active highlight set. Selections are mutually exclusive — see the
  // event handlers below; each selection branch clears the others.
  const activeIds = useMemo(() => {
    if (selectedQ) {
      return selectedQ.subgraphNodes || selectedQ.path || [];
    }
    if (selectedRouteShape && selectedRouteShape.path) {
      return selectedRouteShape.path.slice();
    }
    if (activePhase) {
      // Pipeline stage: highlight every node in the stage's domains.
      // The graph predicate then renders multi-hop edges *within* the
      // stage at full opacity, dimming connections that leave it.
      const stage = PIPELINE_STAGES.find(s => s.id === activePhase);
      if (stage) {
        return NODES.filter(n => stage.domainIds.includes(n.kind)).map(n => n.id);
      }
    }
    if (selectedKind) {
      // Domain selection reaches one hop out via DIRECT edges only.
      // The graph is dense enough that any-tier expansion would pull
      // in the entire schema; direct-only keeps the filter tight and
      // meaningful. The evidence floor then layers on top — switch it
      // to "+ Inferred" or "+ Predicted" to widen.
      const domainNodes = NODES.filter(n => n.kind === selectedKind).map(n => n.id);
      const set = new Set(domainNodes);
      EDGES.filter(e => e.type === "direct").forEach(e => {
        if (set.has(e.s)) set.add(e.t);
        if (set.has(e.t)) set.add(e.s);
      });
      return Array.from(set);
    }
    if (selectedNodeId) {
      const neighbors = new Set([selectedNodeId]);
      EDGES.forEach(e => {
        if (e.s === selectedNodeId) neighbors.add(e.t);
        if (e.t === selectedNodeId) neighbors.add(e.s);
      });
      return Array.from(neighbors);
    }
    return [];
  }, [selectedQ, selectedRouteShape, hopIndex, selectedNodeId, selectedKind, activePhase, NODES, EDGES, PIPELINE_STAGES]);

  // No hop animation — subgraph is the answer; reveal it all at once.

  // ⌘K shortcut
  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === "k") {
        e.preventDefault();
        document.querySelector(".search input")?.focus();
      }
      if (e.key === "Escape") {
        setSelectedQ(null);
        setSelectedEndpoint(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const toggleFilter = (k) => {
    if (k === "all") return setFilters(new Set());
    const next = new Set(filters);
    if (next.has(k)) next.delete(k); else next.add(k);
    setFilters(next);
  };

  const stats = {
    nodes: "74.2M",
    edges: "412M",
  };

  const selectedNode = selectedNodeId ? NODES.find(n => n.id === selectedNodeId) : null;

  // Used by CSS to keep the bottom canvas-controls + canvas-filters clear
  // of the right rail when something is selected. When nothing is selected
  // the rail is slid off-screen and the controls hug the canvas edge.
  const noSelection = !selectedNode && !selectedEndpoint && !selectedQ && !selectedRouteShape;
  return React.createElement("div", {
    className: "app",
    "data-rail-collapsed": leftCollapsed && rightCollapsed ? "both" : leftCollapsed ? "left" : rightCollapsed ? "right" : "none",
    "data-mobile-sheet": mobileSheet || "none",
    "data-no-selection": noSelection ? "true" : "false",
  },
    React.createElement(window.Topbar, {
      search, onSearch: setSearch,
      theme: tweaks.theme,
      onTheme: () => setTweak("theme", tweaks.theme === "dark" ? "light" : "dark"),
      onToggleTweaks: () => setTweaksOpen(true),
      onApiAccess: () => openApiAccess(null),
      showTweaks,
      selectedQ: selectedQ?.id,
      onSelectQ: (q) => {
        setSelectedQ(q);
        setHopIndex(0);
        setSelectedEndpoint(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
        setActivePhase(null);
        if (rightCollapsed) setRightCollapsed(false);
      },
    }),

    React.createElement(window.LeftRail, {
      search,
      collapsed: leftCollapsed,
      onToggle: () => setLeftCollapsed(!leftCollapsed),
      mode: "questions",
      selectedQ: selectedQ?.id,
      selectedKind,
      onSelectKind: (k) => {
        setSelectedKind(k);
        setSelectedQ(null);
        setSelectedEndpoint(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
        setActivePhase(null);
      },
      onSelectQ: (q) => {
        setSelectedQ(q);
        setHopIndex(0);
        setSelectedEndpoint(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
        setActivePhase(null);
        if (rightCollapsed) setRightCollapsed(false);
      },
      selectedEndpoint: selectedEndpoint?.id,
      onSelectEndpoint: (e) => {
        setSelectedEndpoint(e);
        setSelectedQ(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
        setActivePhase(null);
        if (rightCollapsed) setRightCollapsed(false);
      },
    }),

    React.createElement("section", { className: "canvas" },
      // Top of canvas — pipeline stage filter only. Edge tier toggles
      // and confidence slider live in the bottom-canvas CanvasFilters
      // pill (OG_schema.html pattern).
      React.createElement(window.FilterBar, {
        activePhase,
        onPhase: (id) => {
          setActivePhase(id);
          if (id) {
            setSelectedQ(null);
            setSelectedNodeId(null);
            setSelectedKind(null);
            setSelectedRouteShape(null);
            setSelectedEndpoint(null);
            if (rightCollapsed) setRightCollapsed(false);
          }
        },
        hasFilterDeviation: (
          activePhase != null ||
          selectedKind != null ||
          confidence !== 0.85 ||
          (visibleTiers && (!visibleTiers.direct || !visibleTiers.inferred || !visibleTiers.predicted))
        ),
        onResetFilters: () => {
          setActivePhase(null);
          setSelectedKind(null);
          setConfidence(0.85);
          setVisibleTiers({ direct: true, inferred: true, predicted: true });
        },
      }),
      React.createElement(window.Graph, {
        activeIds,
        activeQ: selectedQ,
        selectedId: selectedNodeId,
        visibleTiers,
        confidenceMin: confidence,
        theme: tweaks.theme,
        zoom,
        onSelect: (id) => {
          setSelectedNodeId(id);
          setSelectedQ(null);
          setSelectedEndpoint(null);
          setSelectedRouteShape(null);
          setSelectedKind(null);
          setActivePhase(null);
          if (rightCollapsed) setRightCollapsed(false);
        },
      }),
      tweaks.showLegend && React.createElement(window.CanvasLegend, {
        selectedKind,
        onSelectKind: (k) => {
          setSelectedKind(k);
          setSelectedQ(null);
          setSelectedEndpoint(null);
          setSelectedNodeId(null);
          setSelectedRouteShape(null);
          setActivePhase(null);
        },
        visibleTiers,
        onToggleTier: toggleTier,
      }),
      // Bottom-canvas floating filter island — OG_schema.html pattern:
      // edge tier toggles + confidence slider. Anchored inside .canvas
      // so it follows the graph as the viewport resizes.
      React.createElement(window.CanvasFilters, {
        visibleTiers,
        onToggleTier: toggleTier,
        confidence, onConfidence: setConfidence,
        visibleEdgeCount, totalEdgeCount,
      }),

      // Zoom controls — tertiary affordance, sized to match peer canvases
      // (Figma, Mapbox, Apple Maps). Small horizontal pill, hairline border,
      // no glass box. Scroll-wheel and trackpad pinch handle most zoom.
      React.createElement("div", {
        className: "canvas-controls",
        // Visual treatment is inline; positioning lives in components.css
        // so it can shift left when the right rail is open without
        // an inline `right` value winning the cascade.
        style: {
          background: "color-mix(in oklab, var(--bg-2) 70%, transparent)",
          border: "1px solid var(--hairline)",
          backdropFilter: "blur(6px)",
          WebkitBackdropFilter: "blur(6px)",
          zIndex: 10,
        },
      },
        React.createElement("button", {
          className: "icon-btn",
          title: "Zoom out",
          onClick: () => setZoom(z => Math.max(0.5, z / 1.2)),
          style: { width: 26, height: 26, borderRadius: 999 },
        }, React.createElement(window.Icons.Minus, { size: 12 })),
        React.createElement("div", { style: { width: 1, height: 16, background: "var(--hairline)" } }),
        React.createElement("button", {
          className: "icon-btn",
          title: "Fit to view",
          onClick: () => setZoom(1),
          style: { width: 26, height: 26, borderRadius: 999 },
        }, React.createElement(window.Icons.Fit, { size: 12 })),
        React.createElement("div", { style: { width: 1, height: 16, background: "var(--hairline)" } }),
        React.createElement("button", {
          className: "icon-btn",
          title: "Zoom in",
          onClick: () => setZoom(z => Math.min(2.5, z * 1.2)),
          style: { width: 26, height: 26, borderRadius: 999 },
        }, React.createElement(window.Icons.Plus, { size: 12 }))
      ),

      // ── Mobile control nav — only renders at <768px via CSS ────
      // Three icon-buttons mirror the desktop chrome: filters, legend,
      // API endpoint browser. Each opens a fullscreen overlay sheet.
      React.createElement("nav", {
        className: "mobile-nav",
        "aria-label": "Mobile controls",
      },
        React.createElement("button", {
          type: "button",
          className: "mobile-nav-btn",
          "aria-pressed": mobileSheet === "filter" ? "true" : "false",
          onClick: () => openMobileSheet("filter"),
          title: "Filter the graph",
        },
          React.createElement(window.Icons.Filter, { size: 14 }),
          React.createElement("span", null, "Filter")
        ),
        React.createElement("button", {
          type: "button",
          className: "mobile-nav-btn",
          "aria-pressed": mobileSheet === "legend" ? "true" : "false",
          onClick: () => openMobileSheet("legend"),
          title: "Legend",
        },
          React.createElement(window.Icons.Schema || window.Icons.Compass, { size: 14 }),
          React.createElement("span", null, "Legend")
        ),
        React.createElement("button", {
          type: "button",
          className: "mobile-nav-btn",
          "aria-pressed": mobileSheet === "api" ? "true" : "false",
          onClick: () => openMobileSheet("api"),
          title: "Browse API endpoints",
        },
          React.createElement(window.Icons.Code, { size: 14 }),
          React.createElement("span", null, "API")
        )
      ),

      // ── Mobile sheet backdrop — tap to dismiss ────────────────
      mobileSheet && React.createElement("div", {
        className: "mobile-sheet-backdrop",
        onClick: closeMobileSheet,
      })
    ),

    React.createElement(window.RightRail, {
      collapsed: rightCollapsed,
      onToggle: () => {
        // On mobile the right rail is a bottom sheet — close clears
        // selection. On desktop, close collapses the side panel.
        // Uses live `isMobileLayout` state so resizing mid-session
        // dispatches to the correct branch.
        if (isMobileLayout) {
          setSelectedNodeId(null);
          setSelectedEndpoint(null);
          setSelectedQ(null);
          setSelectedRouteShape(null);
        } else {
          setRightCollapsed(!rightCollapsed);
        }
      },
      selectedNode,
      selectedEndpoint,
      selectedQ,
      hopIndex,
      onSetHop: setHopIndex,
      selectedRouteShape,
      onApiAccess: (endpoint) => openApiAccess(endpoint),
      onSelectNode: (id) => {
        setSelectedNodeId(id);
        setSelectedQ(null);
        setSelectedEndpoint(null);
        setSelectedRouteShape(null);
        setSelectedKind(null);
        setActivePhase(null);
      },
      onSelectEndpoint: (ep) => {
        setSelectedEndpoint(ep);
        setSelectedQ(null);
        setSelectedNodeId(null);
        setSelectedRouteShape(null);
        setSelectedKind(null);
        setActivePhase(null);
      },
      onSelectShape: (shape) => {
        // Toggle: clicking the same shape clears it
        setSelectedRouteShape(prev =>
          prev && prev.path.join(">") === shape.path.join(">") ? null : shape
        );
        setSelectedQ(null);
        setActivePhase(null);
      },
      onSelectQ: (q) => {
        setSelectedQ(q);
        setHopIndex(0);
        setSelectedEndpoint(null);
        setSelectedRouteShape(null);
        setActivePhase(null);
      },
    }),

    React.createElement(window.Footer, { stats }),

    tweaksOpen && React.createElement(TweaksPanel, {
      tweaks, setTweak,
      onClose: () => setTweaksOpen(false),
    }),

    apiAccessContext && React.createElement(ApiAccessModal, {
      context: apiAccessContext,
      onClose: closeApiAccess,
    })
  );
}

// ── API access modal — placeholder, no live submission ─────────
// Real-looking request form wired to local state only. The submit
// handler shows a "we'll be in touch" success state without
// posting anywhere. Refine the contact flow when the user has
// thought through where these leads should land.
function ApiAccessModal({ context, onClose }) {
  const [submitted, setSubmitted] = useState(false);
  const [form, setForm] = useState({ name: "", email: "", company: "", role: "", useCase: "" });
  const update = (k) => (e) => setForm({ ...form, [k]: e.target.value });
  // Real email validation — non-empty wasn't enough.
  const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const valid =
    form.name.trim() &&
    EMAIL_RE.test(form.email.trim()) &&
    form.company.trim();
  const onSubmit = (e) => {
    e.preventDefault();
    if (!valid) return;
    // Telemetry — fire even when no backend is wired, so we know
    // someone clicked submit even pre-launch.
    try {
      if (window.CIQ_CONFIG && window.CIQ_CONFIG.track) {
        window.CIQ_CONFIG.track("api_access_submitted", {
          company: form.company,
          endpoint_context: context && context.endpoint ? context.endpoint.id : null,
        });
      }
    } catch (err) {}

    const cfg = (window.CIQ_CONFIG && window.CIQ_CONFIG.apiAccessSubmit) || {};

    // If a fallback URL is configured (e.g. Calendly), open it.
    if (cfg.fallbackUrl) {
      try { window.open(cfg.fallbackUrl, "_blank", "noopener"); } catch (err) {}
      setSubmitted(true);
      return;
    }

    // If a real endpoint is configured, POST the form. Either way,
    // flip to the local success state so the user sees confirmation.
    if (cfg.endpoint) {
      try {
        fetch(cfg.endpoint, {
          method: cfg.method || "POST",
          headers: cfg.headers || { "Content-Type": "application/json" },
          body: JSON.stringify({
            ...form,
            endpoint_context: context && context.endpoint ? context.endpoint.id : null,
          }),
        }).catch(() => {});
      } catch (err) {}
    }

    setSubmitted(true);
  };
  const endpointLine = context?.endpoint
    ? `${context.endpoint.method} ${context.endpoint.path}`
    : null;

  return React.createElement("div", {
    onClick: onClose,
    className: "api-access-backdrop",
    style: {
      position: "fixed", inset: 0, zIndex: 200,
      // Scrim works in both themes — dark navy reads as backdrop in dark,
      // dimming overlay in light mode. Using bg-0 (cream) made the
      // backdrop near-white in light mode, which lost the "modal-ness".
      background: "rgba(8, 12, 20, 0.55)",
      backdropFilter: "blur(8px)",
      WebkitBackdropFilter: "blur(8px)",
      display: "flex", alignItems: "center", justifyContent: "center",
      padding: 24,
    },
  },
    React.createElement("div", {
      onClick: (e) => e.stopPropagation(),
      className: "glass-3",
      style: {
        width: "min(440px, 100%)",
        borderRadius: 16,
        padding: 24,
        maxHeight: "85vh",
        overflowY: "auto",
        position: "relative",
      },
    },
      React.createElement("button", {
        onClick: onClose,
        className: "icon-btn",
        title: "Close",
        style: { position: "absolute", top: 12, right: 12, width: 28, height: 28 },
      }, React.createElement(window.Icons.Close, { size: 13 })),

      !submitted && React.createElement(React.Fragment, null,
        React.createElement("div", {
          style: { fontSize: 9.5, fontWeight: 700, letterSpacing: "0.12em", color: "var(--accent)", textTransform: "uppercase", marginBottom: 6 },
        }, "Request access"),
        React.createElement("h2", { style: { fontSize: 19, fontWeight: 600, margin: "0 0 8px", color: "var(--text-1)" } }, "Get API access"),
        React.createElement("p", { style: { fontSize: 12.5, lineHeight: 1.55, color: "var(--text-3)", margin: "0 0 16px" } },
          "API access is gated. Tell us about your use case and we'll route you to the right plan."),

        endpointLine && React.createElement("div", {
          style: {
            padding: "8px 10px", borderRadius: 8,
            background: "var(--accent-tint)",
            border: "1px solid var(--glass-border-strong)",
            fontSize: 11, color: "var(--text-2)",
            marginBottom: 16,
            display: "flex", alignItems: "center", gap: 8,
          },
        },
          React.createElement("span", { style: { fontSize: 9.5, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600 } }, "For"),
          React.createElement("span", { className: "mono", style: { fontFamily: "var(--font-mono)", color: "var(--text-1)" } }, endpointLine)
        ),

        React.createElement("form", { onSubmit },
          React.createElement(ModalField, { label: "Name",     value: form.name,    onChange: update("name"),    placeholder: "Sarah Lee" }),
          React.createElement(ModalField, { label: "Work email",value: form.email,   onChange: update("email"),   placeholder: "sarah@example.com", type: "email" }),
          React.createElement(ModalField, { label: "Company",  value: form.company, onChange: update("company"), placeholder: "Acme Therapeutics" }),
          React.createElement(ModalField, { label: "Role",     value: form.role,    onChange: update("role"),    placeholder: "Head of External Innovation" }),
          React.createElement(ModalField, { label: "Use case", value: form.useCase, onChange: update("useCase"), placeholder: "Competitive intel for our oncology pipeline", textarea: true }),

          React.createElement("button", {
            type: "submit",
            disabled: !valid,
            style: {
              width: "100%",
              marginTop: 8,
              padding: "11px 14px",
              borderRadius: 10,
              background: valid ? "var(--accent-bright)" : "var(--bg-2)",
              color: valid ? "var(--text-on-accent)" : "var(--text-3)",
              fontWeight: 600, fontSize: 12.5,
              border: valid
                ? "1px solid color-mix(in oklab, var(--accent-bright) 80%, transparent)"
                : "1px solid var(--hairline)",
              boxShadow: valid
                ? "0 6px 18px color-mix(in oklab, var(--accent-bright) 30%, transparent), inset 0 1px 0 rgba(255,255,255,0.35)"
                : "none",
              cursor: valid ? "pointer" : "not-allowed",
              transition: "all 150ms",
            },
          }, "Request credentials"),

          React.createElement("div", {
            style: { marginTop: 12, fontSize: 10.5, color: "var(--text-4)", lineHeight: 1.5, textAlign: "center" },
          }, "We typically respond within 1 business day.")
        )
      ),

      submitted && React.createElement("div", { style: { textAlign: "center", padding: "20px 0 8px" } },
        React.createElement("div", {
          style: {
            width: 56, height: 56, margin: "0 auto 16px",
            borderRadius: 16,
            background: "color-mix(in oklab, var(--accent) 18%, transparent)",
            border: "1px solid var(--glass-border-strong)",
            display: "flex", alignItems: "center", justifyContent: "center",
            color: "var(--accent-bright, var(--accent))",
            fontSize: 24,
          },
        }, "✓"),
        React.createElement("h2", { style: { fontSize: 17, fontWeight: 600, margin: "0 0 8px", color: "var(--text-1)" } }, "Thanks — we'll be in touch."),
        React.createElement("p", { style: { fontSize: 12.5, lineHeight: 1.55, color: "var(--text-3)", margin: "0 auto", maxWidth: 320 } },
          "Your request is in our queue. Expect a reply within one business day with next steps and a calendar link."),
        React.createElement("button", {
          type: "button", onClick: onClose,
          style: {
            marginTop: 20, padding: "9px 22px", borderRadius: 999,
            background: "transparent", color: "var(--text-2)",
            border: "1px solid var(--hairline-strong)",
            fontSize: 12, fontWeight: 500, cursor: "pointer",
          },
        }, "Close")
      )
    )
  );
}

function ModalField({ label, value, onChange, placeholder, type = "text", textarea = false }) {
  const inputStyle = {
    width: "100%",
    padding: "9px 11px",
    borderRadius: 8,
    background: "var(--glass-ground)",
    border: "1px solid var(--hairline-strong)",
    color: "var(--text-1)",
    fontSize: 12.5,
    fontFamily: "var(--font-sans)",
    outline: "none",
    transition: "border-color 120ms",
    resize: "vertical",
    minHeight: textarea ? 64 : "auto",
  };
  return React.createElement("label", { style: { display: "block", marginBottom: 12 } },
    React.createElement("div", {
      style: { fontSize: 10, fontWeight: 600, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 5 },
    }, label),
    textarea
      ? React.createElement("textarea", { value, onChange, placeholder, rows: 3, style: inputStyle })
      : React.createElement("input", { type, value, onChange, placeholder, style: inputStyle })
  );
}

function TweaksPanel({ tweaks, setTweak, onClose }) {
  return React.createElement("div", {
    className: "glass-3",
    style: {
      position: "fixed",
      bottom: 64,
      right: 24,
      width: 280,
      borderRadius: 14,
      padding: 16,
      zIndex: 100,
      maxHeight: "70vh",
      overflowY: "auto",
    }
  },
    React.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 } },
      React.createElement("div", { style: { fontSize: 13, fontWeight: 600 } }, "Tweaks"),
      React.createElement("button", { className: "icon-btn", onClick: onClose, style: { width: 24, height: 24 } },
        React.createElement(window.Icons.Close, { size: 12 })
      )
    ),

    React.createElement(TweakRow, { label: "Theme" },
      React.createElement(SegBtn, {
        options: [{ k: "dark", l: "Dark" }, { k: "light", l: "Light" }],
        value: tweaks.theme, onChange: v => setTweak("theme", v),
      })
    ),
    React.createElement(TweakRow, { label: "Accent" },
      React.createElement("div", { style: { display: "flex", gap: 6 } },
        [190, 220, 145, 28, 285].map(h => React.createElement("button", {
          key: h, onClick: () => setTweak("accentHue", h),
          style: {
            width: 24, height: 24, borderRadius: 8,
            background: `oklch(78% 0.13 ${h})`,
            border: tweaks.accentHue === h ? "2px solid var(--text-1)" : "1px solid var(--hairline-strong)",
            cursor: "pointer",
          }
        }))
      )
    ),
    React.createElement(TweakRow, { label: "Glass blur" },
      React.createElement("input", {
        type: "range", min: 8, max: 40, value: tweaks.glassBlur,
        onChange: e => setTweak("glassBlur", +e.target.value),
        style: { width: "100%" },
      }),
      React.createElement("div", { style: { fontSize: 10, color: "var(--text-3)", textAlign: "right" } }, tweaks.glassBlur + "px")
    ),
    React.createElement(TweakRow, { label: "Glass saturation" },
      React.createElement("input", {
        type: "range", min: 100, max: 200, value: tweaks.glassSat,
        onChange: e => setTweak("glassSat", +e.target.value),
        style: { width: "100%" },
      }),
      React.createElement("div", { style: { fontSize: 10, color: "var(--text-3)", textAlign: "right" } }, tweaks.glassSat + "%")
    ),
    React.createElement(TweakRow, { label: "Pipeline strip" },
      React.createElement(SegBtn, {
        options: [{ k: true, l: "Show" }, { k: false, l: "Hide" }],
        value: tweaks.showPipeline, onChange: v => setTweak("showPipeline", v),
      })
    ),
    React.createElement(TweakRow, { label: "Legend" },
      React.createElement(SegBtn, {
        options: [{ k: true, l: "Show" }, { k: false, l: "Hide" }],
        value: tweaks.showLegend, onChange: v => setTweak("showLegend", v),
      })
    )
  );
}

function TweakRow({ label, children }) {
  return React.createElement("div", { style: { marginBottom: 14 } },
    React.createElement("div", { style: { fontSize: 10.5, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 6, fontWeight: 600 } }, label),
    children
  );
}

function SegBtn({ options, value, onChange }) {
  return React.createElement("div", {
    style: {
      display: "flex",
      padding: 3,
      borderRadius: 999,
      background: "oklch(0% 0 0 / 0.18)",
      border: "1px solid var(--hairline)",
      gap: 0,
    }
  }, options.map(o => React.createElement("button", {
    key: String(o.k),
    onClick: () => onChange(o.k),
    style: {
      flex: 1,
      height: 24,
      fontSize: 11,
      fontWeight: 500,
      borderRadius: 999,
      color: value === o.k ? "var(--text-1)" : "var(--text-3)",
      background: value === o.k ? "var(--bg-3)" : "transparent",
      boxShadow: value === o.k ? "0 1px 2px oklch(0% 0 0 / 0.3), inset 0 1px 0 var(--hairline-specular)" : "none",
      transition: "all 150ms",
    }
  }, o.l)));
}

// ── Error boundary ────────────────────────────────────────────
// If any descendant throws during render, show a graceful fallback
// instead of a blank page. Logs to console + window.__ciqError so
// telemetry / monitoring can pick it up.
class AppErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { error: null }; }
  static getDerivedStateFromError(error) { return { error }; }
  componentDidCatch(error, info) {
    try { console.error("[CollabIQ Explorer]", error, info); } catch (e) {}
    try { window.__ciqError = { error: String(error), stack: error && error.stack, info }; } catch (e) {}
  }
  render() {
    if (!this.state.error) return this.props.children;
    return React.createElement("div", {
      style: {
        position: "fixed", inset: 0,
        display: "flex", flexDirection: "column",
        alignItems: "center", justifyContent: "center",
        padding: 32, textAlign: "center",
        background: "var(--canvas-bg, #06101e)",
        color: "var(--text-2, #cbd5e1)",
        fontFamily: "var(--font-sans, system-ui, sans-serif)",
        gap: 14,
      },
    },
      React.createElement("div", {
        style: {
          fontSize: 9.5, fontWeight: 700, color: "var(--accent, #58efe0)",
          textTransform: "uppercase", letterSpacing: "0.12em",
        },
      }, "Something went wrong"),
      React.createElement("h1", {
        style: { fontSize: 22, fontWeight: 600, color: "var(--text-1, #e2e8f0)", margin: 0, lineHeight: 1.3, maxWidth: 480 },
      }, "We couldn't render this view."),
      React.createElement("p", {
        style: { fontSize: 13, lineHeight: 1.55, maxWidth: 460, margin: 0 },
      }, "Refreshing usually fixes it. If the problem persists, the engineering team has been notified via console logs."),
      React.createElement("button", {
        type: "button",
        onClick: () => { try { window.location.reload(); } catch (e) {} },
        style: {
          marginTop: 8,
          padding: "10px 18px",
          borderRadius: 999,
          fontSize: 12.5, fontWeight: 600,
          color: "var(--text-on-accent, #0a0a0a)",
          background: "var(--accent-bright, #00E5BF)",
          border: "1px solid color-mix(in oklab, var(--accent-bright, #00E5BF) 80%, transparent)",
          boxShadow: "0 6px 18px rgba(0, 229, 191, 0.3), inset 0 1px 0 rgba(255,255,255,0.35)",
          cursor: "pointer",
        },
      }, "Reload")
    );
  }
}

window.App = App;
ReactDOM.createRoot(document.getElementById("root")).render(
  React.createElement(AppErrorBoundary, null, React.createElement(App))
);
