Index: frontend/node_modules/react-router-dom/server.js
===================================================================
--- frontend/node_modules/react-router-dom/server.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/react-router-dom/server.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,322 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+var React = require('react');
+var router = require('@remix-run/router');
+var reactRouter = require('react-router');
+var reactRouterDom = require('react-router-dom');
+
+function _interopNamespace(e) {
+    if (e && e.__esModule) return e;
+    var n = Object.create(null);
+    if (e) {
+        Object.keys(e).forEach(function (k) {
+            if (k !== 'default') {
+                var d = Object.getOwnPropertyDescriptor(e, k);
+                Object.defineProperty(n, k, d.get ? d : {
+                    enumerable: true,
+                    get: function () { return e[k]; }
+                });
+            }
+        });
+    }
+    n["default"] = e;
+    return Object.freeze(n);
+}
+
+var React__namespace = /*#__PURE__*/_interopNamespace(React);
+
+/**
+ * A `<Router>` that may not navigate to any other location. This is useful
+ * on the server where there is no stateful UI.
+ */
+function StaticRouter({
+  basename,
+  children,
+  location: locationProp = "/",
+  future
+}) {
+  if (typeof locationProp === "string") {
+    locationProp = reactRouterDom.parsePath(locationProp);
+  }
+  let action = router.Action.Pop;
+  let location = {
+    pathname: locationProp.pathname || "/",
+    search: locationProp.search || "",
+    hash: locationProp.hash || "",
+    state: locationProp.state != null ? locationProp.state : null,
+    key: locationProp.key || "default"
+  };
+  let staticNavigator = getStatelessNavigator();
+  return /*#__PURE__*/React__namespace.createElement(reactRouterDom.Router, {
+    basename: basename,
+    children: children,
+    location: location,
+    navigationType: action,
+    navigator: staticNavigator,
+    future: future,
+    static: true
+  });
+}
+/**
+ * A Data Router that may not navigate to any other location. This is useful
+ * on the server where there is no stateful UI.
+ */
+function StaticRouterProvider({
+  context,
+  router: router$1,
+  hydrate = true,
+  nonce
+}) {
+  !(router$1 && context) ? process.env.NODE_ENV !== "production" ? router.UNSAFE_invariant(false, "You must provide `router` and `context` to <StaticRouterProvider>") : router.UNSAFE_invariant(false) : void 0;
+  let dataRouterContext = {
+    router: router$1,
+    navigator: getStatelessNavigator(),
+    static: true,
+    staticContext: context,
+    basename: context.basename || "/"
+  };
+  let fetchersContext = new Map();
+  let hydrateScript = "";
+  if (hydrate !== false) {
+    let data = {
+      loaderData: context.loaderData,
+      actionData: context.actionData,
+      errors: serializeErrors(context.errors)
+    };
+    // Use JSON.parse here instead of embedding a raw JS object here to speed
+    // up parsing on the client.  Dual-stringify is needed to ensure all quotes
+    // are properly escaped in the resulting string.  See:
+    //   https://v8.dev/blog/cost-of-javascript-2019#json
+    let json = htmlEscape(JSON.stringify(JSON.stringify(data)));
+    hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${json});`;
+  }
+  let {
+    state
+  } = dataRouterContext.router;
+  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(reactRouterDom.UNSAFE_DataRouterContext.Provider, {
+    value: dataRouterContext
+  }, /*#__PURE__*/React__namespace.createElement(reactRouterDom.UNSAFE_DataRouterStateContext.Provider, {
+    value: state
+  }, /*#__PURE__*/React__namespace.createElement(reactRouterDom.UNSAFE_FetchersContext.Provider, {
+    value: fetchersContext
+  }, /*#__PURE__*/React__namespace.createElement(reactRouterDom.UNSAFE_ViewTransitionContext.Provider, {
+    value: {
+      isTransitioning: false
+    }
+  }, /*#__PURE__*/React__namespace.createElement(reactRouterDom.Router, {
+    basename: dataRouterContext.basename,
+    location: state.location,
+    navigationType: state.historyAction,
+    navigator: dataRouterContext.navigator,
+    static: dataRouterContext.static,
+    future: {
+      v7_relativeSplatPath: router$1.future.v7_relativeSplatPath
+    }
+  }, /*#__PURE__*/React__namespace.createElement(DataRoutes, {
+    routes: router$1.routes,
+    future: router$1.future,
+    state: state
+  })))))), hydrateScript ? /*#__PURE__*/React__namespace.createElement("script", {
+    suppressHydrationWarning: true,
+    nonce: nonce,
+    dangerouslySetInnerHTML: {
+      __html: hydrateScript
+    }
+  }) : null);
+}
+function DataRoutes({
+  routes,
+  future,
+  state
+}) {
+  return reactRouter.UNSAFE_useRoutesImpl(routes, undefined, state, future);
+}
+function serializeErrors(errors) {
+  if (!errors) return null;
+  let entries = Object.entries(errors);
+  let serialized = {};
+  for (let [key, val] of entries) {
+    // Hey you!  If you change this, please change the corresponding logic in
+    // deserializeErrors in react-router-dom/index.tsx :)
+    if (router.isRouteErrorResponse(val)) {
+      serialized[key] = {
+        ...val,
+        __type: "RouteErrorResponse"
+      };
+    } else if (val instanceof Error) {
+      // Do not serialize stack traces from SSR for security reasons
+      serialized[key] = {
+        message: val.message,
+        __type: "Error",
+        // If this is a subclass (i.e., ReferenceError), send up the type so we
+        // can re-create the same type during hydration.
+        ...(val.name !== "Error" ? {
+          __subType: val.name
+        } : {})
+      };
+    } else {
+      serialized[key] = val;
+    }
+  }
+  return serialized;
+}
+function getStatelessNavigator() {
+  return {
+    createHref,
+    encodeLocation,
+    push(to) {
+      throw new Error(`You cannot use navigator.push() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${JSON.stringify(to)})\` somewhere in your app.`);
+    },
+    replace(to) {
+      throw new Error(`You cannot use navigator.replace() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` + `in your app.`);
+    },
+    go(delta) {
+      throw new Error(`You cannot use navigator.go() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${delta})\` somewhere in your app.`);
+    },
+    back() {
+      throw new Error(`You cannot use navigator.back() on the server because it is a stateless ` + `environment.`);
+    },
+    forward() {
+      throw new Error(`You cannot use navigator.forward() on the server because it is a stateless ` + `environment.`);
+    }
+  };
+}
+function createStaticHandler(routes, opts) {
+  return router.createStaticHandler(routes, {
+    ...opts,
+    mapRouteProperties: reactRouter.UNSAFE_mapRouteProperties
+  });
+}
+function createStaticRouter(routes, context, opts = {}) {
+  let manifest = {};
+  let dataRoutes = router.UNSAFE_convertRoutesToDataRoutes(routes, reactRouter.UNSAFE_mapRouteProperties, undefined, manifest);
+  // Because our context matches may be from a framework-agnostic set of
+  // routes passed to createStaticHandler(), we update them here with our
+  // newly created/enhanced data routes
+  let matches = context.matches.map(match => {
+    let route = manifest[match.route.id] || match.route;
+    return {
+      ...match,
+      route
+    };
+  });
+  let msg = method => `You cannot use router.${method}() on the server because it is a stateless environment`;
+  return {
+    get basename() {
+      return context.basename;
+    },
+    get future() {
+      return {
+        v7_fetcherPersist: false,
+        v7_normalizeFormMethod: false,
+        v7_partialHydration: opts.future?.v7_partialHydration === true,
+        v7_prependBasename: false,
+        v7_relativeSplatPath: opts.future?.v7_relativeSplatPath === true,
+        v7_skipActionErrorRevalidation: false
+      };
+    },
+    get state() {
+      return {
+        historyAction: router.Action.Pop,
+        location: context.location,
+        matches,
+        loaderData: context.loaderData,
+        actionData: context.actionData,
+        errors: context.errors,
+        initialized: true,
+        navigation: router.IDLE_NAVIGATION,
+        restoreScrollPosition: null,
+        preventScrollReset: false,
+        revalidation: "idle",
+        fetchers: new Map(),
+        blockers: new Map()
+      };
+    },
+    get routes() {
+      return dataRoutes;
+    },
+    get window() {
+      return undefined;
+    },
+    initialize() {
+      throw msg("initialize");
+    },
+    subscribe() {
+      throw msg("subscribe");
+    },
+    enableScrollRestoration() {
+      throw msg("enableScrollRestoration");
+    },
+    navigate() {
+      throw msg("navigate");
+    },
+    fetch() {
+      throw msg("fetch");
+    },
+    revalidate() {
+      throw msg("revalidate");
+    },
+    createHref,
+    encodeLocation,
+    getFetcher() {
+      return router.IDLE_FETCHER;
+    },
+    deleteFetcher() {
+      throw msg("deleteFetcher");
+    },
+    dispose() {
+      throw msg("dispose");
+    },
+    getBlocker() {
+      return router.IDLE_BLOCKER;
+    },
+    deleteBlocker() {
+      throw msg("deleteBlocker");
+    },
+    patchRoutes() {
+      throw msg("patchRoutes");
+    },
+    _internalFetchControllers: new Map(),
+    _internalActiveDeferreds: new Map(),
+    _internalSetRoutes() {
+      throw msg("_internalSetRoutes");
+    }
+  };
+}
+function createHref(to) {
+  return typeof to === "string" ? to : reactRouterDom.createPath(to);
+}
+function encodeLocation(to) {
+  let href = typeof to === "string" ? to : reactRouterDom.createPath(to);
+  // Treating this as a full URL will strip any trailing spaces so we need to
+  // pre-encode them since they might be part of a matching splat param from
+  // an ancestor route
+  href = href.replace(/ $/, "%20");
+  let encoded = ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
+  return {
+    pathname: encoded.pathname,
+    search: encoded.search,
+    hash: encoded.hash
+  };
+}
+const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
+// This utility is based on https://github.com/zertosh/htmlescape
+// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
+const ESCAPE_LOOKUP = {
+  "&": "\\u0026",
+  ">": "\\u003e",
+  "<": "\\u003c",
+  "\u2028": "\\u2028",
+  "\u2029": "\\u2029"
+};
+const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
+function htmlEscape(str) {
+  return str.replace(ESCAPE_REGEX, match => ESCAPE_LOOKUP[match]);
+}
+
+exports.StaticRouter = StaticRouter;
+exports.StaticRouterProvider = StaticRouterProvider;
+exports.createStaticHandler = createStaticHandler;
+exports.createStaticRouter = createStaticRouter;
