/**
 * Browser reactive runtime (Point 2: reactive directives).
 *
 * Served verbatim at `/__wrnexus/reactive.js` for any page that contains a
 * `data-scope`. It is plain browser JS (no build step) and self-contained: it
 * inlines a tiny `signal()` so it has no imports to resolve.
 *
 * Supported directives (this is exactly what the `.wrn` compiler emits):
 *   data-scope="count: 0, name: 'x'"   declare reactive state on a subtree
 *   data-on-<event>="count++"          run a statement in scope on an event
 *   data-text="expr"                   element textContent follows an expression
 *   data-wrnexus-csr="id"                 target for generated CSR fetch bindings
 *   {{expr}} or {expr}                 interpolation inside text nodes
 *
 * Expressions are evaluated by a tiny parser instead of `eval`/`new Function`,
 * so production can use a strong CSP without `unsafe-eval`.
 */

export const REACTIVE_RUNTIME = String.raw`
(function () {
  var behaviorInstances = new WeakMap();
  var mountedBehaviorRoots = new Set();
  var pendingUpdateHooks = new Map();
  var updateHooksScheduled = false;
  var behaviorObserver;

  function scheduleUpdateHook(element, callback) {
    pendingUpdateHooks.set(element, callback);
    if (updateHooksScheduled) return;
    updateHooksScheduled = true;
    queueMicrotask(function () {
      updateHooksScheduled = false;
      var pending = Array.from(pendingUpdateHooks.entries());
      pendingUpdateHooks.clear();
      pending.forEach(function (entry) {
        var root = entry[0];
        var hook = entry[1];
        if (!root.isConnected) return;
        try { hook(); } catch (error) {
          console.error("[wrnexus] component update hook failed", error);
        }
      });
    });
  }

  function decodeBehaviorPayload(value) {
    var binary = window.atob(value);
    var bytes = new Uint8Array(binary.length);

    for (var index = 0; index < binary.length; index++) {
      bytes[index] = binary.charCodeAt(index);
    }

    return new TextDecoder("utf-8").decode(bytes);
  }

  function parseBehavior(element) {
    var raw = element.getAttribute(
      "data-wrn-behavior",
    );

    if (!raw) {
      return null;
    }

    try {
      var decoded =
        decodeBehaviorPayload(raw);

      var parsed = JSON.parse(decoded);

      if (
        !parsed ||
        typeof parsed !== "object"
      ) {
        return null;
      }

      return parsed;
    } catch (error) {
      console.error(
        "[wrnexus] failed to parse component behavior",
        error,
      );

      return null;
    }
  }

  function extractBehaviorFunctions(source) {
    var functions = [];
    var re = /(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g;
    var match;
    while ((match = re.exec(source || ""))) {
      var start = re.lastIndex;
      var depth = 1;
      var quote = "";
      var i = start;
      for (; i < source.length; i++) {
        var ch = source[i];
        if (quote) {
          if (ch === "\\") i++;
          else if (ch === quote) quote = "";
          continue;
        }
        if (ch === '"' || ch === "'" || ch === "\`") {
          quote = ch;
          continue;
        }
        if (ch === "{") depth++;
        else if (ch === "}" && --depth === 0) break;
      }
      if (depth !== 0) break;
      functions.push({
        name: match[1],
        args: match[2].split(",").map(function (arg) { return arg.trim(); }).filter(Boolean),
        body: source.slice(start, i),
      });
      re.lastIndex = i + 1;
    }
    return functions;
  }

  function splitStatements(input) {
    var parts = [];
    var start = 0;
    var depth = 0;
    var quote = "";
    for (var i = 0; i < input.length; i++) {
      var ch = input[i];
      if (quote) {
        if (ch === "\\") i++;
        else if (ch === quote) quote = "";
        continue;
      }
      if (ch === '"' || ch === "'" || ch === "\`") {
        quote = ch;
        continue;
      }
      if (ch === "(" || ch === "[" || ch === "{") depth++;
      else if (ch === ")" || ch === "]" || ch === "}") depth--;
      else if ((ch === ";" || ch === "\n" || ch === "\r") && depth === 0) {
        var part = input.slice(start, i).trim();
        if (part) parts.push(part);
        start = i + 1;
      }
    }
    var tail = input.slice(start).trim();
    if (tail) parts.push(tail);
    return parts;
  }

  function signal(initial) {
    var value = initial;
    var subs = new Set();
    return {
      get: function () { return value; },
      set: function (v) {
        if (Object.is(v, value)) return;
        var previous = value;
        value = v;
        subs.forEach(function (f) { f(value, previous); });
      },
      subscribe: function (f) { subs.add(f); return function () { subs.delete(f); }; }
    };
  }
  function applyReactiveAttribute(node, name, value) {
    var lowerName = String(name || "").toLowerCase();

    if (
      lowerName === "value" &&
      (
        node instanceof HTMLInputElement ||
        node instanceof HTMLTextAreaElement ||
        node instanceof HTMLSelectElement
      )
    ) {
      var nextValue =
        value == null ? "" : String(value);

      if (node.value !== nextValue) {
        node.value = nextValue;
      }

      if (value == null) {
        node.removeAttribute("value");
      } else {
        node.setAttribute(
          "value",
          nextValue,
        );
      }

      return;
    }

    if (
      lowerName === "checked" &&
      node instanceof HTMLInputElement
    ) {
      var checked = !!value;

      if (node.checked !== checked) {
        node.checked = checked;
      }

      if (checked) {
        node.setAttribute(
          "checked",
          "",
        );
      } else {
        node.removeAttribute(
          "checked",
        );
      }

      return;
    }

    if (
      lowerName === "selected" &&
      node instanceof HTMLOptionElement
    ) {
      var selected = !!value;

      node.selected = selected;

      if (selected) {
        node.setAttribute(
          "selected",
          "",
        );
      } else {
        node.removeAttribute(
          "selected",
        );
      }

      return;
    }

    if (
      lowerName === "disabled" ||
      lowerName === "required" ||
      lowerName === "readonly" ||
      lowerName === "multiple"
    ) {
      var enabled = !!value;

      if (lowerName in node) {
        try {
          node[lowerName] = enabled;
        } catch (_) {
          // Fall through to attribute handling.
        }
      }

      if (enabled) {
        node.setAttribute(
          name,
          "",
        );
      } else {
        node.removeAttribute(name);
      }

      return;
    }

    if (value === false || value == null) {
      node.removeAttribute(name);
    } else {
      node.setAttribute(
        name,
        value === true ? "" : String(value),
      );
    }
  }

  function setupScope(el) {
    if (el.__wrnexusScope) return; // idempotent: safe to call again after an HMR morph
    el.__wrnexusScope = true;
    el.__wrnexusHydrated = true; // marks the subtree as client-owned for the HMR morph
    var decl = el.getAttribute("data-scope") || "";
    var initial = parseScopeDecl(decl);

    var signals = {};
    Object.keys(initial).forEach(function (k) { signals[k] = signal(initial[k]); });

    var renderers = [];
    // Dependency-tracked rendering: while a renderer runs, every signal it reads
    // subscribes THAT renderer (not a blanket "re-render everything"). A signal
    // change then re-runs only the renderers that actually read it. The Set in
    // signal.subscribe dedupes, so re-subscribing each run is cheap and bounded.
    var currentRenderer = null;
    function reactive(fn) {
      function run() {
        var prev = currentRenderer;
        currentRenderer = run;
        try { fn(); } finally { currentRenderer = prev; }
      }
      renderers.push(run);
      return run;
    }
    function renderAll() { renderers.forEach(function (f) { f(); }); }

    var behaviorFunctions = {};
    var stateWatchers = {};
    var anyStateListeners = new Set();
    var cleanupCallbacks = [];
    var disposed = false;

    function readGlobal(name) {
      if (name === "window") return window;
      if (name === "document") return document;
      if (name === "console") return console;
      if (name === "Math") return Math;
      if (name === "JSON") return JSON;
      if (name === "Date") return Date;
      if (name === "URL") return URL;
      if (name === "location") return window.location;
      if (name === "history") return window.history;
      if (name === "navigator") return window.navigator;
      if (name === "setTimeout") return window.setTimeout.bind(window);
      if (name === "clearTimeout") return window.clearTimeout.bind(window);
      if (name === "requestAnimationFrame") return window.requestAnimationFrame.bind(window);
      if (name === "cancelAnimationFrame") return window.cancelAnimationFrame.bind(window);
      return undefined;
    }

    function readScope(name) {
      var sig = signals[name];
      if (sig) {
        if (currentRenderer) sig.subscribe(currentRenderer);
        return sig.get();
      }
      if (Object.prototype.hasOwnProperty.call(behaviorFunctions, name)) {
        return behaviorFunctions[name];
      }
      return readGlobal(name);
    }

    function peekScope(name) {
      if (signals[name]) return signals[name].get();
      if (Object.prototype.hasOwnProperty.call(behaviorFunctions, name)) {
        return behaviorFunctions[name];
      }
      return readGlobal(name);
    }

    function notifyState(name, value, previous) {
      var watchers = stateWatchers[name];
      if (watchers) {
        watchers.slice().forEach(function (watcher) {
          try { watcher(value, previous); } catch (error) {
            console.error("[wrnexus] watcher for '" + name + "' failed", error);
          }
        });
      }
      anyStateListeners.forEach(function (listener) {
        try { listener(name, value, previous); } catch (error) {
          console.error("[wrnexus] state subscriber failed", error);
        }
      });
    }

    function writeScope(name, value) {
      if (!signals[name]) {
        signals[name] = signal(value);
        renderAll();
        notifyState(name, value, undefined);
        return;
      }
      var previous = signals[name].get();
      if (Object.is(previous, value)) return;
      signals[name].set(value);
      notifyState(name, value, previous);
    }

    function evalExpr(expr, locals) {
      return evaluateExpression(expr, function (name) {
        if (locals && Object.prototype.hasOwnProperty.call(locals, name)) {
          return locals[name];
        }
        return readScope(name);
      });
    }

    function runStmt(stmt, locals) {
      splitStatements(stmt).forEach(function (part) {
        runStatement(
          part,
          function (expression) { return evalExpr(expression, locals); },
          function (name) {
            if (locals && Object.prototype.hasOwnProperty.call(locals, name)) return locals[name];
            return peekScope(name);
          },
          function (name, value) {
            if (locals && Object.prototype.hasOwnProperty.call(locals, name)) {
              locals[name] = value;
            } else {
              writeScope(name, value);
            }
          },
        );
      });
    }

    function installBehaviorFunctions(source) {
      extractBehaviorFunctions(source || "").forEach(function (definition) {
        behaviorFunctions[definition.name] = function () {
          var locals = {};
          for (var i = 0; i < definition.args.length; i++) {
            locals[definition.args[i]] = arguments[i];
          }
          runStmt(definition.body, locals);
        };
      });
    }

    // A binding belongs to THIS scope only when el is the node's nearest
    // [data-scope] ancestor. Otherwise a nested scope owns it and we skip it,
    // so an outer scope never clobbers an inner one's values.
    function owns(node) {
      var host = node.nodeType === 1 ? node : node.parentNode;
      return !!host && host.closest && host.closest("[data-scope]") === el;
    }

    // --- data-for list rendering -------------------------------------------
    // Each [data-for="item in list"] element is a per-item template. On any
    // change to the list (or a dependency an item reads), the list re-renders.
    function parseFor(value) {
      var m = /^\s*([A-Za-z_$][\w$]*)\s*(?:,\s*([A-Za-z_$][\w$]*)\s*)?\s+in\s+([\s\S]+?)\s*$/.exec(
        value || "",
      );
      return m ? { item: m[1], index: m[2], list: m[3] } : null;
    }
    function fillMustache(str, itemEval) {
      return str.replace(/\{\{\s*([^}]+?)\s*\}\}|\{([^{}]+)\}/g, function (_, d, s) {
        var e = (d || s).trim();
        try { return String(itemEval(e)); } catch (err) { return ""; }
      });
    }
    function hydrateItem(root, locals) {
      function localRead(name) {
        return Object.prototype.hasOwnProperty.call(locals, name) ? locals[name] : peekScope(name);
      }
      function itemEval(expr) { return evaluateExpression(expr, localRead); }

      var els = [root];
      if (root.querySelectorAll) Array.prototype.push.apply(els, root.querySelectorAll("*"));
      els.forEach(function (n) {
        if (n.nodeType !== 1) return;
        Array.prototype.slice.call(n.attributes).forEach(function (attr) {
          if (attr.name === "data-text") {
            try { n.textContent = String(itemEval(attr.value)); } catch (e) { /* ignore */ }
          } else if (attr.name.indexOf("data-on-") === 0) {
            var evt = attr.name.slice("data-on-".length);
            var stmt = attr.value;
            n.addEventListener(evt, function () {
              try {
                splitTopLevel(stmt, ";").forEach(function (part) {
                  runStatement(part, itemEval, localRead, function (name, value) {
                    if (Object.prototype.hasOwnProperty.call(locals, name)) locals[name] = value;
                    else if (!signals[name]) { signals[name] = signal(value); renderAll(); }
                    else signals[name].set(value);
                  });
                });
              } catch (e) { console.error("[wrnexus] data-for handler error", e); }
            });
          } else if (attr.value.indexOf("{") !== -1) {
            attr.value = fillMustache(attr.value, itemEval);
          }
        });
      });

      var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
      var tn;
      while ((tn = walker.nextNode())) {
        if (tn.nodeValue.indexOf("{") === -1) continue;
        tn.nodeValue = fillMustache(tn.nodeValue, itemEval);
      }
    }

    Array.prototype.slice.call(el.querySelectorAll("[data-for]")).forEach(function (tpl) {
      if (!tpl.parentNode || !owns(tpl)) return;
      var spec = parseFor(tpl.getAttribute("data-for"));
      if (!spec) return;
      tpl.removeAttribute("data-for");
      var parent = tpl.parentNode;
      var marker = document.createComment("wire-for");
      parent.insertBefore(marker, tpl);
      parent.removeChild(tpl);
      var clones = [];
      reactive(function () {
        var list = evalExpr(spec.list);
        if (!list || typeof list.length !== "number") list = [];
        for (var c = 0; c < clones.length; c++) {
          if (clones[c].parentNode) clones[c].parentNode.removeChild(clones[c]);
        }
        clones = [];
        var frag = document.createDocumentFragment();
        for (var i = 0; i < list.length; i++) {
          var clone = tpl.cloneNode(true);
          var locals = {};
          locals[spec.item] = list[i];
          if (spec.index) locals[spec.index] = i;
          hydrateItem(clone, locals);
          frag.appendChild(clone);
          clones.push(clone);
        }
        parent.insertBefore(frag, marker.nextSibling);
      });
    });

    // data-text bindings
    el.querySelectorAll("[data-text]").forEach(function (node) {
      if (!owns(node)) return;
      var expr = node.getAttribute("data-text");
      reactive(function () {
        try { node.textContent = String(evalExpr(expr)); } catch (e) { /* ignore */ }
      });
    });

    // data-show="expr" — toggle visibility on truthiness.
    el.querySelectorAll("[data-show]").forEach(function (node) {
      if (!owns(node)) return;
      var showExpr = node.getAttribute("data-show");
      reactive(function () {
        var visible = true;
        try { visible = !!evalExpr(showExpr); } catch (e) { /* keep visible */ }
        node.style.display = visible ? "" : "none";
      });
    });

    // Conditional class bindings emitted as:
    // data-wrn-class-*='["class-name","expression"]'
    var classBindNodes = [el].concat(
      Array.prototype.slice.call(el.querySelectorAll("*")),
    );

    classBindNodes.forEach(function (node) {
      if (!owns(node)) return;

      Array.prototype.slice.call(node.attributes).forEach(function (marker) {
        if (marker.name.indexOf("data-wrn-class-") !== 0) return;

        var binding;

        try {
          binding = JSON.parse(marker.value);
        } catch (e) {
          return;
        }

        if (!binding || binding.length !== 2) return;

        var className = binding[0];
        var expression = binding[1];

        reactive(function () {
          var enabled = false;

          try {
            enabled = !!evalExpr(expression);
          } catch (e) {
            enabled = false;
          }

          node.classList.toggle(className, enabled);
        });
      });
    });

    // Reactive ordinary attributes emitted by the compiler. Each marker stores
    // [attributeName, originalTemplate], preserving an SSR value while allowing
    // state changes to update type, aria-*, class, href, and other attributes.
    var bindNodes = [el].concat(Array.prototype.slice.call(el.querySelectorAll("*")));
    bindNodes.forEach(function (node) {
      if (!owns(node)) return;
      Array.prototype.slice.call(node.attributes).forEach(function (marker) {
        if (marker.name.indexOf("data-wrn-bind-") !== 0) return;
        var binding;
        try { binding = JSON.parse(marker.value); } catch (e) { return; }
        if (!binding || binding.length !== 2) return;
        var name = binding[0];
        var template = binding[1];
        reactive(function () {
          var exact = /^\{([^{}]+)\}$/.exec(template);
          var raw;
          if (exact) {
            try { raw = evalExpr(exact[1].trim()); } catch (e) { return; }
          } else {
            raw = template.replace(/\{([^{}]+)\}/g, function (_, expr) {
              try {
                var value = evalExpr(expr.trim());
                return value == null ? "" : String(value);
              } catch (e) { return ""; }
            });
          }
          applyReactiveAttribute(node, name, raw);
        });
      });
    });

    // {{expr}} / {expr} interpolation in text nodes
    var walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null);
    var textNode;
    while ((textNode = walker.nextNode())) {
      var template = textNode.nodeValue;
      if (template.indexOf("{") === -1) continue;
      if (!owns(textNode)) continue;
      (function (node, tpl) {
        reactive(function () {
          node.nodeValue = tpl.replace(/\{\{\s*([^}]+?)\s*\}\}|\{([^{}]+)\}/g, function (_, doubleExpr, singleExpr) {
            var expr = doubleExpr || singleExpr;
            try { return String(evalExpr(expr.trim())); } catch (err) { return ""; }
          });
        });
      })(textNode, template);
    }

    // Event handlers on elements, window, and document.
    var nodes = [el].concat(Array.prototype.slice.call(el.querySelectorAll("*")));
    nodes.forEach(function (node) {
      if (!owns(node)) return;
      Array.prototype.slice.call(node.attributes).forEach(function (attr) {
        if (attr.name.indexOf("data-on-") !== 0) return;

        var rawName = attr.name.slice("data-on-".length);
        var target = node;
        var evt = rawName;

        if (rawName.indexOf("window-") === 0) {
          target = window;
          evt = rawName.slice("window-".length);
        } else if (rawName.indexOf("document-") === 0) {
          target = document;
          evt = rawName.slice("document-".length);
        }

        var stmt = attr.value;
        var listener = function (event) {
          try { runStmt(stmt, { event: event, $event: event }); } catch (error) {
            console.error("[wrnexus] handler error in '" + stmt + "'", error);
          }
        };

        var options =
          target === window && (evt === "scroll" || evt === "touchstart" || evt === "touchmove")
            ? { passive: true }
            : undefined;

        target.addEventListener(evt, listener, options);
        cleanupCallbacks.push(function () {
          target.removeEventListener(evt, listener, options);
        });
      });
    });

    var behavior = parseBehavior(el);
    if (behavior) {
      installBehaviorFunctions(behavior.functions);

      (behavior.watches || []).forEach(function (watch) {
        if (!watch || typeof watch.state !== "string" || typeof watch.body !== "string") return;
        if (!stateWatchers[watch.state]) stateWatchers[watch.state] = [];
        stateWatchers[watch.state].push(function (value, previous) {
          runStmt(watch.body, { value: value, previous: previous });
        });
      });

      var updateSource =
        behavior.lifecycle && typeof behavior.lifecycle.update === "string"
          ? behavior.lifecycle.update.trim()
          : "";

      if (updateSource) {
        anyStateListeners.add(function () {
          scheduleUpdateHook(el, function () {
            if (!disposed) runStmt(updateSource);
          });
        });
      }

      var mountSource =
        behavior.lifecycle && typeof behavior.lifecycle.mount === "string"
          ? behavior.lifecycle.mount.trim()
          : "";

      var unmountSource =
        behavior.lifecycle && typeof behavior.lifecycle.unmount === "string"
          ? behavior.lifecycle.unmount.trim()
          : "";

      var instance = {
        dispose: function () {
          if (disposed) return;
          disposed = true;
          cleanupCallbacks.splice(0).reverse().forEach(function (cleanup) {
            try { cleanup(); } catch (_) { /* ignore cleanup errors */ }
          });
          pendingUpdateHooks.delete(el);
          if (unmountSource) {
            try { runStmt(unmountSource); } catch (error) {
              console.error("[wrnexus] component unmount hook failed", error);
            }
          }
          mountedBehaviorRoots.delete(el);
          behaviorInstances.delete(el);
          el.__wrnexusScope = false;
        },
      };

      behaviorInstances.set(el, instance);
      mountedBehaviorRoots.add(el);

      if (mountSource) {
        queueMicrotask(function () {
          if (!disposed && el.isConnected) {
            try { runStmt(mountSource); } catch (error) {
              console.error("[wrnexus] component mount hook failed", error);
            }
          }
        });
      }
    }

    renderAll();
  }

  function disposeBehaviors(root) {
    var elements = [];
    if (root && root.nodeType === 1 && root.hasAttribute("data-wrn-behavior")) {
      elements.push(root);
    }
    if (root && root.querySelectorAll) {
      Array.prototype.push.apply(elements, root.querySelectorAll("[data-wrn-behavior]"));
    }
    elements.reverse().forEach(function (element) {
      var instance = behaviorInstances.get(element);
      if (instance) instance.dispose();
    });
  }

  function ensureBehaviorObserver() {
    if (behaviorObserver || typeof MutationObserver === "undefined") return;
    behaviorObserver = new MutationObserver(function (records) {
      records.forEach(function (record) {
        Array.prototype.forEach.call(record.removedNodes, function (node) {
          if (node && node.nodeType === 1) disposeBehaviors(node);
        });
      });
    });
    behaviorObserver.observe(document.documentElement, { childList: true, subtree: true });
  }

  function hydrateScopes(root) {
    var host = root || document;
    if (host.nodeType === 1 && host.matches && host.matches("[data-scope]")) setupScope(host);
    if (host.querySelectorAll) host.querySelectorAll("[data-scope]").forEach(setupScope);
    ensureBehaviorObserver();
  }

  function parseScopeDecl(decl) {
    var initial = {};
    splitTopLevel(decl, ",").forEach(function (part) {
      var idx = findTopLevel(part, ":");
      if (idx < 0) return;
      var name = part.slice(0, idx).trim();
      var expr = part.slice(idx + 1).trim();
      if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) return;
      try {
        initial[name] = evaluateExpression(expr, function () { return undefined; });
      } catch (e) {
        console.error("[wrnexus] invalid data-scope value for '" + name + "'", e);
      }
    });
    return initial;
  }

  function runStatement(stmt, evalExpr, read, write) {
    stmt = String(stmt || "").trim();
    if (!stmt) return;

    var inc = stmt.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\s*(\+\+|--)$/);
    if (inc) {
      write(inc[1], Number(read(inc[1]) || 0) + (inc[2] === "++" ? 1 : -1));
      return;
    }

    var assign = stmt.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\s*(\+=|-=|\*=|\/=|%=|=)\s*([\s\S]+)$/);
    if (!assign) {
      evalExpr(stmt); // bare expression statement (e.g. a method/function call)
      return;
    }

    var name = assign[1];
    var op = assign[2];
    var next = evalExpr(assign[3]);
    var current = read(name);
    if (op === "+=") next = current + next;
    else if (op === "-=") next = Number(current || 0) - Number(next || 0);
    else if (op === "*=") next = Number(current || 0) * Number(next || 0);
    else if (op === "/=") next = Number(current || 0) / Number(next || 0);
    else if (op === "%=") next = Number(current || 0) % Number(next || 0);
    write(name, next);
  }

  // A small, eval-free expression evaluator (so a strict CSP needs no
  // 'unsafe-eval'). Supports: literals, identifiers, member access (a.b, a[b]),
  // function/method calls, arrays, objects, arithmetic, comparison, equality,
  // logical (&& ||), unary (! - +), and the ternary operator.
  function evaluateExpression(expr, read) {
    var tokens = tokenizeExpression(String(expr || ""));
    var index = 0;
    function peek() { return tokens[index]; }
    function next() { return tokens[index++]; }
    function is(v) { return peek() && peek().value === v; }
    function match(v) { if (is(v)) { index++; return true; } return false; }
    function expect(v) { if (!match(v)) throw new Error("Expected '" + v + "'"); }

    function parsePrimary() {
      var t = next();
      if (!t) throw new Error("Unexpected end of expression");
      if (t.type === "number" || t.type === "string") return { value: t.value };
      if (t.type === "ident") {
        if (t.value === "true") return { value: true };
        if (t.value === "false") return { value: false };
        if (t.value === "null") return { value: null };
        if (t.value === "undefined") return { value: undefined };
        return { value: read(t.value) };
      }
      if (t.value === "(") { var v = parseTernary(); expect(")"); return { value: v }; }
      if (t.value === "[") {
        var arr = [];
        if (!is("]")) { arr.push(parseTernary()); while (match(",")) arr.push(parseTernary()); }
        expect("]");
        return { value: arr };
      }
      if (t.value === "{") {
        var obj = {};
        if (!is("}")) {
          do {
            var kt = next();
            var key = kt.value;
            expect(":");
            obj[key] = parseTernary();
          } while (match(","));
        }
        expect("}");
        return { value: obj };
      }
      throw new Error("Unexpected token '" + t.value + "'");
    }

    function parsePostfix() {
      var node = parsePrimary();
      for (;;) {
        if (match(".")) {
          var prop = next().value;
          node = { value: node.value == null ? undefined : node.value[prop], obj: node.value };
        } else if (match("[")) {
          var key = parseTernary();
          expect("]");
          node = { value: node.value == null ? undefined : node.value[key], obj: node.value };
        } else if (is("(")) {
          next();
          var args = [];
          if (!is(")")) { args.push(parseTernary()); while (match(",")) args.push(parseTernary()); }
          expect(")");
          var fn = node.value;
          node = { value: typeof fn === "function" ? fn.apply(node.obj, args) : undefined };
        } else break;
      }
      return node;
    }

    function parseUnary() {
      if (match("!")) return !parseUnary();
      if (match("-")) return -parseUnary();
      if (match("+")) return +parseUnary();
      return parsePostfix().value;
    }
    function parseMul() {
      var l = parseUnary();
      while (peek() && (is("*") || is("/") || is("%"))) {
        var op = next().value, r = parseUnary();
        l = op === "*" ? l * r : op === "/" ? l / r : l % r;
      }
      return l;
    }
    function parseAdd() {
      var l = parseMul();
      while (peek() && (is("+") || is("-"))) {
        var op = next().value, r = parseMul();
        l = op === "+" ? l + r : l - r;
      }
      return l;
    }
    function parseCmp() {
      var l = parseAdd();
      while (peek() && (is("<") || is(">") || is("<=") || is(">="))) {
        var op = next().value, r = parseAdd();
        l = op === "<" ? l < r : op === ">" ? l > r : op === "<=" ? l <= r : l >= r;
      }
      return l;
    }
    function parseEq() {
      var l = parseCmp();
      while (peek() && (is("==") || is("!=") || is("===") || is("!=="))) {
        var op = next().value, r = parseCmp();
        l = op === "==" ? l == r : op === "!=" ? l != r : op === "===" ? l === r : l !== r;
      }
      return l;
    }
    function parseAnd() {
      var l = parseEq();
      while (match("&&")) l = l && parseEq();
      return l;
    }
    function parseOr() {
      var l = parseAnd();
      while (match("||")) l = l || parseAnd();
      return l;
    }
    function parseTernary() {
      var c = parseOr();
      if (match("?")) { var a = parseTernary(); expect(":"); var b = parseTernary(); return c ? a : b; }
      return c;
    }

    var value = parseTernary();
    if (index < tokens.length) throw new Error("Unexpected token '" + tokens[index].value + "'");
    return value;
  }

  function tokenizeExpression(input) {
    var tokens = [];
    var i = 0;
    while (i < input.length) {
      var ch = input[i];
      if (/\s/.test(ch)) { i++; continue; }
      if (/[0-9]/.test(ch) || (ch === "." && /[0-9]/.test(input[i + 1]))) {
        var start = i++;
        while (i < input.length && /[0-9.]/.test(input[i])) i++;
        tokens.push({ type: "number", value: Number(input.slice(start, i)) });
        continue;
      }
      if (ch === '"' || ch === "'") {
        var quote = ch, value = "";
        i++;
        while (i < input.length) {
          ch = input[i++];
          if (ch === quote) break;
          if (ch === "\\") {
            var esc = input[i++];
            value += esc === "n" ? "\n" : esc === "t" ? "\t" : esc || "";
          } else value += ch;
        }
        tokens.push({ type: "string", value: value });
        continue;
      }
      if (/[A-Za-z_$]/.test(ch)) {
        var s = i++;
        while (i < input.length && /[A-Za-z0-9_$]/.test(input[i])) i++;
        tokens.push({ type: "ident", value: input.slice(s, i) });
        continue;
      }
      var three = input.substr(i, 3);
      if (three === "===" || three === "!==") { tokens.push({ type: "op", value: three }); i += 3; continue; }
      var two = input.substr(i, 2);
      if (["==", "!=", "<=", ">=", "&&", "||"].indexOf(two) !== -1) {
        tokens.push({ type: "op", value: two });
        i += 2;
        continue;
      }
      if ("()+-*/%!<>.,?:[]{}".indexOf(ch) !== -1) { tokens.push({ type: "op", value: ch }); i++; continue; }
      throw new Error("Unexpected character '" + ch + "'");
    }
    return tokens;
  }

  function splitTopLevel(input, separator) {
    var parts = [];
    var start = 0;
    var depth = 0;
    var quote = "";
    for (var i = 0; i < input.length; i++) {
      var ch = input[i];
      if (quote) {
        if (ch === "\\") i++;
        else if (ch === quote) quote = "";
        continue;
      }
      if (ch === '"' || ch === "'") {
        quote = ch;
        continue;
      }
      if (ch === "(" || ch === "[" || ch === "{") depth++;
      else if (ch === ")" || ch === "]" || ch === "}") depth--;
      else if (ch === separator && depth === 0) {
        parts.push(input.slice(start, i).trim());
        start = i + 1;
      }
    }
    parts.push(input.slice(start).trim());
    return parts.filter(Boolean);
  }

  function findTopLevel(input, needle) {
    var depth = 0;
    var quote = "";
    for (var i = 0; i < input.length; i++) {
      var ch = input[i];
      if (quote) {
        if (ch === "\\") i++;
        else if (ch === quote) quote = "";
        continue;
      }
      if (ch === '"' || ch === "'") quote = ch;
      else if (ch === "(" || ch === "[" || ch === "{") depth++;
      else if (ch === ")" || ch === "]" || ch === "}") depth--;
      else if (ch === needle && depth === 0) return i;
    }
    return -1;
  }

  function safeCsrUrl(id) {
    try {
      if (!/^[A-Za-z0-9_-]+$/.test(id)) return null;
      return "/__wrnexus/csr?route=" + encodeURIComponent(location.pathname) + "&id=" + encodeURIComponent(id);
    } catch (_) {
      return null;
    }
  }

  function setupCsrFetch(el) {
    if (el.__wrnexusCsrFetch) return;
    el.__wrnexusCsrFetch = true;

    var id = el.getAttribute("data-wrnexus-csr") || "";
    var url = safeCsrUrl(id);
    if (!url) {
      console.error("[wrnexus] blocked unsafe CSR binding '" + id + "'");
      return;
    }

    var headers = { "accept": "text/plain" };
    var storage = localStorageSnapshotHeader();
    if (storage) headers["x-wrnexus-local-storage"] = storage;

    fetch(url, { headers: headers })
      .then(function (res) {
        if (!res.ok) throw new Error("HTTP " + res.status);
        return res.text();
      })
      .then(function (text) {
        el.textContent = text;
      })
      .catch(function (err) {
        console.error("[wrnexus] CSR fetch failed for binding '" + id + "'", err);
        el.textContent = "Failed to load";
      });
  }

  function hydrateCsrFetches(root) {
    (root || document).querySelectorAll("[data-wrnexus-csr]").forEach(setupCsrFetch);
  }

  function localStorageSnapshotHeader() {
    try {
      if (!("localStorage" in window)) return "";
      var values = {};
      for (var i = 0; i < window.localStorage.length; i++) {
        var key = window.localStorage.key(i);
        if (!key) continue;
        var value = window.localStorage.getItem(key);
        if (typeof value === "string") values[key] = value;
      }
      var encoded = encodeURIComponent(JSON.stringify(values));
      return encoded.length <= 12000 ? encoded : "";
    } catch (_) {
      return "";
    }
  }

  window.__wrnexusHydrateScopes = hydrateScopes;
  window.__wrnexusHydrateCsrFetches = hydrateCsrFetches;
  window.__wrnexusDisposeBehaviors = disposeBehaviors;
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", function () {
      hydrateScopes(document);
      hydrateCsrFetches(document);
    });
  } else {
    hydrateScopes(document);
    hydrateCsrFetches(document);
  }
})();
`.trim();
