From 0388d31d364139d0a3999126b06d8ac850117ab9 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Tue, 19 Sep 2023 00:39:00 -0300
Subject: account page

---
 packages/demobank-ui/src/components/Routing.tsx | 109 ++++++++++++++++++++++++
 1 file changed, 109 insertions(+)
 create mode 100644 packages/demobank-ui/src/components/Routing.tsx

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
new file mode 100644
index 000000000..d5ea44e10
--- /dev/null
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -0,0 +1,109 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { createHashHistory } from "history";
+import { VNode, h } from "preact";
+import { Route, Router, route } from "preact-router";
+import { useEffect } from "preact/hooks";
+import { BankFrame } from "../pages/BankFrame.js";
+import { BusinessAccount } from "../pages/BusinessAccount.js";
+import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
+import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
+import { RegistrationPage } from "../pages/RegistrationPage.js";
+
+export function Routing(): VNode {
+  const history = createHashHistory();
+
+  return (
+    <BankFrame
+      goToBusinessAccount={() => {
+        route("/business");
+      }}
+    >
+      <Router history={history}>
+        <Route
+          path="/operation/:wopid"
+          component={({ wopid }: { wopid: string }) => (
+            <WithdrawalOperationPage
+              operationId={wopid}
+              onContinue={() => {
+                route("/account");
+              }}
+              onLoadNotOk={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/public-accounts"
+          component={() => <PublicHistoriesPage />}
+        />
+        <Route
+          path="/register"
+          component={() => (
+            <RegistrationPage
+              onComplete={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/account"
+          component={() => (
+            <HomePage
+              onPendingOperationFound={(wopid) => {
+                route(`/operation/${wopid}`);
+              }}
+              onRegister={() => {
+                route("/register");
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/business"
+          component={() => (
+            <BusinessAccount
+              onClose={() => {
+                route("/account");
+              }}
+              onRegister={() => {
+                route("/register");
+              }}
+              onLoadNotOk={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+        <Route default component={Redirect} to="/account" />
+      </Router>
+    </BankFrame>
+  );
+}
+
+function Redirect({ to }: { to: string }): VNode {
+  useEffect(() => {
+    route(to, true);
+  }, []);
+  return <div>being redirected to {to}</div>;
+}
+
+export function assertUnreachable(x: never): never {
+  throw new Error("Didn't expect to get here");
+}
-- 
cgit v1.2.3


From a5406c5a5dc437e036168eb068db11d88e05bb0f Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Tue, 19 Sep 2023 08:31:08 -0300
Subject: some ui

---
 packages/demobank-ui/build.mjs                     |     4 +-
 packages/demobank-ui/dev.mjs                       |     4 +-
 packages/demobank-ui/package.json                  |     4 +
 packages/demobank-ui/postcss.config.js             |     6 +
 packages/demobank-ui/src/components/CopyButton.tsx |   102 +-
 packages/demobank-ui/src/components/Routing.tsx    |     5 +
 .../src/components/Transactions/views.tsx          |    76 +-
 packages/demobank-ui/src/components/app.tsx        |     1 -
 packages/demobank-ui/src/hooks/access.ts           |    10 +-
 packages/demobank-ui/src/index.html                |    49 +-
 packages/demobank-ui/src/index.tsx                 |     2 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |    93 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |   266 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |   100 +-
 .../src/pages/PaytoWireTransferForm.tsx            |   581 +-
 packages/demobank-ui/src/pages/Test.tsx            |     5 +
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |     8 +-
 packages/demobank-ui/src/scss/DurationPicker.scss  |    70 -
 packages/demobank-ui/src/scss/_aside.scss          |   128 -
 packages/demobank-ui/src/scss/_card.scss           |    69 -
 .../demobank-ui/src/scss/_custom-calendar.scss     |   263 -
 packages/demobank-ui/src/scss/_footer.scss         |    35 -
 packages/demobank-ui/src/scss/_form.scss           |    71 -
 packages/demobank-ui/src/scss/_hero-bar.scss       |    55 -
 packages/demobank-ui/src/scss/_loading.scss        |    51 -
 packages/demobank-ui/src/scss/_main-section.scss   |    24 -
 packages/demobank-ui/src/scss/_misc.scss           |    50 -
 packages/demobank-ui/src/scss/_mixins.scss         |    34 -
 packages/demobank-ui/src/scss/_modal.scss          |    35 -
 packages/demobank-ui/src/scss/_nav-bar.scss        |   144 -
 packages/demobank-ui/src/scss/_table.scss          |   179 -
 packages/demobank-ui/src/scss/_theme-default.scss  |   136 -
 packages/demobank-ui/src/scss/_tiles.scss          |    24 -
 packages/demobank-ui/src/scss/_title-bar.scss      |    50 -
 packages/demobank-ui/src/scss/bank.scss            |   353 -
 packages/demobank-ui/src/scss/colors-bank.scss     |    31 -
 packages/demobank-ui/src/scss/demo.scss            |   167 -
 .../src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf        |   Bin 43752 -> 0 bytes
 packages/demobank-ui/src/scss/fonts/nunito.css     |    22 -
 .../fonts/materialdesignicons-webfont-4.9.95.eot   |   Bin 844600 -> 0 bytes
 .../fonts/materialdesignicons-webfont-4.9.95.ttf   |   Bin 844380 -> 0 bytes
 .../fonts/materialdesignicons-webfont-4.9.95.woff  |   Bin 404384 -> 0 bytes
 .../fonts/materialdesignicons-webfont-4.9.95.woff2 |   Bin 283040 -> 0 bytes
 .../scss/icons/materialdesignicons-4.9.95.min.css  | 15109 -------------------
 packages/demobank-ui/src/scss/libs/_all.scss       |    29 -
 packages/demobank-ui/src/scss/main.css             |     3 +
 packages/demobank-ui/src/scss/main.scss            |     5 -
 packages/demobank-ui/src/scss/pure.scss            |  1397 --
 packages/demobank-ui/src/scss/toggle.scss          |    51 -
 packages/demobank-ui/src/stories.tsx               |     2 -
 packages/demobank-ui/tailwind.config.js            |     8 +
 pnpm-lock.yaml                                     |    65 +-
 52 files changed, 820 insertions(+), 19156 deletions(-)
 create mode 100644 packages/demobank-ui/postcss.config.js
 create mode 100644 packages/demobank-ui/src/pages/Test.tsx
 delete mode 100644 packages/demobank-ui/src/scss/DurationPicker.scss
 delete mode 100644 packages/demobank-ui/src/scss/_aside.scss
 delete mode 100644 packages/demobank-ui/src/scss/_card.scss
 delete mode 100644 packages/demobank-ui/src/scss/_custom-calendar.scss
 delete mode 100644 packages/demobank-ui/src/scss/_footer.scss
 delete mode 100644 packages/demobank-ui/src/scss/_form.scss
 delete mode 100644 packages/demobank-ui/src/scss/_hero-bar.scss
 delete mode 100644 packages/demobank-ui/src/scss/_loading.scss
 delete mode 100644 packages/demobank-ui/src/scss/_main-section.scss
 delete mode 100644 packages/demobank-ui/src/scss/_misc.scss
 delete mode 100644 packages/demobank-ui/src/scss/_mixins.scss
 delete mode 100644 packages/demobank-ui/src/scss/_modal.scss
 delete mode 100644 packages/demobank-ui/src/scss/_nav-bar.scss
 delete mode 100644 packages/demobank-ui/src/scss/_table.scss
 delete mode 100644 packages/demobank-ui/src/scss/_theme-default.scss
 delete mode 100644 packages/demobank-ui/src/scss/_tiles.scss
 delete mode 100644 packages/demobank-ui/src/scss/_title-bar.scss
 delete mode 100644 packages/demobank-ui/src/scss/bank.scss
 delete mode 100644 packages/demobank-ui/src/scss/colors-bank.scss
 delete mode 100644 packages/demobank-ui/src/scss/demo.scss
 delete mode 100644 packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf
 delete mode 100644 packages/demobank-ui/src/scss/fonts/nunito.css
 delete mode 100644 packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot
 delete mode 100644 packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf
 delete mode 100644 packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff
 delete mode 100644 packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2
 delete mode 100644 packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css
 delete mode 100644 packages/demobank-ui/src/scss/libs/_all.scss
 create mode 100644 packages/demobank-ui/src/scss/main.css
 delete mode 100644 packages/demobank-ui/src/scss/main.scss
 delete mode 100644 packages/demobank-ui/src/scss/pure.scss
 delete mode 100644 packages/demobank-ui/src/scss/toggle.scss
 create mode 100644 packages/demobank-ui/tailwind.config.js

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/build.mjs b/packages/demobank-ui/build.mjs
index 22b91803a..64ddc3774 100755
--- a/packages/demobank-ui/build.mjs
+++ b/packages/demobank-ui/build.mjs
@@ -21,8 +21,8 @@ await build({
   type: "production",
   source: {
     js: ["src/index.tsx"],
-    assets: [{base:"src",files:["src/index.html"]}],
+    assets: [{ base: "src", files: ["src/index.html"] }],
   },
   destination: "./dist/prod",
-  css: "sass",
+  css: "postcss",
 });
diff --git a/packages/demobank-ui/dev.mjs b/packages/demobank-ui/dev.mjs
index 8b870451b..9c09e5716 100755
--- a/packages/demobank-ui/dev.mjs
+++ b/packages/demobank-ui/dev.mjs
@@ -24,11 +24,11 @@ const build = initializeDev({
   type: "development",
   source: {
     js: devEntryPoints,
-    assets: [{base:"src",files:["src/index.html"]}],
+    assets: [{ base: "src", files: ["src/index.html"] }],
   },
   destination: "./dist/dev",
   public: "/app",
-  css: "sass",
+  css: "postcss",
 });
 
 await build();
diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json
index 8b999aeed..744cb4180 100644
--- a/packages/demobank-ui/package.json
+++ b/packages/demobank-ui/package.json
@@ -46,6 +46,9 @@
   "devDependencies": {
     "@creativebulma/bulma-tooltip": "^1.2.0",
     "@gnu-taler/pogen": "^0.0.5",
+    "@tailwindcss/forms": "^0.5.3",
+    "@tailwindcss/typography": "^0.5.9",
+    "autoprefixer": "^10.4.14",
     "@types/chai": "^4.3.0",
     "@types/history": "^4.7.8",
     "@types/mocha": "^10.0.1",
@@ -62,6 +65,7 @@
     "po2json": "^0.4.5",
     "preact-render-to-string": "^5.2.6",
     "sass": "1.56.1",
+    "tailwindcss": "^3.3.2",
     "typescript": "5.2.2"
   },
   "pogen": {
diff --git a/packages/demobank-ui/postcss.config.js b/packages/demobank-ui/postcss.config.js
new file mode 100644
index 000000000..2e7af2b7f
--- /dev/null
+++ b/packages/demobank-ui/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}
diff --git a/packages/demobank-ui/src/components/CopyButton.tsx b/packages/demobank-ui/src/components/CopyButton.tsx
index c61083074..97ccbf2bf 100644
--- a/packages/demobank-ui/src/components/CopyButton.tsx
+++ b/packages/demobank-ui/src/components/CopyButton.tsx
@@ -4,57 +4,57 @@ import { useEffect, useState } from "preact/hooks";
 
 
 export function CopyIcon(): VNode {
-    return (
-      <svg height="16" viewBox="0 0 16 16" width="16">
-        <path
-          fill-rule="evenodd"
-          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
-        />
-        <path
-          fill-rule="evenodd"
-          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
-        />
-      </svg>
-    )
-  };
-  
-  export function CopiedIcon(): VNode {
-    return (
-      <svg height="16" viewBox="0 0 16 16" width="16">
-        <path
-          fill-rule="evenodd"
-          d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
-        />
-      </svg>
-    )
-  };
-  
-export   function CopyButton({ getContent }: { getContent: () => string }): VNode {
-    const [copied, setCopied] = useState(false);
-    function copyText(): void {
-      navigator.clipboard.writeText(getContent() || "");
-      setCopied(true);
-    }
-    useEffect(() => {
-      if (copied) {
-        setTimeout(() => {
-          setCopied(false);
-        }, 1000);
-      }
-    }, [copied]);
-  
-    if (!copied) {
-      return (
-        <button onClick={copyText} style={{ width: 32, height: 32, fontSize: "initial" }}>
-          <CopyIcon />
-        </button>
-      );
+  return (
+    <svg height="16" viewBox="0 0 16 16" width="16" stroke="currentColor" strokeWidth="1.5">
+      <path
+        fill-rule="evenodd"
+        d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
+      />
+      <path
+        fill-rule="evenodd"
+        d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
+      />
+    </svg>
+  )
+};
+
+export function CopiedIcon(): VNode {
+  return (
+    <svg height="16" viewBox="0 0 16 16" width="16" stroke="currentColor" strokeWidth="1.5">
+      <path
+        fill-rule="evenodd"
+        d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
+      />
+    </svg>
+  )
+};
+
+export function CopyButton({ getContent }: { getContent: () => string }): VNode {
+  const [copied, setCopied] = useState(false);
+  function copyText(): void {
+    navigator.clipboard.writeText(getContent() || "");
+    setCopied(true);
+  }
+  useEffect(() => {
+    if (copied) {
+      setTimeout(() => {
+        setCopied(false);
+      }, 1000);
     }
+  }, [copied]);
+
+  if (!copied) {
     return (
-      <div content="Copied" style={{ display: "inline-block" }}>
-        <button disabled style={{ width: 32, height: 32, fontSize: "initial" }}>
-          <CopiedIcon />
-        </button>
-      </div>
+      <button class="text-white" onClick={copyText} style={{ width: 32, height: 32, fontSize: "initial" }}>
+        <CopyIcon />
+      </button>
     );
-  }
\ No newline at end of file
+  }
+  return (
+    <div class="text-white" content="Copied" style={{ display: "inline-block" }}>
+      <button disabled style={{ width: 32, height: 32, fontSize: "initial" }}>
+        <CopiedIcon />
+      </button>
+    </div>
+  );
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index d5ea44e10..2c532e863 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -23,6 +23,7 @@ import { BusinessAccount } from "../pages/BusinessAccount.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
+import { Test } from "../pages/Test.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
@@ -34,6 +35,10 @@ export function Routing(): VNode {
       }}
     >
       <Router history={history}>
+        <Route
+          path="/test"
+          component={Test}
+        />
         <Route
           path="/operation/:wopid"
           component={({ wopid }: { wopid: string }) => (
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index 34d078c16..f4a78e516 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -33,42 +33,46 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
 export function ReadyView({ transactions }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   return (
-    <div class="results">
-      <table class="pure-table pure-table-striped">
-        <thead>
-          <tr>
-            <th>{i18n.str`Date`}</th>
-            <th>{i18n.str`Amount`}</th>
-            <th>{i18n.str`Counterpart`}</th>
-            <th>{i18n.str`Subject`}</th>
-          </tr>
-        </thead>
-        <tbody>
-          {transactions.map((item, idx) => {
-            return (
-              <tr key={idx}>
-                <td>
-                  {item.when.t_ms === "never"
-                    ? ""
-                    : format(item.when.t_ms, "dd/MM/yyyy HH:mm:ss")}
-                </td>
-                <td>
-                  {item.negative ? "-" : ""}
-                  {item.amount ? (
-                    `${Amounts.stringifyValue(item.amount)} ${
-                      item.amount.currency
-                    }`
-                  ) : (
-                    <span style={{ color: "grey" }}>&lt;invalid value&gt;</span>
-                  )}
-                </td>
-                <td>{item.counterpart}</td>
-                <td>{item.subject}</td>
-              </tr>
-            );
-          })}
-        </tbody>
-      </table>
+    <div class="px-4 mt-4">
+      <div class="sm:flex sm:items-center">
+        <div class="sm:flex-auto">
+          <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
+        </div>
+      </div>
+      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg">
+        <table class="min-w-full divide-y divide-gray-300">
+          <thead>
+            <tr>
+              <th scope="col" class="pl-4 pr-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:pl-6">{i18n.str`Date`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Amount`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Counterpart`}</th>
+              <th scope="col" class="px-3      py-3.5 text-left text-sm font-semibold text-gray-900 lg:table-cell">{i18n.str`Subject`}</th>
+            </tr>
+          </thead>
+          <tbody>
+            {transactions.map((item, idx) => {
+              return (
+                <tr key={idx}>
+                  <td class="relative py-4 pl-4 pr-3 text-sm sm:pl-6">
+                    <div class="font-medium text-gray-900">{item.when.t_ms === "never"
+                      ? ""
+                      : format(item.when.t_ms, "dd/MM/yyyy HH:mm:ss")}</div>
+                  </td>
+                  <td class="px-3 py-3.5 text-sm text-gray-500">{item.negative ? "-" : ""}
+                    {item.amount ? (
+                      `${Amounts.stringifyValue(item.amount)} ${item.amount.currency
+                      }`
+                    ) : (
+                      <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
+                    )}</td>
+                  <td class="px-3 py-3.5 text-sm text-gray-500">{item.counterpart}</td>
+                  <td class="px-3 py-3.5 text-sm text-gray-500">{item.subject}</td>
+                </tr>
+              );
+            })}
+          </tbody>
+        </table>
+      </div>
     </div>
   );
 }
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index 1f7034bc5..22752ab78 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -24,7 +24,6 @@ import { SWRConfig } from "swr";
 import { BackendStateProvider } from "../context/backend.js";
 import { strings } from "../i18n/strings.js";
 import { Routing } from "./Routing.js";
-
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 /**
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 61f458e51..af8381135 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -365,7 +365,15 @@ export function useTransactions(
     RequestError<SandboxBackend.SandboxError>
   >(
     [`access-api/accounts/${account}/transactions`, args?.page, PAGE_SIZE],
-    paginatedFetcher,
+    paginatedFetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    refreshWhenOffline: false,
+    // revalidateOnMount: false,
+    revalidateIfStale: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+  }
   );
 
   const [lastAfter, setLastAfter] = useState<
diff --git a/packages/demobank-ui/src/index.html b/packages/demobank-ui/src/index.html
index e21e1fccc..315985648 100644
--- a/packages/demobank-ui/src/index.html
+++ b/packages/demobank-ui/src/index.html
@@ -16,27 +16,28 @@
  @author Sebastian Javier Marchano
 -->
 <!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1" />
-    <meta name="taler-support" content="uri">
-    <meta name="mobile-web-app-capable" content="yes" />
-    <meta name="apple-mobile-web-app-capable" content="yes" />
-    <link
-      rel="icon"
-      href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
-    />
-    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <title>Demobank</title>
-    <!-- Optional customization script.  -->
-    <script src="demobank-ui-settings.js"></script>
-    <!-- Entry point for the demobank SPA. -->
-    <script type="module" src="index.js"></script>
-    <link rel="stylesheet" href="index.css" />
-  </head>
-  <body>
-    <div id="app"></div>
-  </body>
-</html>
+<html lang="en" class="h-full bg-gray-100">
+
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+  <meta charset="utf-8" />
+  <meta name="viewport" content="width=device-width,initial-scale=1" />
+  <meta name="taler-support" content="uri">
+  <meta name="mobile-web-app-capable" content="yes" />
+  <meta name="apple-mobile-web-app-capable" content="yes" />
+  <link rel="icon"
+    href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" />
+  <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
+  <title>Demobank</title>
+  <!-- Optional customization script.  -->
+  <script src="demobank-ui-settings.js"></script>
+  <!-- Entry point for the demobank SPA. -->
+  <script type="module" src="index.js"></script>
+  <link rel="stylesheet" href="index.css" />
+</head>
+
+<body class="h-full">
+  <div id="app"></div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/packages/demobank-ui/src/index.tsx b/packages/demobank-ui/src/index.tsx
index 2e0f740fe..b7d69fd2d 100644
--- a/packages/demobank-ui/src/index.tsx
+++ b/packages/demobank-ui/src/index.tsx
@@ -16,7 +16,7 @@
 
 import App from "./components/app.js";
 import { h, render } from "preact";
-import "./scss/main.scss";
+import "./scss/main.css"
 
 const app = document.getElementById("app");
 
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index b476759b4..f2cbbba6c 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -21,54 +21,67 @@ import { Transactions } from "../../components/Transactions/index.js";
 import { PaymentOptions } from "../PaymentOptions.js";
 import { State } from "./index.js";
 import { CopyButton } from "../../components/CopyButton.js";
+import { bankUiSettings } from "../../settings.js";
 
-export function InvalidIbanView({error}:State.InvalidIban) {
-    return (
-      <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
-    );
+export function InvalidIbanView({ error }: State.InvalidIban) {
+  return (
+    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+  );
 }
 
-export function ReadyView({ account, balance, balanceIsDebit, limit, payto }: State.Ready): VNode<{}> {
+const IS_PUBLIC_ACCOUNT_ENABLED = false
+
+function ImportantMessage(): VNode {
   const { i18n } = useTranslationContext();
-  return <Fragment>
-    <div>
-      <h1 class="nav welcome-text">
-        <i18n.Translate>
-          Welcome,  {account} (<a href={stringifyPaytoUri(payto)}>{payto.iban}</a>)! <CopyButton getContent={() => stringifyPaytoUri(payto)} />
-        </i18n.Translate>
-      </h1>
-    </div>
+  return <div aria-live="assertive" class="pointer-events-none flex items-end px-4 py-6 sm:items-start sm:p-6">
+    <div class="flex w-full flex-col items-center space-y-4 ">
+      <div class="pointer-events-auto flex w-full max-w-md rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5">
+        <div class="w-0 flex-1 p-4">
+          <div class="flex items-start">
+            <div class="ml-3 w-0 flex-1">
+              <p class="text-sm font-medium text-gray-900">
+                <i18n.Translate>
+                  Welcome,  "account"
+                </i18n.Translate>
 
-    <section id="assets">
-      <div class="asset-summary">
-        <h2>{i18n.str`Bank account balance`}</h2>
-        {!balance ? (
-          <div class="large-amount" style={{ color: "gray" }}>
-            Waiting server response...
+              </p>
+              <p class="mt-1 text-sm text-gray-500">
+                {bankUiSettings.showDemoNav &&
+                  <p>
+                    {IS_PUBLIC_ACCOUNT_ENABLED ? (
+                      <i18n.Translate>
+                        This part of the demo shows how a bank that supports Taler
+                        directly would work. In addition to using your own bank
+                        account, you can also see the transaction history of some{" "}
+                        <a href="/public-accounts">Public Accounts</a>.
+                      </i18n.Translate>
+                    ) : (
+                      <i18n.Translate>
+                        This part of the demo shows how a bank that supports Taler
+                        directly would work.
+                      </i18n.Translate>
+                    )}
+                  </p>
+                }</p>
+            </div>
           </div>
-        ) : (
-          <div class="large-amount amount">
-            {balanceIsDebit ? <b>-</b> : null}
-            <span class="value">{`${Amounts.stringifyValue(balance)}`}</span>
-            &nbsp;
-            <span class="currency">{`${balance.currency}`}</span>
-          </div>
-        )}
-      </div>
-    </section>
-    <section id="payments">
-      <div class="payments">
-        <h2>{i18n.str`Payments`}</h2>
-        <PaymentOptions limit={limit} />
+        </div>
+        <div class="flex border-l border-gray-200">
+          <button type="button" class="flex w-full items-center justify-center rounded-none rounded-r-lg border border-transparent p-4 text-sm font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500">
+            Dismiss
+          </button>
+        </div>
       </div>
-    </section>
+    </div>
+  </div>
 
-    <section style={{ marginTop: "2em" }}>
-      <div class="active">
-        <h3>{i18n.str`Latest transactions`}</h3>
-        <Transactions account={account} />
-      </div>
-    </section>
+}
+
+export function ReadyView({ account, balance, balanceIsDebit, limit, payto }: State.Ready): VNode<{}> {
+  const { i18n } = useTranslationContext();
+  return <Fragment>
+    <PaymentOptions limit={limit} />
+    <Transactions account={account} />
   </Fragment>;
 }
 
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index dc61f1302..5b6d95ade 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Logger, TranslatedString } from "@gnu-taler/taler-util";
+import { Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useState } from "preact/hooks";
@@ -25,6 +25,7 @@ import { useBusinessAccountDetails } from "../hooks/circuit.js";
 import { bankUiSettings } from "../settings.js";
 import { useSettings } from "../hooks/settings.js";
 import { ErrorMessage, onNotificationUpdate } from "../hooks/notification.js";
+import { CopyButton, CopyIcon } from "../components/CopyButton.js";
 
 const IS_PUBLIC_ACCOUNT_ENABLED = false;
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
@@ -70,6 +71,7 @@ export function BankFrame({
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
   const [settings, updateSettings] = useSettings();
+  const [open, setOpen] = useState(false)
 
   const demo_sites = [];
   for (const i in bankUiSettings.demoSites)
@@ -79,83 +81,165 @@ export function BankFrame({
       </a>,
     );
 
-  return (
-    <Fragment>
-      <header
-        class="demobar"
-        style="display: flex; flex-direction: row; justify-content: space-between;"
-      >
-        <a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
-        <div style="max-width: 50em; margin-left: 2em; margin-right: 2em;">
-          <h1>
-            <span class="it">
-              <a href="/">{bankUiSettings.bankName}</a>
-            </span>
-          </h1>
-          {maybeDemoContent(
-            <p>
-              {IS_PUBLIC_ACCOUNT_ENABLED ? (
-                <i18n.Translate>
-                  This part of the demo shows how a bank that supports Taler
-                  directly would work. In addition to using your own bank
-                  account, you can also see the transaction history of some{" "}
-                  <a href="/public-accounts">Public Accounts</a>.
-                </i18n.Translate>
-              ) : (
-                <i18n.Translate>
-                  This part of the demo shows how a bank that supports Taler
-                  directly would work.
-                </i18n.Translate>
-              )}
-            </p>,
-          )}
+  return (<div class="min-h-full">
+    <div class="bg-indigo-600 pb-32">
+      <nav class="border-b border-indigo-300 border-opacity-25 bg-indigo-600 lg:border-none">
+        <div class="mx-auto max-w-7xl px-2 sm:px-4 lg:px-8">
+          <div class="relative flex h-16 items-center justify-between lg:border-b lg:border-indigo-400 lg:border-opacity-25">
+            <div class="flex items-center px-2 lg:px-0">
+              <div class="flex-shrink-0">
+                <img class="block h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=300" alt="Your Company" />
+              </div>
+              <div class="hidden lg:ml-10 lg:block">
+                <div class="flex space-x-4">
+                  {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
+                  {bankUiSettings.demoSites.map(([name, url]) => {
+                    return <a href={url} class="text-white hover:bg-indigo-500 hover:bg-opacity-75 rounded-md py-2 px-3 text-sm font-medium">{name}</a>
+                  })}
+                </div>
+              </div>
+            </div>
+
+            <div class="flex lg:hidden">
+              {/* <!-- Mobile menu button --> */}
+              <button type="button" class="relative inline-flex items-center justify-center rounded-md bg-indigo-600 p-2 text-indigo-200 hover:bg-indigo-500 hover:bg-opacity-75 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-indigo-600" aria-controls="mobile-menu" aria-expanded="false"
+                onClick={(e) => {
+                  setOpen(!open)
+                }}>
+                <span class="absolute -inset-0.5"></span>
+                <span class="sr-only">Open main menu</span>
+                {/* <!-- Menu open: "hidden", Menu closed: "block" --> */}
+                <svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                  <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
+                </svg>
+                {/* <!-- Menu open: "block", Menu closed: "hidden" --> */}
+                <svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
+                </svg>
+              </button>
+            </div>
+          </div>
         </div>
-      </header>
-      <div style="display:flex; flex-direction: column;" class="navcontainer">
-        <nav class="demolist">
-          {maybeDemoContent(<Fragment>{demo_sites}</Fragment>)}
-          {backend.state.status === "loggedIn" ? (
-            <Fragment>
-              {goToBusinessAccount && !backend.state.isUserAdministrator ? (
-                <MaybeBusinessButton
-                  account={backend.state.username}
-                  onClick={goToBusinessAccount}
-                />
-              ) : undefined}
 
-              <LangSelector />
+        {/* <!-- Mobile menu, show/hide based on menu state. --> */}
+        {open &&
+          <div class="lg:hidden" id="mobile-menu">
+            <div class="space-y-1 px-2 pb-3 pt-2">
+              {/* <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
+              {bankUiSettings.demoSites.map(([name, url]) => {
+                return <a href={url} class="text-white hover:bg-indigo-500 hover:bg-opacity-75 block rounded-md py-2 px-3 text-base font-medium">{name}</a>
+              })}
+            </div>
 
-              <a
-                href="#"
-                class="pure-button logout-button"
-                onClick={() => {
-                  backend.logOut();
-                  updateSettings("currentWithdrawalOperationId", undefined);
-                }}
-              >{i18n.str`Logout`}</a>
-            </Fragment>
-          ) : undefined}
-        </nav>
-      </div>
-      <section id="main" class="content">
-        <StatusBanner />
-        {children}
-      </section>
-      <section id="footer" class="footer">
-        <hr />
-        <div>
-          <p>
-            You can learn more about GNU Taler on our{" "}
-            <a href="https://taler.net">main website</a>.
-          </p>
+          </div>
+        }
+      </nav >
+      <header class="py-10">
+
+        <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
+          <div class=" flex flex-wrap items-center justify-between sm:flex-nowrap">
+            {/* <h1 class="text-base font-semibold leading-6 text-gray-900"></h1> */}
+            <h1 class="text-3xl font-bold tracking-tight text-white"><WelcomeAccount /></h1>
+            <div>
+
+              <h2 class="text-3xl font-bold tracking-tight text-white">KUDOS 100.00</h2>
+            </div>
+            {/* <div class="ml-4 mt-2 flex-shrink-0">
+              <button type="button" class="relative inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Create new job</button>
+            </div> */}
+          </div>
         </div>
-        <div style="flex-grow:1" />
-        <p>
-          Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
-          <TestingTag />
-        </p>
-      </section>
-    </Fragment>
+      </header>
+    </div >
+
+    <main class="-mt-32">
+      <div class="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8">
+        <div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
+          {/* <!-- Your content --> */}
+          {children}
+        </div>
+      </div>
+    </main>
+
+    <Footer />
+  </div >
+
+    // <Fragment>
+    //   <header
+    //     class="demobar"
+    //     style="display: flex; flex-direction: row; justify-content: space-between;"
+    //   >
+    //     <a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
+    //     <div style="max-width: 50em; margin-left: 2em; margin-right: 2em;">
+    //       <h1>
+    //         <span class="it">
+    //           <a href="/">{bankUiSettings.bankName}</a>
+    //         </span>
+    //       </h1>
+    //       {maybeDemoContent(
+    //         <p>
+    //           {IS_PUBLIC_ACCOUNT_ENABLED ? (
+    //             <i18n.Translate>
+    //               This part of the demo shows how a bank that supports Taler
+    //               directly would work. In addition to using your own bank
+    //               account, you can also see the transaction history of some{" "}
+    //               <a href="/public-accounts">Public Accounts</a>.
+    //             </i18n.Translate>
+    //           ) : (
+    //             <i18n.Translate>
+    //               This part of the demo shows how a bank that supports Taler
+    //               directly would work.
+    //             </i18n.Translate>
+    //           )}
+    //         </p>,
+    //       )}
+    //     </div>
+    //   </header>
+    //   <div style="display:flex; flex-direction: column;" class="navcontainer">
+    //     <nav class="demolist">
+    //       {maybeDemoContent(<Fragment>{demo_sites}</Fragment>)}
+    //       {backend.state.status === "loggedIn" ? (
+    //         <Fragment>
+    //           {goToBusinessAccount && !backend.state.isUserAdministrator ? (
+    //             <MaybeBusinessButton
+    //               account={backend.state.username}
+    //               onClick={goToBusinessAccount}
+    //             />
+    //           ) : undefined}
+
+    //           <LangSelector />
+
+    //           <a
+    //             href="#"
+    //             class="pure-button logout-button"
+    //             onClick={() => {
+    //               backend.logOut();
+    //               updateSettings("currentWithdrawalOperationId", undefined);
+    //             }}
+    //           >{i18n.str`Logout`}</a>
+    //         </Fragment>
+    //       ) : undefined}
+    //     </nav>
+    //   </div>
+    //   <section id="main" class="content">
+    //     <StatusBanner />
+    //     {children}
+    //   </section>
+    //   <section id="footer" class="footer">
+    //     <hr />
+    //     <div>
+    //       <p>
+    //         You can learn more about GNU Taler on our{" "}
+    //         <a href="https://taler.net">main website</a>.
+    //       </p>
+    //     </div>
+    //     <div style="flex-grow:1" />
+    //     <p>
+    //       Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
+    //       <TestingTag />
+    //     </p>
+    //   </section>
+    // </Fragment>
   );
 }
 
@@ -290,7 +374,7 @@ function TestingTag(): VNode {
   const testingUrl = localStorage.getItem("bank-base-url");
   if (!testingUrl) return <Fragment />;
   return (
-    <span style={{ color: "gray" }}>
+    <p class="text-xs leading-5 text-gray-300">
       Testing with {testingUrl}{" "}
       <a
         href=""
@@ -302,6 +386,36 @@ function TestingTag(): VNode {
       >
         stop testing
       </a>
-    </span>
+    </p>
   );
 }
+
+function Footer() {
+  return (
+    <footer class="absolute bottom-4">
+      <div class="mt-8 mx-8 md:order-1 md:mt-0">
+        <div>
+          <p class="text-xs leading-5 text-gray-400">
+            You can learn more about GNU Taler on our{" "}
+            <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net">main website</a>.
+          </p>
+        </div>
+        <div style="flex-grow:1" />
+        <p class="text-xs leading-5 text-gray-400">
+          Copyright &copy; 2014&mdash;2022 Taler Systems SA. {versionText}{" "}
+          <TestingTag />
+        </p>
+      </div>
+    </footer>
+  );
+}
+
+function WelcomeAccount(): VNode {
+  const { i18n } = useTranslationContext();
+  const account = "Sebastian"
+  const payto: PaytoUriIBAN = parsePaytoUri("payto://iban/bank.localhost/DE955922") as PaytoUriIBAN
+  return <i18n.Translate>
+    Welcome,  {account} (<a href={stringifyPaytoUri(payto)}>{payto.iban}</a>)! <CopyButton getContent={() => stringifyPaytoUri(payto)} />
+  </i18n.Translate>
+
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 3552da7b4..cf3f41deb 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -31,11 +31,77 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings();
 
-  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer">(
-    "charge-wallet",
-  );
+  const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
 
-  return (
+  return (<fieldset>
+    <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
+      <i18n.Translate>Send money to</i18n.Translate>
+    </legend>
+
+    <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
+      {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+      <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+        <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => {
+          setTab("charge-wallet")
+        }} />
+        <span class="flex flex-1">
+          <span class="flex flex-col">
+            <span id="project-type-0-label" class="block text-sm font-semibold font-medium text-gray-900">
+              <i18n.Translate>a Taler Wallet</i18n.Translate>
+            </span>
+            <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+              <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
+            </span>
+          </span>
+        </span>
+        {/* <!-- Not Checked: "invisible" --> */}
+        <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+          <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+        </svg>
+      </label>
+
+
+      {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+      <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+        <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => {
+          setTab("wire-transfer")
+        }} />
+        <span class="flex flex-1">
+          <span class="flex flex-col">
+            <span id="project-type-1-label" class="block text-sm font-semibold font-medium text-gray-900">
+              <i18n.Translate>another bank account</i18n.Translate>
+            </span>
+            <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+              <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+            </span>
+          </span>
+        </span>
+        <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+          <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+        </svg>
+      </label>
+    </div>
+    {tab === "charge-wallet" && (
+      <WalletWithdrawForm
+        focus
+        limit={limit}
+        onSuccess={(id) => {
+          updateSettings("currentWithdrawalOperationId", id);
+        }}
+      />
+    )}
+    {tab === "wire-transfer" && (
+      <PaytoWireTransferForm
+        focus
+        limit={limit}
+        onSuccess={() => {
+          notifyInfo(i18n.str`Wire transfer created!`);
+        }}
+      />
+    )}
+
+  </fieldset>)
+  {/* return (
     <article>
       <div class="payments">
         <div class="tab">
@@ -56,31 +122,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
             {i18n.str`Wire transfer`}
           </button>
         </div>
-        {tab === "charge-wallet" && (
-          <div id="charge-wallet" class="tabcontent active">
-            <h3>{i18n.str`Obtain digital cash`}</h3>
-            <WalletWithdrawForm
-              focus
-              limit={limit}
-              onSuccess={(id) => {
-                updateSettings("currentWithdrawalOperationId", id);
-              }}
-            />
-          </div>
-        )}
-        {tab === "wire-transfer" && (
-          <div id="wire-transfer" class="tabcontent active">
-            <h3>{i18n.str`Transfer to bank account`}</h3>
-            <PaytoWireTransferForm
-              focus
-              limit={limit}
-              onSuccess={() => {
-                notifyInfo(i18n.str`Wire transfer created!`);
-              }}
-            />
-          </div>
-        )}
       </div>
     </article>
-  );
+  ); */}
 }
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index d16dc70f8..1107360bd 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -17,26 +17,24 @@
 import {
   AmountJson,
   Amounts,
-  buildPayto,
   HttpStatusCode,
   Logger,
-  parsePaytoUri,
-  stringifyPaytoUri,
+  parsePaytoUri
 } from "@gnu-taler/taler-util";
 import {
   RequestError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
+import { h, VNode, Fragment } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
-import { notifyError } from "../hooks/notification.js";
+import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAccessAPI } from "../hooks/access.js";
+import { notifyError } from "../hooks/notification.js";
 import {
   buildRequestErrorMessage,
   undefinedIfEmpty,
   validateIBAN,
 } from "../utils.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 
 const logger = new Logger("PaytoWireTransferForm");
 
@@ -72,293 +70,322 @@ export function PaytoWireTransferForm({
     iban: !iban
       ? i18n.str`Missing IBAN`
       : !IBAN_REGEX.test(iban)
-      ? i18n.str`IBAN should have just uppercased letters and numbers`
-      : validateIBAN(iban, i18n),
+        ? i18n.str`IBAN should have just uppercased letters and numbers`
+        : validateIBAN(iban, i18n),
     subject: !subject ? i18n.str`Missing subject` : undefined,
     amount: !trimmedAmountStr
       ? i18n.str`Missing amount`
       : !parsedAmount
-      ? i18n.str`Amount is not valid`
-      : Amounts.isZero(parsedAmount)
-      ? i18n.str`Should be greater than 0`
-      : Amounts.cmp(limit, parsedAmount) === -1
-      ? i18n.str`balance is not enough`
-      : undefined,
+        ? i18n.str`Amount is not valid`
+        : Amounts.isZero(parsedAmount)
+          ? i18n.str`Should be greater than 0`
+          : Amounts.cmp(limit, parsedAmount) === -1
+            ? i18n.str`balance is not enough`
+            : undefined,
   });
 
   const { createTransaction } = useAccessAPI();
 
-  if (!isRawPayto)
-    return (
-      <div>
-        <form
-          class="pure-form"
-          name="wire-transfer-form"
-          onSubmit={(e) => {
-            e.preventDefault();
-          }}
-          autoCapitalize="none"
-          autoCorrect="off"
-        >
-          <label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
-          <input
-            ref={ref}
-            type="text"
-            id="iban"
-            name="iban"
-            value={iban ?? ""}
-            placeholder="CC0123456789"
-            required
-            pattern={ibanRegex}
-            onInput={(e): void => {
-              setIban(e.currentTarget.value);
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errorsWire?.iban}
-            isDirty={iban !== undefined}
-          />
-          <label for="subject">{i18n.str`Transfer subject:`}</label>&nbsp;
-          <input
-            type="text"
-            name="subject"
-            id="subject"
-            placeholder="subject"
-            value={subject ?? ""}
-            required
-            onInput={(e): void => {
-              setSubject(e.currentTarget.value);
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errorsWire?.subject}
-            isDirty={subject !== undefined}
-          />
-          <label for="amount">{i18n.str`Amount:`}</label>&nbsp;
-          <div style={{ width: "max-content", display: "flex" }}>
-            <input
-              type="text"
-              readonly
-              class="currency-indicator"
-              size={limit.currency.length}
-              maxLength={limit.currency.length}
-              tabIndex={-1}
-              style={{
-                borderTopRightRadius: 0,
-                borderBottomRightRadius: 0,
-                borderRight: 0,
-              }}
-              value={limit.currency}
-            />
-            <input
-              type="number"
-              name="amount"
-              id="amount"
-              placeholder="amount"
-              required
-              style={{
-                borderTopLeftRadius: 0,
-                borderBottomLeftRadius: 0,
-                borderLeft: 0,
-                width: 150,
-              }}
-              value={amount ?? ""}
-              onInput={(e): void => {
-                setAmount(e.currentTarget.value);
-              }}
-            />
-          </div>
-          <ShowInputErrorLabel
-            message={errorsWire?.amount}
-            isDirty={amount !== undefined}
-          />
-          <p style={{ display: "flex", justifyContent: "space-between" }}>
-            <input
-              type="submit"
-              class="pure-button pure-button-primary"
-              disabled={!!errorsWire}
-              value="Send"
-              onClick={async (e) => {
-                e.preventDefault();
-                if (!(iban && subject && amount)) {
-                  return;
-                }
-                const ibanPayto = buildPayto("iban", iban, undefined);
-                ibanPayto.params.message = encodeURIComponent(subject);
-                const paytoUri = stringifyPaytoUri(ibanPayto);
-
-                try {
-                  await createTransaction({
-                    paytoUri,
-                    amount: `${limit.currency}:${amount}`,
-                  });
-                  onSuccess();
-                  setAmount(undefined);
-                  setIban(undefined);
-                  setSubject(undefined);
-                } catch (error) {
-                  if (error instanceof RequestError) {
-                    notifyError(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.BadRequest
-                            ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
-                            : undefined,
-                      }),
-                    );
-                  } else {
-                    notifyError({
-                      title: i18n.str`Operation failed, please report`,
-                      description:
-                        error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error),
-                    });
-                  }
-                }
-              }}
-            />
-            <input
-              type="button"
-              class="pure-button"
-              value="Clear"
-              onClick={async (e) => {
-                e.preventDefault();
-                setAmount(undefined);
-                setIban(undefined);
-                setSubject(undefined);
-              }}
-            />
-          </p>
-        </form>
-        <p>
-          <a
-            href="#"
-            onClick={(e) => {
-              setIsRawPayto(true);
-              e.preventDefault();
-            }}
-          >
-            {i18n.str`Want to try the raw payto://-format?`}
-          </a>
-        </p>
-      </div>
-    );
-
   const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput);
 
   const errorsPayto = undefinedIfEmpty({
     rawPaytoInput: !rawPaytoInput
       ? i18n.str`required`
       : !parsed
-      ? i18n.str`does not follow the pattern`
-      : !parsed.params.amount
-      ? i18n.str`use the "amount" parameter to specify the amount to be transferred`
-      : Amounts.parse(parsed.params.amount) === undefined
-      ? i18n.str`the amount is not valid`
-      : !parsed.params.message
-      ? i18n.str`use the "message" parameter to specify a reference text for the transfer`
-      : !parsed.isKnown || parsed.targetType !== "iban"
-      ? i18n.str`only "IBAN" target are supported`
-      : !IBAN_REGEX.test(parsed.iban)
-      ? i18n.str`IBAN should have just uppercased letters and numbers`
-      : validateIBAN(parsed.iban, i18n),
+        ? i18n.str`does not follow the pattern`
+        : !parsed.params.amount
+          ? i18n.str`use the "amount" parameter to specify the amount to be transferred`
+          : Amounts.parse(parsed.params.amount) === undefined
+            ? i18n.str`the amount is not valid`
+            : !parsed.params.message
+              ? i18n.str`use the "message" parameter to specify a reference text for the transfer`
+              : !parsed.isKnown || parsed.targetType !== "iban"
+                ? i18n.str`only "IBAN" target are supported`
+                : !IBAN_REGEX.test(parsed.iban)
+                  ? i18n.str`IBAN should have just uppercased letters and numbers`
+                  : validateIBAN(parsed.iban, i18n),
   });
+  // if (!isRawPayto) {
+  return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+    <div class="px-4 sm:px-0">
+      <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Transfer details</i18n.Translate></h2>
+      <div>
+        <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
+          {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+          <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+            <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => {
+              setIsRawPayto(false)
+            }} />
+            <span class="flex flex-1">
+              <span class="flex flex-col">
+                <span id="project-type-0-label" class="block text-sm  font-medium text-gray-900">
+                  <i18n.Translate>form</i18n.Translate>
+                </span>
+              </span>
+            </span>
+          </label>
 
-  return (
-    <div>
-      <p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p>
-      <form
-        class="pure-form"
-        name="payto-form"
-        onSubmit={(e) => {
-          e.preventDefault();
-        }}
-        autoCapitalize="none"
-        autoCorrect="off"
-      >
-        <p>
-          <label for="address">{i18n.str`payto URI:`}</label>&nbsp;
-          <input
-            name="address"
-            type="text"
-            size={50}
-            ref={ref}
-            id="address"
-            value={rawPaytoInput ?? ""}
-            required
-            placeholder={i18n.str`payto address`}
-            // pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
-            onInput={(e): void => {
-              rawPaytoInputSetter(e.currentTarget.value);
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errorsPayto?.rawPaytoInput}
-            isDirty={rawPaytoInput !== undefined}
-          />
-          <br />
-          <div style={{ fontSize: "small", marginTop: 4 }}>
-            Hint:
-            <code>
-              payto://iban/[receiver-iban]?message=[subject]&amount=[
-              {limit.currency}
-              :X.Y]
-            </code>
-          </div>
-        </p>
-        <p>
-          <input
-            class="pure-button pure-button-primary"
-            type="button"
-            disabled={!!errorsPayto}
-            value={i18n.str`Send`}
-            onClick={async () => {
-              if (!rawPaytoInput) {
-                logger.error("Didn't get any raw Payto string!");
-                return;
-              }
 
-              try {
-                await createTransaction({
-                  paytoUri: rawPaytoInput,
-                });
-                onSuccess();
-                rawPaytoInputSetter(undefined);
-              } catch (error) {
-                if (error instanceof RequestError) {
-                  notifyError(
-                    buildRequestErrorMessage(i18n, error.cause, {
-                      onClientError: (status) =>
-                        status === HttpStatusCode.BadRequest
-                          ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
-                          : undefined,
-                    }),
-                  );
-                } else {
-                  notifyError({
-                    title: i18n.str`Operation failed, please report`,
-                    description:
-                      error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error),
-                  });
-                }
-              }
-            }}
-          />
-        </p>
-        <p>
-          <a
-            href="/account"
-            onClick={() => {
-              setIsRawPayto(false);
-            }}
-          >
-            {i18n.str`Use wire-transfer form?`}
-          </a>
-        </p>
-      </form>
+          {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+          <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+            <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => {
+              setIsRawPayto(true)
+            }} />
+            <span class="flex flex-1">
+              <span class="flex flex-col">
+                <span id="project-type-1-label" class="block text-sm  font-medium text-gray-900">
+                  <i18n.Translate>payto://</i18n.Translate>
+                </span>
+              </span>
+            </span>
+          </label>
+        </div>
+      </div>
     </div>
-  );
+
+    <form class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
+      <div class="px-4 py-6 sm:p-8">
+        <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+          {!isRawPayto ?
+            <Fragment>
+
+              <div class="sm:col-span-3">
+                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Account number`}</label>
+                <div class="mt-2">
+                  <input
+                    ref={ref}
+                    type="text"
+                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                    id="iban"
+                    name="iban"
+                    value={iban ?? ""}
+                    placeholder="CC0123456789"
+                    required
+                    pattern={ibanRegex}
+                    onInput={(e): void => {
+                      setIban(e.currentTarget.value);
+                    }}
+                  />
+                  <ShowInputErrorLabel
+                    message={errorsWire?.iban}
+                    isDirty={iban !== undefined}
+                  />
+                </div>
+                <p class="mt-2 text-sm text-gray-500" id="email-description">the receiver of the money</p>
+              </div>
+
+              <div class="sm:col-span-3">
+              </div>
+
+              <div class="sm:col-span-3">
+                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Transfer subject`}</label>
+                <div class="mt-2">
+
+                  <input
+                    type="text"
+                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                    name="subject"
+                    id="subject"
+                    placeholder="subject"
+                    value={subject ?? ""}
+                    required
+                    onInput={(e): void => {
+                      setSubject(e.currentTarget.value);
+                    }}
+                  />
+                  <ShowInputErrorLabel
+                    message={errorsWire?.subject}
+                    isDirty={subject !== undefined}
+                  />
+                </div>
+                <p class="mt-2 text-sm text-gray-500" id="email-description">some text to identify the transfer</p>
+
+              </div>
+
+              <div class="sm:col-span-3">
+              </div>
+
+              <div class="sm:col-span-3">
+                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`Amount`}</label>
+                <div class="mt-2">
+                  <input type="text" name="first-name" id="first-name" autocomplete="given-name" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" />
+                </div>
+              </div>
+
+              <div class="sm:col-span-3">
+              </div>
+            </Fragment> :
+            <Fragment>
+              <div class="sm:col-span-6">
+                <label for="first-name" class="block text-sm font-medium leading-6 text-gray-900">{i18n.str`payto URI:`}</label>
+                <div class="mt-2">
+                  <input
+                    name="address"
+                    type="text"
+                    size={50}
+                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" ref={ref}
+                    id="address"
+                    value={rawPaytoInput ?? ""}
+                    required
+                    placeholder={i18n.str`payto://iban/[receiver-iban]?message=[subject]&amount=[${limit.currency}:X.Y]`}
+                    // pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
+                    onInput={(e): void => {
+                      rawPaytoInputSetter(e.currentTarget.value);
+                    }}
+                  />
+                  <ShowInputErrorLabel
+                    message={errorsPayto?.rawPaytoInput}
+                    isDirty={rawPaytoInput !== undefined}
+                  />
+                </div>
+              </div>
+
+            </Fragment>
+          }
+        </div>
+      </div>
+      <div class="flex items-center justify-end gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+        <button type="button" class="text-sm font-semibold leading-6 text-gray-900">Cancel</button>
+        <button type="submit" class="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
+          <i18n.Translate>Send</i18n.Translate>
+        </button>
+      </div>
+    </form>
+  </div >
+  )
+  // }
+  // return (
+  //   <div>
+  //     <form
+  //       class="pure-form"
+  //       name="wire-transfer-form"
+  //       onSubmit={(e) => {
+  //         e.preventDefault();
+  //       }}
+  //       autoCapitalize="none"
+  //       autoCorrect="off"
+  //     >
+  //       <label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
+
+  //       <label for="subject">{i18n.str`Transfer subject:`}</label>&nbsp;
+
+  //       <label for="amount">{i18n.str`Amount:`}</label>&nbsp;
+  //       <div style={{ width: "max-content", display: "flex" }}>
+  //         <input
+  //           type="text"
+  //           readonly
+  //           class="currency-indicator"
+  //           size={limit.currency.length}
+  //           maxLength={limit.currency.length}
+  //           tabIndex={-1}
+  //           style={{
+  //             borderTopRightRadius: 0,
+  //             borderBottomRightRadius: 0,
+  //             borderRight: 0,
+  //           }}
+  //           value={limit.currency}
+  //         />
+  //         <input
+  //           type="number"
+  //           name="amount"
+  //           id="amount"
+  //           placeholder="amount"
+  //           required
+  //           style={{
+  //             borderTopLeftRadius: 0,
+  //             borderBottomLeftRadius: 0,
+  //             borderLeft: 0,
+  //             width: 150,
+  //           }}
+  //           value={amount ?? ""}
+  //           onInput={(e): void => {
+  //             setAmount(e.currentTarget.value);
+  //           }}
+  //         />
+  //       </div>
+  //       <ShowInputErrorLabel
+  //         message={errorsWire?.amount}
+  //         isDirty={amount !== undefined}
+  //       />
+  //       <p style={{ display: "flex", justifyContent: "space-between" }}>
+  //         <input
+  //           type="submit"
+  //           class="pure-button pure-button-primary"
+  //           disabled={!!errorsWire}
+  //           value="Send"
+  //           onClick={async (e) => {
+  //             e.preventDefault();
+  //             if (!(iban && subject && amount)) {
+  //               return;
+  //             }
+  //             const ibanPayto = buildPayto("iban", iban, undefined);
+  //             ibanPayto.params.message = encodeURIComponent(subject);
+  //             const paytoUri = stringifyPaytoUri(ibanPayto);
+
+  //             try {
+  //               await createTransaction({
+  //                 paytoUri,
+  //                 amount: `${limit.currency}:${amount}`,
+  //               });
+  //               onSuccess();
+  //               setAmount(undefined);
+  //               setIban(undefined);
+  //               setSubject(undefined);
+  //             } catch (error) {
+  //               if (error instanceof RequestError) {
+  //                 notifyError(
+  //                   buildRequestErrorMessage(i18n, error.cause, {
+  //                     onClientError: (status) =>
+  //                       status === HttpStatusCode.BadRequest
+  //                         ? i18n.str`The request was invalid or the payto://-URI used unacceptable features.`
+  //                         : undefined,
+  //                   }),
+  //                 );
+  //               } else {
+  //                 notifyError({
+  //                   title: i18n.str`Operation failed, please report`,
+  //                   description:
+  //                     error instanceof Error
+  //                       ? error.message
+  //                       : JSON.stringify(error),
+  //                 });
+  //               }
+  //             }
+  //           }}
+  //         />
+  //         <input
+  //           type="button"
+  //           class="pure-button"
+  //           value="Clear"
+  //           onClick={async (e) => {
+  //             e.preventDefault();
+  //             setAmount(undefined);
+  //             setIban(undefined);
+  //             setSubject(undefined);
+  //           }}
+  //         />
+  //       </p>
+  //     </form>
+  //     <p>
+  //       <a
+  //         href="#"
+  //         onClick={(e) => {
+  //           setIsRawPayto(true);
+  //           e.preventDefault();
+  //         }}
+  //       >
+  //         {i18n.str`Want to try the raw payto://-format?`}
+  //       </a>
+  //     </p>
+  //   </div>
+  // );
+
+
+
+  // return (
+  //   <div>
+  //     <p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p>
+
+  //   </div>
+  // );
 }
diff --git a/packages/demobank-ui/src/pages/Test.tsx b/packages/demobank-ui/src/pages/Test.tsx
new file mode 100644
index 000000000..874f7fe68
--- /dev/null
+++ b/packages/demobank-ui/src/pages/Test.tsx
@@ -0,0 +1,5 @@
+import { VNode, h } from "preact";
+
+export function Test(): VNode {
+    return <div >hola</div>
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 83be99d6f..da624f61b 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -65,10 +65,10 @@ export function WalletWithdrawForm({
       trimmedAmountStr == null
         ? i18n.str`required`
         : !parsedAmount
-        ? i18n.str`invalid`
-        : Amounts.cmp(limit, parsedAmount) === -1
-        ? i18n.str`balance is not enough`
-        : undefined,
+          ? i18n.str`invalid`
+          : Amounts.cmp(limit, parsedAmount) === -1
+            ? i18n.str`balance is not enough`
+            : undefined,
   });
 
   return (
diff --git a/packages/demobank-ui/src/scss/DurationPicker.scss b/packages/demobank-ui/src/scss/DurationPicker.scss
deleted file mode 100644
index aa75b9916..000000000
--- a/packages/demobank-ui/src/scss/DurationPicker.scss
+++ /dev/null
@@ -1,70 +0,0 @@
-.rdp-picker {
-  display: flex;
-  height: 175px;
-}
-
-@media (max-width: 400px) {
-  .rdp-picker {
-    width: 250px;
-  }
-}
-
-.rdp-masked-div {
-  overflow: hidden;
-  height: 175px;
-  position: relative;
-}
-
-.rdp-column-container {
-  flex-grow: 1;
-  display: inline-block;
-}
-
-.rdp-column {
-  position: absolute;
-  z-index: 0;
-  width: 100%;
-}
-
-.rdp-reticule {
-  border: 0;
-  border-top: 2px solid rgba(109, 202, 236, 1);
-  height: 2px;
-  position: absolute;
-  width: 80%;
-  margin: 0;
-  z-index: 100;
-  left: 50%;
-  -webkit-transform: translateX(-50%);
-  transform: translateX(-50%);
-}
-
-.rdp-text-overlay {
-  position: absolute;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 35px;
-  font-size: 20px;
-  left: 50%;
-  -webkit-transform: translateX(-50%);
-  transform: translateX(-50%);
-}
-
-.rdp-cell div {
-  font-size: 17px;
-  color: gray;
-  font-style: italic;
-}
-
-.rdp-cell {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 35px;
-  font-size: 18px;
-}
-
-.rdp-center {
-  font-size: 25px;
-}
diff --git a/packages/demobank-ui/src/scss/_aside.scss b/packages/demobank-ui/src/scss/_aside.scss
deleted file mode 100644
index 11809990b..000000000
--- a/packages/demobank-ui/src/scss/_aside.scss
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-html {
-  &.has-aside-left {
-    &.has-aside-expanded {
-      nav.navbar,
-      body {
-        padding-left: $aside-width;
-      }
-    }
-    aside.is-placed-left {
-      display: block;
-    }
-  }
-}
-
-aside.aside.is-expanded {
-  width: $aside-width;
-
-  .menu-list {
-    @include icon-with-update-mark($aside-icon-width);
-
-    span.menu-item-label {
-      display: inline-block;
-    }
-
-    li.is-active {
-      ul {
-        display: block;
-      }
-      background-color: $body-background-color;
-    }
-  }
-}
-
-aside.aside {
-  display: none;
-  position: fixed;
-  top: 0;
-  left: 0;
-  z-index: 40;
-  height: 100vh;
-  padding: 0;
-  box-shadow: $aside-box-shadow;
-  background: $aside-background-color;
-
-  .aside-tools {
-    display: flex;
-    flex-direction: row;
-    width: 100%;
-    background-color: $aside-tools-background-color;
-    color: $aside-tools-color;
-    line-height: $navbar-height;
-    height: $navbar-height;
-    padding-left: $default-padding * 0.5;
-    flex: 1;
-
-    .icon {
-      margin-right: $default-padding * 0.5;
-    }
-  }
-
-  .menu-list {
-    li {
-      a {
-        &.has-dropdown-icon {
-          position: relative;
-          padding-right: $aside-icon-width;
-
-          .dropdown-icon {
-            position: absolute;
-            top: $size-base * 0.5;
-            right: 0;
-          }
-        }
-      }
-      ul {
-        display: none;
-        border-left: 0;
-        background-color: darken($base-color, 2.5%);
-        padding-left: 0;
-        margin: 0 0 $default-padding * 0.5;
-
-        li {
-          a {
-            padding: $default-padding * 0.5 0 $default-padding * 0.5
-              $default-padding * 0.5;
-            font-size: $aside-submenu-font-size;
-
-            &.has-icon {
-              padding-left: 0;
-            }
-            &.is-active {
-              &:not(:hover) {
-                background: transparent;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  .menu-label {
-    padding: 0 $default-padding * 0.5;
-    margin-top: $default-padding * 0.5;
-    margin-bottom: $default-padding * 0.5;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_card.scss b/packages/demobank-ui/src/scss/_card.scss
deleted file mode 100644
index 3f71aeb6a..000000000
--- a/packages/demobank-ui/src/scss/_card.scss
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-.card:not(:last-child) {
-  margin-bottom: $default-padding;
-}
-
-.card {
-  border-radius: $radius-large;
-  border: $card-border;
-
-  &.has-table {
-    .card-content {
-      padding: 0;
-    }
-    .b-table {
-      border-radius: $radius-large;
-      overflow: hidden;
-    }
-  }
-
-  &.is-card-widget {
-    .card-content {
-      padding: $default-padding * 0.5;
-    }
-  }
-
-  .card-header {
-    border-bottom: 1px solid $base-color-light;
-  }
-
-  .card-content {
-    hr {
-      margin-left: $card-content-padding * -1;
-      margin-right: $card-content-padding * -1;
-    }
-  }
-
-  .is-widget-icon {
-    .icon {
-      width: 5rem;
-      height: 5rem;
-    }
-  }
-
-  .is-widget-label {
-    .subtitle {
-      color: $grey;
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_custom-calendar.scss b/packages/demobank-ui/src/scss/_custom-calendar.scss
deleted file mode 100644
index 463cd88d3..000000000
--- a/packages/demobank-ui/src/scss/_custom-calendar.scss
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-:root {
-  --primary-color: #3298dc;
-
-  --primary-text-color-dark: rgba(0, 0, 0, 0.87);
-  --secondary-text-color-dark: rgba(0, 0, 0, 0.57);
-  --disabled-text-color-dark: rgba(0, 0, 0, 0.13);
-
-  --primary-text-color-light: rgba(255, 255, 255, 0.87);
-  --secondary-text-color-light: rgba(255, 255, 255, 0.57);
-  --disabled-text-color-light: rgba(255, 255, 255, 0.13);
-
-  --font-stack: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
-
-  --primary-card-color: #fff;
-  --primary-background-color: #f2f2f2;
-
-  --box-shadow-lvl-1: 0 1px 3px rgba(0, 0, 0, 0.12),
-    0 1px 2px rgba(0, 0, 0, 0.24);
-  --box-shadow-lvl-2: 0 3px 6px rgba(0, 0, 0, 0.16),
-    0 3px 6px rgba(0, 0, 0, 0.23);
-  --box-shadow-lvl-3: 0 10px 20px rgba(0, 0, 0, 0.19),
-    0 6px 6px rgba(0, 0, 0, 0.23);
-  --box-shadow-lvl-4: 0 14px 28px rgba(0, 0, 0, 0.25),
-    0 10px 10px rgba(0, 0, 0, 0.22);
-}
-
-.home .datePicker div {
-  margin-top: 0px;
-  margin-bottom: 0px;
-}
-.datePicker {
-  text-align: left;
-  background: var(--primary-card-color);
-  border-radius: 3px;
-  z-index: 200;
-  position: fixed;
-  height: auto;
-  max-height: 90vh;
-  width: 90vw;
-  max-width: 448px;
-  transform-origin: top left;
-  transition: transform 0.22s ease-in-out, opacity 0.22s ease-in-out;
-  top: 50%;
-  left: 50%;
-  opacity: 0;
-  transform: scale(0) translate(-50%, -50%);
-  user-select: none;
-
-  &.datePicker--opened {
-    opacity: 1;
-    transform: scale(1) translate(-50%, -50%);
-  }
-
-  .datePicker--titles {
-    border-top-left-radius: 3px;
-    border-top-right-radius: 3px;
-    padding: 24px;
-    height: 100px;
-    background: var(--primary-color);
-
-    h2,
-    h3 {
-      cursor: pointer;
-      color: #fff;
-      line-height: 1;
-      padding: 0;
-      margin: 0;
-      font-size: 32px;
-    }
-
-    h3 {
-      color: rgba(255, 255, 255, 0.57);
-      font-size: 18px;
-      padding-bottom: 2px;
-    }
-  }
-
-  nav {
-    padding: 20px;
-    height: 56px;
-
-    h4 {
-      width: calc(100% - 60px);
-      text-align: center;
-      display: inline-block;
-      padding: 0;
-      font-size: 14px;
-      line-height: 24px;
-      margin: 0;
-      position: relative;
-      top: -9px;
-      color: var(--primary-text-color);
-    }
-
-    i {
-      cursor: pointer;
-      color: var(--secondary-text-color);
-      font-size: 26px;
-      user-select: none;
-      border-radius: 50%;
-
-      &:hover {
-        background: var(--disabled-text-color-dark);
-      }
-    }
-  }
-
-  .datePicker--scroll {
-    overflow-y: auto;
-    max-height: calc(90vh - 56px - 100px);
-  }
-
-  .datePicker--calendar {
-    padding: 0 20px;
-
-    .datePicker--dayNames {
-      width: 100%;
-      display: grid;
-      text-align: center;
-
-      // there's probably a better way to do this, but wanted to try out CSS grid
-      grid-template-columns:
-        calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7)
-        calc(100% / 7) calc(100% / 7) calc(100% / 7);
-
-      span {
-        color: var(--secondary-text-color-dark);
-        font-size: 14px;
-        line-height: 42px;
-        display: inline-grid;
-      }
-    }
-
-    .datePicker--days {
-      width: 100%;
-      display: grid;
-      text-align: center;
-      grid-template-columns:
-        calc(100% / 7) calc(100% / 7) calc(100% / 7) calc(100% / 7)
-        calc(100% / 7) calc(100% / 7) calc(100% / 7);
-
-      span {
-        color: var(--primary-text-color-dark);
-        line-height: 42px;
-        font-size: 14px;
-        display: inline-grid;
-        transition: color 0.22s;
-        height: 42px;
-        position: relative;
-        cursor: pointer;
-        user-select: none;
-        border-radius: 50%;
-
-        &::before {
-          content: "";
-          position: absolute;
-          z-index: -1;
-          height: 42px;
-          width: 42px;
-          left: calc(50% - 21px);
-          background: var(--primary-color);
-          border-radius: 50%;
-          transition: transform 0.22s, opacity 0.22s;
-          transform: scale(0);
-          opacity: 0;
-        }
-
-        &[disabled="true"] {
-          cursor: unset;
-        }
-
-        &.datePicker--today {
-          font-weight: 700;
-        }
-
-        &.datePicker--selected {
-          color: rgba(255, 255, 255, 0.87);
-
-          &:before {
-            transform: scale(1);
-            opacity: 1;
-          }
-        }
-      }
-    }
-  }
-
-  .datePicker--selectYear {
-    padding: 0 20px;
-    display: block;
-    width: 100%;
-    text-align: center;
-    max-height: 362px;
-
-    span {
-      display: block;
-      width: 100%;
-      font-size: 24px;
-      margin: 20px auto;
-      cursor: pointer;
-
-      &.selected {
-        font-size: 42px;
-        color: var(--primary-color);
-      }
-    }
-  }
-
-  div.datePicker--actions {
-    width: 100%;
-    padding: 8px;
-    text-align: right;
-
-    button {
-      margin-bottom: 0;
-      font-size: 15px;
-      cursor: pointer;
-      color: var(--primary-text-color);
-      border: none;
-      margin-left: 8px;
-      min-width: 64px;
-      line-height: 36px;
-      background-color: transparent;
-      appearance: none;
-      padding: 0 16px;
-      border-radius: 3px;
-      transition: background-color 0.13s;
-
-      &:hover,
-      &:focus {
-        outline: none;
-        background-color: var(--disabled-text-color-dark);
-      }
-    }
-  }
-}
-
-.datePicker--background {
-  z-index: 199;
-  position: fixed;
-  top: 0;
-  left: 0;
-  bottom: 0;
-  right: 0;
-  background: rgba(0, 0, 0, 0.52);
-  animation: fadeIn 0.22s forwards;
-}
diff --git a/packages/demobank-ui/src/scss/_footer.scss b/packages/demobank-ui/src/scss/_footer.scss
deleted file mode 100644
index 112522ed8..000000000
--- a/packages/demobank-ui/src/scss/_footer.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-footer.footer {
-  .logo {
-    img {
-      width: auto;
-      height: $footer-logo-height;
-    }
-  }
-}
-
-@include mobile {
-  .footer-copyright {
-    text-align: center;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_form.scss b/packages/demobank-ui/src/scss/_form.scss
deleted file mode 100644
index 9d93477fd..000000000
--- a/packages/demobank-ui/src/scss/_form.scss
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-.field {
-  &.has-check {
-    .field-body {
-      margin-top: $default-padding * 0.125;
-    }
-  }
-  .control {
-    .mdi-24px.mdi-set,
-    .mdi-24px.mdi:before {
-      font-size: inherit;
-    }
-  }
-}
-.upload {
-  .upload-draggable {
-    display: block;
-  }
-}
-
-.input,
-.textarea,
-select {
-  box-shadow: none;
-
-  &:focus,
-  &:active {
-    box-shadow: none !important;
-  }
-}
-
-.switch input[type="checkbox"] + .check:before {
-  box-shadow: none;
-}
-
-.switch,
-.b-checkbox.checkbox {
-  input[type="checkbox"] {
-    &:focus + .check,
-    &:focus:checked + .check {
-      box-shadow: none !important;
-    }
-  }
-}
-
-.b-checkbox.checkbox input[type="checkbox"],
-.b-radio.radio input[type="radio"] {
-  & + .check {
-    border: $checkbox-border;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_hero-bar.scss b/packages/demobank-ui/src/scss/_hero-bar.scss
deleted file mode 100644
index 31b7e623e..000000000
--- a/packages/demobank-ui/src/scss/_hero-bar.scss
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-section.hero.is-hero-bar {
-  background-color: $hero-bar-background;
-  border-bottom: $light-border;
-
-  .hero-body {
-    padding: $default-padding;
-
-    .level-item {
-      &.is-hero-avatar-item {
-        margin-right: $default-padding;
-      }
-
-      > div > .level {
-        margin-bottom: $default-padding * 0.5;
-      }
-
-      .subtitle + p {
-        margin-top: $default-padding * 0.5;
-      }
-    }
-
-    .button {
-      &.is-hero-button {
-        background-color: rgba($white, 0.5);
-        font-weight: 300;
-        @include transition(background-color);
-
-        &:hover {
-          background-color: $white;
-        }
-      }
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_loading.scss b/packages/demobank-ui/src/scss/_loading.scss
deleted file mode 100644
index d25bf8048..000000000
--- a/packages/demobank-ui/src/scss/_loading.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-.lds-ring {
-  display: inline-block;
-  position: relative;
-  width: 80px;
-  height: 80px;
-}
-.lds-ring div {
-  box-sizing: border-box;
-  display: block;
-  position: absolute;
-  width: 64px;
-  height: 64px;
-  margin: 8px;
-  border: 8px solid black;
-  border-radius: 50%;
-  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
-  border-color: black transparent transparent transparent;
-}
-.lds-ring div:nth-child(1) {
-  animation-delay: -0.45s;
-}
-.lds-ring div:nth-child(2) {
-  animation-delay: -0.3s;
-}
-.lds-ring div:nth-child(3) {
-  animation-delay: -0.15s;
-}
-@keyframes lds-ring {
-  0% {
-    transform: rotate(0deg);
-  }
-  100% {
-    transform: rotate(360deg);
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_main-section.scss b/packages/demobank-ui/src/scss/_main-section.scss
deleted file mode 100644
index 01edc24bf..000000000
--- a/packages/demobank-ui/src/scss/_main-section.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-section.section.is-main-section {
-  padding-top: $default-padding;
-}
diff --git a/packages/demobank-ui/src/scss/_misc.scss b/packages/demobank-ui/src/scss/_misc.scss
deleted file mode 100644
index 65bd28dbd..000000000
--- a/packages/demobank-ui/src/scss/_misc.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-.is-user-avatar {
-  &.has-max-width {
-    max-width: $size-base * 7;
-  }
-
-  &.is-aligned-center {
-    margin: 0 auto;
-  }
-
-  img {
-    margin: 0 auto;
-    border-radius: $radius-rounded;
-  }
-}
-
-.icon.has-update-mark {
-  position: relative;
-
-  &:after {
-    content: "";
-    width: $icon-update-mark-size;
-    height: $icon-update-mark-size;
-    position: absolute;
-    top: 1px;
-    right: 1px;
-    background-color: $icon-update-mark-color;
-    border-radius: $radius-rounded;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_mixins.scss b/packages/demobank-ui/src/scss/_mixins.scss
deleted file mode 100644
index b52e590e3..000000000
--- a/packages/demobank-ui/src/scss/_mixins.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-@mixin transition($t) {
-  transition: $t 250ms ease-in-out 50ms;
-}
-
-@mixin icon-with-update-mark($icon-base-width) {
-  .icon {
-    width: $icon-base-width;
-
-    &.has-update-mark:after {
-      right: ($icon-base-width / 2) - 0.85;
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_modal.scss b/packages/demobank-ui/src/scss/_modal.scss
deleted file mode 100644
index b3a31ebf1..000000000
--- a/packages/demobank-ui/src/scss/_modal.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-.modal-card {
-  width: $modal-card-width;
-}
-
-.modal-card-foot {
-  background-color: $modal-card-foot-background-color;
-}
-
-@include mobile {
-  .modal .animation-content .modal-card {
-    width: $modal-card-width-mobile;
-    margin: 0 auto;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_nav-bar.scss b/packages/demobank-ui/src/scss/_nav-bar.scss
deleted file mode 100644
index c6dd04263..000000000
--- a/packages/demobank-ui/src/scss/_nav-bar.scss
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-nav.navbar {
-  box-shadow: $navbar-box-shadow;
-
-  .navbar-item {
-    &.has-user-avatar {
-      .is-user-avatar {
-        margin-right: $default-padding * 0.5;
-        display: inline-flex;
-        width: $navbar-avatar-size;
-        height: $navbar-avatar-size;
-      }
-    }
-
-    &.has-divider {
-      border-right: $navbar-divider-border;
-    }
-
-    &.no-left-space {
-      padding-left: 0;
-    }
-
-    &.has-dropdown {
-      padding-right: 0;
-      padding-left: 0;
-
-      .navbar-link {
-        padding-right: $navbar-item-h-padding;
-        padding-left: $navbar-item-h-padding;
-      }
-    }
-
-    &.has-control {
-      padding-top: 0;
-      padding-bottom: 0;
-    }
-
-    .control {
-      .input {
-        color: $navbar-input-color;
-        border: 0;
-        box-shadow: none;
-        background: transparent;
-
-        &::placeholder {
-          color: $navbar-input-placeholder-color;
-        }
-      }
-    }
-  }
-}
-
-@include touch {
-  nav.navbar {
-    display: flex;
-    padding-right: 0;
-
-    .navbar-brand {
-      flex: 1;
-
-      &.is-right {
-        flex: none;
-      }
-    }
-
-    .navbar-item {
-      &.no-left-space-touch {
-        padding-left: 0;
-      }
-    }
-
-    .navbar-menu {
-      position: absolute;
-      width: 100vw;
-      padding-top: 0;
-      top: $navbar-height;
-      left: 0;
-
-      .navbar-item {
-        .icon:first-child {
-          margin-right: $default-padding * 0.5;
-        }
-
-        &.has-dropdown {
-          > .navbar-link {
-            background-color: $white-ter;
-            .icon:last-child {
-              display: none;
-            }
-          }
-        }
-
-        &.has-user-avatar {
-          > .navbar-link {
-            display: flex;
-            align-items: center;
-            padding-top: $default-padding * 0.5;
-            padding-bottom: $default-padding * 0.5;
-          }
-        }
-      }
-    }
-  }
-}
-
-@include desktop {
-  nav.navbar {
-    .navbar-item {
-      padding-right: $navbar-item-h-padding;
-      padding-left: $navbar-item-h-padding;
-
-      &:not(.is-desktop-icon-only) {
-        .icon:first-child {
-          margin-right: $default-padding * 0.5;
-        }
-      }
-      &.is-desktop-icon-only {
-        span:not(.icon) {
-          display: none;
-        }
-      }
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_table.scss b/packages/demobank-ui/src/scss/_table.scss
deleted file mode 100644
index b68d50e4f..000000000
--- a/packages/demobank-ui/src/scss/_table.scss
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-table.table {
-  thead {
-    th {
-      border-bottom-width: 1px;
-    }
-  }
-
-  td,
-  th {
-    &.checkbox-cell {
-      .b-checkbox.checkbox:not(.button) {
-        margin-right: 0;
-        width: 20px;
-
-        .control-label {
-          display: none;
-          padding: 0;
-        }
-      }
-    }
-  }
-
-  td {
-    .image {
-      margin: 0 auto;
-      width: $table-avatar-size;
-      height: $table-avatar-size;
-    }
-
-    &.is-progress-col {
-      min-width: 5rem;
-      vertical-align: middle;
-    }
-  }
-}
-
-.b-table {
-  .table {
-    border: 0;
-    border-radius: 0;
-  }
-
-  /* This stylizes buefy's pagination */
-  .table-wrapper {
-    margin-bottom: 0;
-  }
-
-  .table-wrapper + .level {
-    padding: $notification-padding;
-    padding-left: $card-content-padding;
-    padding-right: $card-content-padding;
-    margin: 0;
-    border-top: $base-color-light;
-    background: $notification-background-color;
-
-    .pagination-link {
-      background: $button-background-color;
-      color: $button-color;
-      border-color: $button-border-color;
-
-      &.is-current {
-        border-color: $button-active-border-color;
-      }
-    }
-
-    .pagination-previous,
-    .pagination-next,
-    .pagination-link {
-      border-color: $button-border-color;
-      color: $base-color;
-
-      &[disabled] {
-        background-color: transparent;
-      }
-    }
-  }
-}
-
-@include mobile {
-  .card {
-    &.has-table {
-      .b-table {
-        .table-wrapper + .level {
-          .level-left + .level-right {
-            margin-top: 0;
-          }
-        }
-      }
-    }
-    &.has-mobile-sort-spaced {
-      .b-table {
-        .field.table-mobile-sort {
-          padding-top: $default-padding * 0.5;
-        }
-      }
-    }
-  }
-  .b-table {
-    .field.table-mobile-sort {
-      padding: 0 $default-padding * 0.5;
-    }
-
-    .table-wrapper.has-mobile-cards {
-      tr {
-        box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1);
-        margin-bottom: 3px !important;
-      }
-      td {
-        &.is-progress-col {
-          span,
-          progress {
-            display: flex;
-            width: 45%;
-            align-items: center;
-            align-self: center;
-          }
-        }
-
-        &.checkbox-cell,
-        &.is-image-cell {
-          border-bottom: 0 !important;
-        }
-
-        &.checkbox-cell,
-        &.is-actions-cell {
-          &:before {
-            display: none;
-          }
-        }
-
-        &.has-no-head-mobile {
-          &:before {
-            display: none;
-          }
-
-          span {
-            display: block;
-            width: 100%;
-          }
-
-          &.is-progress-col {
-            progress {
-              width: 100%;
-            }
-          }
-
-          &.is-image-cell {
-            .image {
-              width: $table-avatar-size-mobile;
-              height: auto;
-              margin: 0 auto $default-padding * 0.25;
-            }
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_theme-default.scss b/packages/demobank-ui/src/scss/_theme-default.scss
deleted file mode 100644
index 538dfd4da..000000000
--- a/packages/demobank-ui/src/scss/_theme-default.scss
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-/* We'll need some initial vars to use here */
-@import "node_modules/bulma/sass/utilities/initial-variables";
-
-/* Base: Size  */
-$size-base: 1rem;
-$default-padding: $size-base * 1.5;
-
-/* Default font */
-$family-sans-serif: "Nunito", sans-serif;
-
-/* Base color */
-$base-color: #2e323a;
-$base-color-light: rgba(24, 28, 33, 0.06);
-
-/* General overrides */
-$primary: $turquoise;
-$body-background-color: #f8f8f8;
-$link: $blue;
-$link-visited: $purple;
-$light-border: 1px solid $base-color-light;
-$hr-height: 1px;
-
-/* NavBar: specifics */
-$navbar-input-color: $grey-darker;
-$navbar-input-placeholder-color: $grey-lighter;
-$navbar-box-shadow: 0 1px 0 rgba(24, 28, 33, 0.04);
-$navbar-divider-border: 1px solid rgba($grey-lighter, 0.25);
-$navbar-item-h-padding: $default-padding * 0.75;
-$navbar-avatar-size: 1.75rem;
-
-/* Aside: Bulma override */
-$menu-item-radius: 0;
-$menu-list-link-padding: $size-base * 0.5 0;
-$menu-label-color: lighten($base-color, 25%);
-$menu-item-color: lighten($base-color, 30%);
-$menu-item-hover-color: $white;
-$menu-item-hover-background-color: darken($base-color, 3.5%);
-$menu-item-active-color: $white;
-$menu-item-active-background-color: darken($base-color, 2.5%);
-
-/* Aside: specifics */
-$aside-width: $size-base * 14;
-$aside-mobile-width: $size-base * 15;
-$aside-icon-width: $size-base * 3;
-$aside-submenu-font-size: $size-base * 0.95;
-$aside-box-shadow: none;
-$aside-background-color: $base-color;
-$aside-tools-background-color: darken($aside-background-color, 10%);
-$aside-tools-color: $white;
-
-/* Title Bar: specifics */
-$title-bar-color: $grey;
-$title-bar-active-color: $black-ter;
-
-/* Hero Bar: specifics */
-$hero-bar-background: $white;
-
-/* Card: Bulma override */
-$card-shadow: none;
-$card-header-shadow: none;
-
-/* Card: specifics */
-$card-border: 1px solid $base-color-light;
-$card-header-border-bottom-color: $base-color-light;
-
-/* Table: Bulma override */
-$table-cell-border: 1px solid $white-bis;
-
-/* Table: specifics */
-$table-avatar-size: $size-base * 1.5;
-$table-avatar-size-mobile: 25vw;
-
-/* Form */
-$checkbox-border: 1px solid $base-color;
-
-/* Modal card: Bulma override */
-$modal-card-head-background-color: $white-ter;
-$modal-card-title-size: $size-base;
-$modal-card-body-padding: $default-padding 20px;
-$modal-card-head-border-bottom: 1px solid $white-ter;
-$modal-card-foot-border-top: 0;
-
-/* Modal card: specifics */
-$modal-card-width: 80vw;
-$modal-card-width-mobile: 90vw;
-$modal-card-foot-background-color: $white-ter;
-
-/* Notification: Bulma override */
-$notification-padding: $default-padding * 0.75 $default-padding;
-
-/* Footer: Bulma override */
-$footer-background-color: $white;
-$footer-padding: $default-padding * 0.33 $default-padding;
-
-/* Footer: specifics */
-$footer-logo-height: $size-base * 2;
-
-/* Progress: Bulma override */
-$progress-bar-background-color: $grey-lighter;
-
-/* Icon: specifics */
-$icon-update-mark-size: $size-base * 0.5;
-$icon-update-mark-color: $yellow;
-
-$input-disabled-border-color: $grey-lighter;
-$table-row-hover-background-color: hsl(0, 0%, 80%);
-
-.menu-list {
-  div {
-    border-radius: $menu-item-radius;
-    color: $menu-item-color;
-    display: block;
-    padding: $menu-list-link-padding;
-  }
-}
diff --git a/packages/demobank-ui/src/scss/_tiles.scss b/packages/demobank-ui/src/scss/_tiles.scss
deleted file mode 100644
index e69d995f0..000000000
--- a/packages/demobank-ui/src/scss/_tiles.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-.is-tiles-wrapper {
-  margin-bottom: $default-padding;
-}
diff --git a/packages/demobank-ui/src/scss/_title-bar.scss b/packages/demobank-ui/src/scss/_title-bar.scss
deleted file mode 100644
index 932f8e65d..000000000
--- a/packages/demobank-ui/src/scss/_title-bar.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-section.section.is-title-bar {
-  padding: $default-padding;
-  border-bottom: $light-border;
-
-  ul {
-    li {
-      display: inline-block;
-      padding: 0 $default-padding * 0.5 0 0;
-      font-size: $default-padding;
-      color: $title-bar-color;
-
-      &:after {
-        display: inline-block;
-        content: "/";
-        padding-left: $default-padding * 0.5;
-      }
-
-      &:last-child {
-        padding-right: 0;
-        font-weight: 900;
-        color: $title-bar-active-color;
-
-        &:after {
-          display: none;
-        }
-      }
-    }
-  }
-}
diff --git a/packages/demobank-ui/src/scss/bank.scss b/packages/demobank-ui/src/scss/bank.scss
deleted file mode 100644
index f8de0a984..000000000
--- a/packages/demobank-ui/src/scss/bank.scss
+++ /dev/null
@@ -1,353 +0,0 @@
-.navcontainer:not(.default-navcontainer) {
-  margin-bottom: 0 !important;
-}
-
-.abort-button {
-  margin-left: 2px;
-  border: 2px solid rgb(0, 120, 231);
-  color: rgb(0, 120, 231);
-  font-size: 87%;
-  margin-top: 1px;
-  background: white;
-}
-
-div.pages-list {
-  margin-top: 15px;
-}
-
-.footer {
-  margin-left: 2em;
-  margin-right: 2em;
-}
-
-.qr-div,
-.login-div,
-.register-div {
-  display: block;
-  text-align: center;
-}
-
-a.page-number {
-  color: blue;
-}
-
-a.current-page-number {
-  color: inherit;
-  background-color: inherit;
-}
-
-.cancelled {
-  text-decoration: line-through;
-}
-
-input[type="number"]::-webkit-outer-spin-button,
-input[type="number"]::-webkit-inner-spin-button {
-  -webkit-appearance: none;
-  margin: 0;
-}
-
-/* This CSS code styles the tab */
-.tab {
-  overflow: hidden;
-}
-
-.top-right {
-  float: right;
-}
-.some-space {
-  display: inline-block;
-  border: 20px;
-  margin-right: 15px;
-  margin-top: 15px;
-}
-
-.tab button {
-  background-color: lightgray;
-  color: black;
-  float: left;
-  border: none;
-  outline: none;
-  cursor: pointer;
-  padding: 18px 19px;
-  border: 2px solid #c1c1c1;
-  transition: 0.5s;
-  font-weight: bold;
-}
-
-.tab button:hover {
-  background-color: yellow;
-  border: 2px solid #c1c1c1;
-  color: black;
-}
-
-.tab button.active {
-  background-color: orange;
-  border: 2px solid #c1c1c1;
-  color: black;
-  font-weight: bold;
-}
-
-.tabcontent {
-  display: none;
-  padding: 8px 16px;
-  border: 2px solid #c1c1c1;
-  width: min-content;
-}
-
-.tabcontent.active {
-  display: block;
-}
-
-input[type="number"] {
-  -moz-appearance: textfield;
-}
-
-#transfer-fields {
-  display: flex;
-  flex-wrap: wrap;
-}
-
-#id_amount {
-  width: 6em;
-  display: inline-block;
-  border-radius: 4px 0px 0px 4px;
-}
-
-/**
- * Amount without the currency,
- * placed left to a .currency-indicator.
- */
-#main .amount {
-  width: 6em;
-  display: inline-block;
-  border-radius: 4px 0px 0px 4px;
-}
-
-input {
-  background-color: inherit;
-}
-
-.large-amount {
-  font-weight: bold;
-  font-size: xxx-large;
-}
-
-.currency {
-  font-style: oblique;
-}
-
-/*
- * Currency indicator to the right of input fields,
- * with non-rounded corners to the left.
- */
-#main .currency-indicator {
-  color: black;
-  border-radius: 4px 0px 0px 4px;
-  position: relative;
-}
-
-#main .fieldlabel {
-  display: block;
-  padding-bottom: 0.5em;
-}
-
-#main .fieldbox {
-  margin-right: 1em;
-  margin-bottom: 0.5em;
-}
-
-#logout-button {
-  display: block;
-  width: fit-content;
-}
-
-.register-form > .pure-form,
-.login-form > .pure-form {
-  background: #4a4a4a;
-  color: #ffffff;
-  display: inline-block;
-  text-align: left;
-  margin-left: auto;
-  margin-right: auto;
-  padding: 16px 16px;
-  border-radius: 8px;
-  width: min-content;
-  .formFieldLabel {
-    margin: 2px 2px;
-  }
-  input[type="text"],
-  input[type="password"] {
-    border: none;
-    border-radius: 4px;
-    background: #6a6a6a;
-    color: #fefefe;
-    box-shadow: none;
-  }
-  input[placeholder="Password"][type="password"] {
-    margin-bottom: 8px;
-  }
-  .btn-register,
-  .btn-login {
-    float: left;
-  }
-  .btn-cancel {
-    float: right;
-  }
-  h2 {
-    margin-top: 0;
-    margin-bottom: 10px;
-  }
-}
-
-.challenge-div {
-  display: block;
-  text-align: center;
-}
-
-.challenge-form > .pure-form {
-  background: #4a4a4a;
-  color: #ffffff;
-  display: inline-block;
-  text-align: left;
-  margin-left: auto;
-  margin-right: auto;
-  padding: 16px 16px;
-  border-radius: 8px;
-  width: min-content;
-  .formFieldLabel {
-    margin: 2px 2px;
-  }
-  input[type="text"] {
-    border: none;
-    border-radius: 4px;
-    background: #6a6a6a;
-    color: #fefefe;
-    box-shadow: none;
-  }
-  .btn-confirm {
-    float: left;
-  }
-  .btn-cancel {
-    float: right;
-  }
-  h2 {
-    margin-top: 0;
-    margin-bottom: 10px;
-  }
-}
-
-.wire-transfer-form > .pure-form,
-.payto-form > .pure-form,
-.reserve-form > .pure-form {
-  background: #4a4a4a;
-  color: #ffffff;
-  display: inline-block;
-  text-align: left;
-  margin-left: auto;
-  margin-right: auto;
-  padding: 16px 16px;
-  border-radius: 8px;
-  width: min-content;
-  .formFieldLabel {
-    margin: 2px 2px;
-  }
-  input[type="text"] {
-    border: none;
-    border-radius: 4px;
-    background: #6a6a6a;
-    color: #fefefe;
-    box-shadow: none;
-  }
-}
-
-html {
-  background: #ffffff;
-  color: #2a2a2a;
-}
-
-.hint {
-  scale: 0.7;
-}
-h1.nav {
-  text-align: center;
-}
-
-.pure-form > fieldset > label {
-  display: block;
-}
-.pure-form > fieldset > input[disabled] {
-  color: black !important;
-}
-.pure-form > fieldset > div > input[disabled] {
-  color: black !important;
-}
-
-.pure-form > fieldset > div.channel > div {
-  display: inline-block;
-  margin: 1em;
-  border: 1px black solid;
-  width: fit-content;
-  padding: 0.4em;
-  cursor: pointer;
-}
-
-.button-success {
-  background: rgb(28, 184, 65);
-  /* this is a green */
-}
-
-.button-error {
-  background: rgb(202, 60, 60);
-  /* this is a maroon */
-}
-
-.button-warning {
-  background: rgb(223, 117, 20);
-  /* this is an orange */
-}
-
-.button-secondary {
-  background: rgb(66, 184, 221);
-  /* this is a light blue */
-}
-
-[name=wire-transfer-form] > input {
-  margin-bottom: 1em;
-  
-}
-
-.lds-ring {
-  display: inline-block;
-  position: relative;
-  width: 80px;
-  height: 80px;
-}
-.lds-ring div {
-  box-sizing: border-box;
-  display: block;
-  position: absolute;
-  width: 64px;
-  height: 64px;
-  margin: 8px;
-  border: 8px solid black;
-  border-radius: 50%;
-  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
-  border-color: black transparent transparent transparent;
-}
-.lds-ring div:nth-child(1) {
-  animation-delay: -0.45s;
-}
-.lds-ring div:nth-child(2) {
-  animation-delay: -0.3s;
-}
-.lds-ring div:nth-child(3) {
-  animation-delay: -0.15s;
-}
-@keyframes lds-ring {
-  0% {
-    transform: rotate(0deg);
-  }
-  100% {
-    transform: rotate(360deg);
-  }
-}
diff --git a/packages/demobank-ui/src/scss/colors-bank.scss b/packages/demobank-ui/src/scss/colors-bank.scss
deleted file mode 100644
index e11bbe203..000000000
--- a/packages/demobank-ui/src/scss/colors-bank.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-nav,
-nav a,
-nav span,
-.navcontainer,
-nav button,
-.demobar,
-.navbtn {
-  color: white;
-  background: #a00000;
-}
-
-nav a.active,
-nav button,
-nav span.active,
-.navbtn.active {
-  background-color: #7a0606;
-}
-
-nav a.active:hover,
-nav span.active:hover,
-.navbtn.active:hover,
-nav button:hover,
-nav a:hover,
-nav span:hover,
-.navbtn:hover {
-  background: #df3d3d;
-}
-
-nav a.navbtn.langbtn:focus {
-  background-color: #df3d3d;
-}
diff --git a/packages/demobank-ui/src/scss/demo.scss b/packages/demobank-ui/src/scss/demo.scss
deleted file mode 100644
index c2d9fa903..000000000
--- a/packages/demobank-ui/src/scss/demo.scss
+++ /dev/null
@@ -1,167 +0,0 @@
-@charset "UTF-8";
-/*
-Style common to all demo pages.
-
-Colors:
-- #1e2739 (dark blue)
-- #0042b2 (default blue)
-- #3daee9 (highlight blue)
-*/
-
-.demobar h1 {
-  text-align: center;
-}
-
-.demobar > p {
-  padding: 0.5em;
-}
-
-.demobar a,
-.demobar a:visited {
-  color: inherit;
-  background-color: inherit;
-}
-
-.tt {
-  font-family: "Lucida Console", Monaco, monospace;
-}
-
-.informational-ok {
-  background: lightgreen;
-  border-radius: 1em;
-  padding: 0.5em;
-}
-
-.informational-fail {
-  background: lightpink;
-  border-radius: 1em;
-  padding: 0.5em;
-}
-
-.content {
-  margin-left: 1em;
-  margin-right: 1em;
-  overflow-x: auto;
-}
-
-.demobar {
-  overflow-x: auto;
-  background-color: #0042b2;
-  color: white;
-}
-
-body {
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-
-.navcontainer {
-  background: #0042b2;
-  margin-bottom: 50px;
-  width: 100%;
-  color: white;
-  // position: -webkit-sticky;
-  // position: sticky;
-  top: 0px;
-  width: 100vw;
-  backdrop-filter: blur(10px);
-  opacity: 1;
-  z-index: 100;
-}
-
-nav {
-  // left: 1vw;
-  position: relative;
-  background: #0042b2;
-  z-index: 100;
-}
-
-nav a,
-nav button,
-nav span,
-.navbtn {
-  border: none;
-  color: white;
-  text-align: center;
-  // text-decoration: none;
-  display: inline-block;
-  font-size: 16px;
-  background: #0042b2;
-  height: inherit;
-}
-
-nav a,
-nav button,
-nav span,
-.navbtn {
-  padding: 8px;
-}
-    
-
-nav a:hover,
-nav span:hover,
-.navbtn:hover {
-  background: #3daee9;
-}
-
-nav a.active,
-nav span.active,
-.navbtn.active {
-  background-color: #1e2739;
-}
-
-nav a.active:hover,
-nav button.active:hover,
-nav span.active:hover,
-.navbtn.active:hover {
-  background: #3daee9;
-}
-
-nav a,
-nav span,
-.navbtn {
-  cursor: pointer;
-}
-
-nav .right {
-  float: right;
-  margin-right: 5vw;
-}
-nav .hide div.nav {
-  display: none;
-}
-// nav .right div.nav:hover {
-//   display: block;
-// }
-
-// nav .right:hover div.nav {
-//   display: block;
-// }
-
-.langbtn {
-  width: 100px;
-  text-align: left;
-}
-
-.skip {
-  position: absolute;
-  left: -10000px;
-  top: auto;
-  width: 1px;
-  height: 1px;
-  overflow: hidden;
-}
-
-.skip:focus {
-  position: static;
-  width: auto;
-  height: auto;
-}
-
-.demolist > a {
-  margin: 8px;
-}
-
-.buttons-account input.pure-button {
-  margin: 8px;
-}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf b/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf
deleted file mode 100644
index 7665ee336..000000000
Binary files a/packages/demobank-ui/src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf and /dev/null differ
diff --git a/packages/demobank-ui/src/scss/fonts/nunito.css b/packages/demobank-ui/src/scss/fonts/nunito.css
deleted file mode 100644
index 8d45df9a1..000000000
--- a/packages/demobank-ui/src/scss/fonts/nunito.css
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-@font-face {
-  font-family: "Nunito";
-  font-style: normal;
-  font-weight: 400;
-  src: url(./XRXV3I6Li01BKofINeaE.ttf) format("truetype");
-}
diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot
deleted file mode 100644
index ab6b25ded..000000000
Binary files a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.eot and /dev/null differ
diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf
deleted file mode 100644
index 824be10fa..000000000
Binary files a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.ttf and /dev/null differ
diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff
deleted file mode 100644
index 7e087c1de..000000000
Binary files a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff and /dev/null differ
diff --git a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 b/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2
deleted file mode 100644
index b5caa4ddc..000000000
Binary files a/packages/demobank-ui/src/scss/icons/fonts/materialdesignicons-webfont-4.9.95.woff2 and /dev/null differ
diff --git a/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css b/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css
deleted file mode 100644
index 2b8a2b244..000000000
--- a/packages/demobank-ui/src/scss/icons/materialdesignicons-4.9.95.min.css
+++ /dev/null
@@ -1,15109 +0,0 @@
-@font-face {
-  font-family: "Material Design Icons";
-  src: url("./fonts/materialdesignicons-webfont-4.9.95.eot");
-  src: url("./fonts/materialdesignicons-webfont-4.9.95.woff2") format("woff2"),
-    url("./fonts/materialdesignicons-webfont-4.9.95.woff") format("woff"),
-    url("./fonts/materialdesignicons-webfont-4.9.95.ttf") format("truetype");
-  font-weight: normal;
-  font-style: normal;
-}
-.mdi:before,
-.mdi-set {
-  display: inline-block;
-  font: normal normal normal 24px/1 "Material Design Icons";
-  font-size: inherit;
-  text-rendering: auto;
-  line-height: inherit;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-.mdi-ab-testing::before {
-  content: "\F001C";
-}
-.mdi-abjad-arabic::before {
-  content: "\F0353";
-}
-.mdi-abjad-hebrew::before {
-  content: "\F0354";
-}
-.mdi-abugida-devanagari::before {
-  content: "\F0355";
-}
-.mdi-abugida-thai::before {
-  content: "\F0356";
-}
-.mdi-access-point::before {
-  content: "\F002";
-}
-.mdi-access-point-network::before {
-  content: "\F003";
-}
-.mdi-access-point-network-off::before {
-  content: "\FBBD";
-}
-.mdi-account::before {
-  content: "\F004";
-}
-.mdi-account-alert::before {
-  content: "\F005";
-}
-.mdi-account-alert-outline::before {
-  content: "\FB2C";
-}
-.mdi-account-arrow-left::before {
-  content: "\FB2D";
-}
-.mdi-account-arrow-left-outline::before {
-  content: "\FB2E";
-}
-.mdi-account-arrow-right::before {
-  content: "\FB2F";
-}
-.mdi-account-arrow-right-outline::before {
-  content: "\FB30";
-}
-.mdi-account-badge::before {
-  content: "\FD83";
-}
-.mdi-account-badge-alert::before {
-  content: "\FD84";
-}
-.mdi-account-badge-alert-outline::before {
-  content: "\FD85";
-}
-.mdi-account-badge-horizontal::before {
-  content: "\FDF0";
-}
-.mdi-account-badge-horizontal-outline::before {
-  content: "\FDF1";
-}
-.mdi-account-badge-outline::before {
-  content: "\FD86";
-}
-.mdi-account-box::before {
-  content: "\F006";
-}
-.mdi-account-box-multiple::before {
-  content: "\F933";
-}
-.mdi-account-box-multiple-outline::before {
-  content: "\F002C";
-}
-.mdi-account-box-outline::before {
-  content: "\F007";
-}
-.mdi-account-cancel::before {
-  content: "\F030A";
-}
-.mdi-account-cancel-outline::before {
-  content: "\F030B";
-}
-.mdi-account-card-details::before {
-  content: "\F5D2";
-}
-.mdi-account-card-details-outline::before {
-  content: "\FD87";
-}
-.mdi-account-cash::before {
-  content: "\F00C2";
-}
-.mdi-account-cash-outline::before {
-  content: "\F00C3";
-}
-.mdi-account-check::before {
-  content: "\F008";
-}
-.mdi-account-check-outline::before {
-  content: "\FBBE";
-}
-.mdi-account-child::before {
-  content: "\FA88";
-}
-.mdi-account-child-circle::before {
-  content: "\FA89";
-}
-.mdi-account-child-outline::before {
-  content: "\F00F3";
-}
-.mdi-account-circle::before {
-  content: "\F009";
-}
-.mdi-account-circle-outline::before {
-  content: "\FB31";
-}
-.mdi-account-clock::before {
-  content: "\FB32";
-}
-.mdi-account-clock-outline::before {
-  content: "\FB33";
-}
-.mdi-account-cog::before {
-  content: "\F039B";
-}
-.mdi-account-cog-outline::before {
-  content: "\F039C";
-}
-.mdi-account-convert::before {
-  content: "\F00A";
-}
-.mdi-account-convert-outline::before {
-  content: "\F032C";
-}
-.mdi-account-details::before {
-  content: "\F631";
-}
-.mdi-account-details-outline::before {
-  content: "\F039D";
-}
-.mdi-account-edit::before {
-  content: "\F6BB";
-}
-.mdi-account-edit-outline::before {
-  content: "\F001D";
-}
-.mdi-account-group::before {
-  content: "\F848";
-}
-.mdi-account-group-outline::before {
-  content: "\FB34";
-}
-.mdi-account-heart::before {
-  content: "\F898";
-}
-.mdi-account-heart-outline::before {
-  content: "\FBBF";
-}
-.mdi-account-key::before {
-  content: "\F00B";
-}
-.mdi-account-key-outline::before {
-  content: "\FBC0";
-}
-.mdi-account-lock::before {
-  content: "\F0189";
-}
-.mdi-account-lock-outline::before {
-  content: "\F018A";
-}
-.mdi-account-minus::before {
-  content: "\F00D";
-}
-.mdi-account-minus-outline::before {
-  content: "\FAEB";
-}
-.mdi-account-multiple::before {
-  content: "\F00E";
-}
-.mdi-account-multiple-check::before {
-  content: "\F8C4";
-}
-.mdi-account-multiple-check-outline::before {
-  content: "\F0229";
-}
-.mdi-account-multiple-minus::before {
-  content: "\F5D3";
-}
-.mdi-account-multiple-minus-outline::before {
-  content: "\FBC1";
-}
-.mdi-account-multiple-outline::before {
-  content: "\F00F";
-}
-.mdi-account-multiple-plus::before {
-  content: "\F010";
-}
-.mdi-account-multiple-plus-outline::before {
-  content: "\F7FF";
-}
-.mdi-account-multiple-remove::before {
-  content: "\F0235";
-}
-.mdi-account-multiple-remove-outline::before {
-  content: "\F0236";
-}
-.mdi-account-network::before {
-  content: "\F011";
-}
-.mdi-account-network-outline::before {
-  content: "\FBC2";
-}
-.mdi-account-off::before {
-  content: "\F012";
-}
-.mdi-account-off-outline::before {
-  content: "\FBC3";
-}
-.mdi-account-outline::before {
-  content: "\F013";
-}
-.mdi-account-plus::before {
-  content: "\F014";
-}
-.mdi-account-plus-outline::before {
-  content: "\F800";
-}
-.mdi-account-question::before {
-  content: "\FB35";
-}
-.mdi-account-question-outline::before {
-  content: "\FB36";
-}
-.mdi-account-remove::before {
-  content: "\F015";
-}
-.mdi-account-remove-outline::before {
-  content: "\FAEC";
-}
-.mdi-account-search::before {
-  content: "\F016";
-}
-.mdi-account-search-outline::before {
-  content: "\F934";
-}
-.mdi-account-settings::before {
-  content: "\F630";
-}
-.mdi-account-settings-outline::before {
-  content: "\F00F4";
-}
-.mdi-account-star::before {
-  content: "\F017";
-}
-.mdi-account-star-outline::before {
-  content: "\FBC4";
-}
-.mdi-account-supervisor::before {
-  content: "\FA8A";
-}
-.mdi-account-supervisor-circle::before {
-  content: "\FA8B";
-}
-.mdi-account-supervisor-outline::before {
-  content: "\F0158";
-}
-.mdi-account-switch::before {
-  content: "\F019";
-}
-.mdi-account-tie::before {
-  content: "\FCBF";
-}
-.mdi-account-tie-outline::before {
-  content: "\F00F5";
-}
-.mdi-account-tie-voice::before {
-  content: "\F0333";
-}
-.mdi-account-tie-voice-off::before {
-  content: "\F0335";
-}
-.mdi-account-tie-voice-off-outline::before {
-  content: "\F0336";
-}
-.mdi-account-tie-voice-outline::before {
-  content: "\F0334";
-}
-.mdi-accusoft::before {
-  content: "\F849";
-}
-.mdi-adjust::before {
-  content: "\F01A";
-}
-.mdi-adobe::before {
-  content: "\F935";
-}
-.mdi-adobe-acrobat::before {
-  content: "\FFBD";
-}
-.mdi-air-conditioner::before {
-  content: "\F01B";
-}
-.mdi-air-filter::before {
-  content: "\FD1F";
-}
-.mdi-air-horn::before {
-  content: "\FD88";
-}
-.mdi-air-humidifier::before {
-  content: "\F00C4";
-}
-.mdi-air-purifier::before {
-  content: "\FD20";
-}
-.mdi-airbag::before {
-  content: "\FBC5";
-}
-.mdi-airballoon::before {
-  content: "\F01C";
-}
-.mdi-airballoon-outline::before {
-  content: "\F002D";
-}
-.mdi-airplane::before {
-  content: "\F01D";
-}
-.mdi-airplane-landing::before {
-  content: "\F5D4";
-}
-.mdi-airplane-off::before {
-  content: "\F01E";
-}
-.mdi-airplane-takeoff::before {
-  content: "\F5D5";
-}
-.mdi-airplay::before {
-  content: "\F01F";
-}
-.mdi-airport::before {
-  content: "\F84A";
-}
-.mdi-alarm::before {
-  content: "\F020";
-}
-.mdi-alarm-bell::before {
-  content: "\F78D";
-}
-.mdi-alarm-check::before {
-  content: "\F021";
-}
-.mdi-alarm-light::before {
-  content: "\F78E";
-}
-.mdi-alarm-light-outline::before {
-  content: "\FBC6";
-}
-.mdi-alarm-multiple::before {
-  content: "\F022";
-}
-.mdi-alarm-note::before {
-  content: "\FE8E";
-}
-.mdi-alarm-note-off::before {
-  content: "\FE8F";
-}
-.mdi-alarm-off::before {
-  content: "\F023";
-}
-.mdi-alarm-plus::before {
-  content: "\F024";
-}
-.mdi-alarm-snooze::before {
-  content: "\F68D";
-}
-.mdi-album::before {
-  content: "\F025";
-}
-.mdi-alert::before {
-  content: "\F026";
-}
-.mdi-alert-box::before {
-  content: "\F027";
-}
-.mdi-alert-box-outline::before {
-  content: "\FCC0";
-}
-.mdi-alert-circle::before {
-  content: "\F028";
-}
-.mdi-alert-circle-check::before {
-  content: "\F0218";
-}
-.mdi-alert-circle-check-outline::before {
-  content: "\F0219";
-}
-.mdi-alert-circle-outline::before {
-  content: "\F5D6";
-}
-.mdi-alert-decagram::before {
-  content: "\F6BC";
-}
-.mdi-alert-decagram-outline::before {
-  content: "\FCC1";
-}
-.mdi-alert-octagon::before {
-  content: "\F029";
-}
-.mdi-alert-octagon-outline::before {
-  content: "\FCC2";
-}
-.mdi-alert-octagram::before {
-  content: "\F766";
-}
-.mdi-alert-octagram-outline::before {
-  content: "\FCC3";
-}
-.mdi-alert-outline::before {
-  content: "\F02A";
-}
-.mdi-alert-rhombus::before {
-  content: "\F01F9";
-}
-.mdi-alert-rhombus-outline::before {
-  content: "\F01FA";
-}
-.mdi-alien::before {
-  content: "\F899";
-}
-.mdi-alien-outline::before {
-  content: "\F00F6";
-}
-.mdi-align-horizontal-center::before {
-  content: "\F01EE";
-}
-.mdi-align-horizontal-left::before {
-  content: "\F01ED";
-}
-.mdi-align-horizontal-right::before {
-  content: "\F01EF";
-}
-.mdi-align-vertical-bottom::before {
-  content: "\F01F0";
-}
-.mdi-align-vertical-center::before {
-  content: "\F01F1";
-}
-.mdi-align-vertical-top::before {
-  content: "\F01F2";
-}
-.mdi-all-inclusive::before {
-  content: "\F6BD";
-}
-.mdi-allergy::before {
-  content: "\F0283";
-}
-.mdi-alpha::before {
-  content: "\F02B";
-}
-.mdi-alpha-a::before {
-  content: "\41";
-}
-.mdi-alpha-a-box::before {
-  content: "\FAED";
-}
-.mdi-alpha-a-box-outline::before {
-  content: "\FBC7";
-}
-.mdi-alpha-a-circle::before {
-  content: "\FBC8";
-}
-.mdi-alpha-a-circle-outline::before {
-  content: "\FBC9";
-}
-.mdi-alpha-b::before {
-  content: "\42";
-}
-.mdi-alpha-b-box::before {
-  content: "\FAEE";
-}
-.mdi-alpha-b-box-outline::before {
-  content: "\FBCA";
-}
-.mdi-alpha-b-circle::before {
-  content: "\FBCB";
-}
-.mdi-alpha-b-circle-outline::before {
-  content: "\FBCC";
-}
-.mdi-alpha-c::before {
-  content: "\43";
-}
-.mdi-alpha-c-box::before {
-  content: "\FAEF";
-}
-.mdi-alpha-c-box-outline::before {
-  content: "\FBCD";
-}
-.mdi-alpha-c-circle::before {
-  content: "\FBCE";
-}
-.mdi-alpha-c-circle-outline::before {
-  content: "\FBCF";
-}
-.mdi-alpha-d::before {
-  content: "\44";
-}
-.mdi-alpha-d-box::before {
-  content: "\FAF0";
-}
-.mdi-alpha-d-box-outline::before {
-  content: "\FBD0";
-}
-.mdi-alpha-d-circle::before {
-  content: "\FBD1";
-}
-.mdi-alpha-d-circle-outline::before {
-  content: "\FBD2";
-}
-.mdi-alpha-e::before {
-  content: "\45";
-}
-.mdi-alpha-e-box::before {
-  content: "\FAF1";
-}
-.mdi-alpha-e-box-outline::before {
-  content: "\FBD3";
-}
-.mdi-alpha-e-circle::before {
-  content: "\FBD4";
-}
-.mdi-alpha-e-circle-outline::before {
-  content: "\FBD5";
-}
-.mdi-alpha-f::before {
-  content: "\46";
-}
-.mdi-alpha-f-box::before {
-  content: "\FAF2";
-}
-.mdi-alpha-f-box-outline::before {
-  content: "\FBD6";
-}
-.mdi-alpha-f-circle::before {
-  content: "\FBD7";
-}
-.mdi-alpha-f-circle-outline::before {
-  content: "\FBD8";
-}
-.mdi-alpha-g::before {
-  content: "\47";
-}
-.mdi-alpha-g-box::before {
-  content: "\FAF3";
-}
-.mdi-alpha-g-box-outline::before {
-  content: "\FBD9";
-}
-.mdi-alpha-g-circle::before {
-  content: "\FBDA";
-}
-.mdi-alpha-g-circle-outline::before {
-  content: "\FBDB";
-}
-.mdi-alpha-h::before {
-  content: "\48";
-}
-.mdi-alpha-h-box::before {
-  content: "\FAF4";
-}
-.mdi-alpha-h-box-outline::before {
-  content: "\FBDC";
-}
-.mdi-alpha-h-circle::before {
-  content: "\FBDD";
-}
-.mdi-alpha-h-circle-outline::before {
-  content: "\FBDE";
-}
-.mdi-alpha-i::before {
-  content: "\49";
-}
-.mdi-alpha-i-box::before {
-  content: "\FAF5";
-}
-.mdi-alpha-i-box-outline::before {
-  content: "\FBDF";
-}
-.mdi-alpha-i-circle::before {
-  content: "\FBE0";
-}
-.mdi-alpha-i-circle-outline::before {
-  content: "\FBE1";
-}
-.mdi-alpha-j::before {
-  content: "\4A";
-}
-.mdi-alpha-j-box::before {
-  content: "\FAF6";
-}
-.mdi-alpha-j-box-outline::before {
-  content: "\FBE2";
-}
-.mdi-alpha-j-circle::before {
-  content: "\FBE3";
-}
-.mdi-alpha-j-circle-outline::before {
-  content: "\FBE4";
-}
-.mdi-alpha-k::before {
-  content: "\4B";
-}
-.mdi-alpha-k-box::before {
-  content: "\FAF7";
-}
-.mdi-alpha-k-box-outline::before {
-  content: "\FBE5";
-}
-.mdi-alpha-k-circle::before {
-  content: "\FBE6";
-}
-.mdi-alpha-k-circle-outline::before {
-  content: "\FBE7";
-}
-.mdi-alpha-l::before {
-  content: "\4C";
-}
-.mdi-alpha-l-box::before {
-  content: "\FAF8";
-}
-.mdi-alpha-l-box-outline::before {
-  content: "\FBE8";
-}
-.mdi-alpha-l-circle::before {
-  content: "\FBE9";
-}
-.mdi-alpha-l-circle-outline::before {
-  content: "\FBEA";
-}
-.mdi-alpha-m::before {
-  content: "\4D";
-}
-.mdi-alpha-m-box::before {
-  content: "\FAF9";
-}
-.mdi-alpha-m-box-outline::before {
-  content: "\FBEB";
-}
-.mdi-alpha-m-circle::before {
-  content: "\FBEC";
-}
-.mdi-alpha-m-circle-outline::before {
-  content: "\FBED";
-}
-.mdi-alpha-n::before {
-  content: "\4E";
-}
-.mdi-alpha-n-box::before {
-  content: "\FAFA";
-}
-.mdi-alpha-n-box-outline::before {
-  content: "\FBEE";
-}
-.mdi-alpha-n-circle::before {
-  content: "\FBEF";
-}
-.mdi-alpha-n-circle-outline::before {
-  content: "\FBF0";
-}
-.mdi-alpha-o::before {
-  content: "\4F";
-}
-.mdi-alpha-o-box::before {
-  content: "\FAFB";
-}
-.mdi-alpha-o-box-outline::before {
-  content: "\FBF1";
-}
-.mdi-alpha-o-circle::before {
-  content: "\FBF2";
-}
-.mdi-alpha-o-circle-outline::before {
-  content: "\FBF3";
-}
-.mdi-alpha-p::before {
-  content: "\50";
-}
-.mdi-alpha-p-box::before {
-  content: "\FAFC";
-}
-.mdi-alpha-p-box-outline::before {
-  content: "\FBF4";
-}
-.mdi-alpha-p-circle::before {
-  content: "\FBF5";
-}
-.mdi-alpha-p-circle-outline::before {
-  content: "\FBF6";
-}
-.mdi-alpha-q::before {
-  content: "\51";
-}
-.mdi-alpha-q-box::before {
-  content: "\FAFD";
-}
-.mdi-alpha-q-box-outline::before {
-  content: "\FBF7";
-}
-.mdi-alpha-q-circle::before {
-  content: "\FBF8";
-}
-.mdi-alpha-q-circle-outline::before {
-  content: "\FBF9";
-}
-.mdi-alpha-r::before {
-  content: "\52";
-}
-.mdi-alpha-r-box::before {
-  content: "\FAFE";
-}
-.mdi-alpha-r-box-outline::before {
-  content: "\FBFA";
-}
-.mdi-alpha-r-circle::before {
-  content: "\FBFB";
-}
-.mdi-alpha-r-circle-outline::before {
-  content: "\FBFC";
-}
-.mdi-alpha-s::before {
-  content: "\53";
-}
-.mdi-alpha-s-box::before {
-  content: "\FAFF";
-}
-.mdi-alpha-s-box-outline::before {
-  content: "\FBFD";
-}
-.mdi-alpha-s-circle::before {
-  content: "\FBFE";
-}
-.mdi-alpha-s-circle-outline::before {
-  content: "\FBFF";
-}
-.mdi-alpha-t::before {
-  content: "\54";
-}
-.mdi-alpha-t-box::before {
-  content: "\FB00";
-}
-.mdi-alpha-t-box-outline::before {
-  content: "\FC00";
-}
-.mdi-alpha-t-circle::before {
-  content: "\FC01";
-}
-.mdi-alpha-t-circle-outline::before {
-  content: "\FC02";
-}
-.mdi-alpha-u::before {
-  content: "\55";
-}
-.mdi-alpha-u-box::before {
-  content: "\FB01";
-}
-.mdi-alpha-u-box-outline::before {
-  content: "\FC03";
-}
-.mdi-alpha-u-circle::before {
-  content: "\FC04";
-}
-.mdi-alpha-u-circle-outline::before {
-  content: "\FC05";
-}
-.mdi-alpha-v::before {
-  content: "\56";
-}
-.mdi-alpha-v-box::before {
-  content: "\FB02";
-}
-.mdi-alpha-v-box-outline::before {
-  content: "\FC06";
-}
-.mdi-alpha-v-circle::before {
-  content: "\FC07";
-}
-.mdi-alpha-v-circle-outline::before {
-  content: "\FC08";
-}
-.mdi-alpha-w::before {
-  content: "\57";
-}
-.mdi-alpha-w-box::before {
-  content: "\FB03";
-}
-.mdi-alpha-w-box-outline::before {
-  content: "\FC09";
-}
-.mdi-alpha-w-circle::before {
-  content: "\FC0A";
-}
-.mdi-alpha-w-circle-outline::before {
-  content: "\FC0B";
-}
-.mdi-alpha-x::before {
-  content: "\58";
-}
-.mdi-alpha-x-box::before {
-  content: "\FB04";
-}
-.mdi-alpha-x-box-outline::before {
-  content: "\FC0C";
-}
-.mdi-alpha-x-circle::before {
-  content: "\FC0D";
-}
-.mdi-alpha-x-circle-outline::before {
-  content: "\FC0E";
-}
-.mdi-alpha-y::before {
-  content: "\59";
-}
-.mdi-alpha-y-box::before {
-  content: "\FB05";
-}
-.mdi-alpha-y-box-outline::before {
-  content: "\FC0F";
-}
-.mdi-alpha-y-circle::before {
-  content: "\FC10";
-}
-.mdi-alpha-y-circle-outline::before {
-  content: "\FC11";
-}
-.mdi-alpha-z::before {
-  content: "\5A";
-}
-.mdi-alpha-z-box::before {
-  content: "\FB06";
-}
-.mdi-alpha-z-box-outline::before {
-  content: "\FC12";
-}
-.mdi-alpha-z-circle::before {
-  content: "\FC13";
-}
-.mdi-alpha-z-circle-outline::before {
-  content: "\FC14";
-}
-.mdi-alphabet-aurebesh::before {
-  content: "\F0357";
-}
-.mdi-alphabet-cyrillic::before {
-  content: "\F0358";
-}
-.mdi-alphabet-greek::before {
-  content: "\F0359";
-}
-.mdi-alphabet-latin::before {
-  content: "\F035A";
-}
-.mdi-alphabet-piqad::before {
-  content: "\F035B";
-}
-.mdi-alphabet-tengwar::before {
-  content: "\F0362";
-}
-.mdi-alphabetical::before {
-  content: "\F02C";
-}
-.mdi-alphabetical-off::before {
-  content: "\F002E";
-}
-.mdi-alphabetical-variant::before {
-  content: "\F002F";
-}
-.mdi-alphabetical-variant-off::before {
-  content: "\F0030";
-}
-.mdi-altimeter::before {
-  content: "\F5D7";
-}
-.mdi-amazon::before {
-  content: "\F02D";
-}
-.mdi-amazon-alexa::before {
-  content: "\F8C5";
-}
-.mdi-amazon-drive::before {
-  content: "\F02E";
-}
-.mdi-ambulance::before {
-  content: "\F02F";
-}
-.mdi-ammunition::before {
-  content: "\FCC4";
-}
-.mdi-ampersand::before {
-  content: "\FA8C";
-}
-.mdi-amplifier::before {
-  content: "\F030";
-}
-.mdi-amplifier-off::before {
-  content: "\F01E0";
-}
-.mdi-anchor::before {
-  content: "\F031";
-}
-.mdi-android::before {
-  content: "\F032";
-}
-.mdi-android-auto::before {
-  content: "\FA8D";
-}
-.mdi-android-debug-bridge::before {
-  content: "\F033";
-}
-.mdi-android-head::before {
-  content: "\F78F";
-}
-.mdi-android-messages::before {
-  content: "\FD21";
-}
-.mdi-android-studio::before {
-  content: "\F034";
-}
-.mdi-angle-acute::before {
-  content: "\F936";
-}
-.mdi-angle-obtuse::before {
-  content: "\F937";
-}
-.mdi-angle-right::before {
-  content: "\F938";
-}
-.mdi-angular::before {
-  content: "\F6B1";
-}
-.mdi-angularjs::before {
-  content: "\F6BE";
-}
-.mdi-animation::before {
-  content: "\F5D8";
-}
-.mdi-animation-outline::before {
-  content: "\FA8E";
-}
-.mdi-animation-play::before {
-  content: "\F939";
-}
-.mdi-animation-play-outline::before {
-  content: "\FA8F";
-}
-.mdi-ansible::before {
-  content: "\F00C5";
-}
-.mdi-antenna::before {
-  content: "\F0144";
-}
-.mdi-anvil::before {
-  content: "\F89A";
-}
-.mdi-apache-kafka::before {
-  content: "\F0031";
-}
-.mdi-api::before {
-  content: "\F00C6";
-}
-.mdi-api-off::before {
-  content: "\F0282";
-}
-.mdi-apple::before {
-  content: "\F035";
-}
-.mdi-apple-finder::before {
-  content: "\F036";
-}
-.mdi-apple-icloud::before {
-  content: "\F038";
-}
-.mdi-apple-ios::before {
-  content: "\F037";
-}
-.mdi-apple-keyboard-caps::before {
-  content: "\F632";
-}
-.mdi-apple-keyboard-command::before {
-  content: "\F633";
-}
-.mdi-apple-keyboard-control::before {
-  content: "\F634";
-}
-.mdi-apple-keyboard-option::before {
-  content: "\F635";
-}
-.mdi-apple-keyboard-shift::before {
-  content: "\F636";
-}
-.mdi-apple-safari::before {
-  content: "\F039";
-}
-.mdi-application::before {
-  content: "\F614";
-}
-.mdi-application-export::before {
-  content: "\FD89";
-}
-.mdi-application-import::before {
-  content: "\FD8A";
-}
-.mdi-approximately-equal::before {
-  content: "\FFBE";
-}
-.mdi-approximately-equal-box::before {
-  content: "\FFBF";
-}
-.mdi-apps::before {
-  content: "\F03B";
-}
-.mdi-apps-box::before {
-  content: "\FD22";
-}
-.mdi-arch::before {
-  content: "\F8C6";
-}
-.mdi-archive::before {
-  content: "\F03C";
-}
-.mdi-archive-arrow-down::before {
-  content: "\F0284";
-}
-.mdi-archive-arrow-down-outline::before {
-  content: "\F0285";
-}
-.mdi-archive-arrow-up::before {
-  content: "\F0286";
-}
-.mdi-archive-arrow-up-outline::before {
-  content: "\F0287";
-}
-.mdi-archive-outline::before {
-  content: "\F0239";
-}
-.mdi-arm-flex::before {
-  content: "\F008F";
-}
-.mdi-arm-flex-outline::before {
-  content: "\F0090";
-}
-.mdi-arrange-bring-forward::before {
-  content: "\F03D";
-}
-.mdi-arrange-bring-to-front::before {
-  content: "\F03E";
-}
-.mdi-arrange-send-backward::before {
-  content: "\F03F";
-}
-.mdi-arrange-send-to-back::before {
-  content: "\F040";
-}
-.mdi-arrow-all::before {
-  content: "\F041";
-}
-.mdi-arrow-bottom-left::before {
-  content: "\F042";
-}
-.mdi-arrow-bottom-left-bold-outline::before {
-  content: "\F9B6";
-}
-.mdi-arrow-bottom-left-thick::before {
-  content: "\F9B7";
-}
-.mdi-arrow-bottom-right::before {
-  content: "\F043";
-}
-.mdi-arrow-bottom-right-bold-outline::before {
-  content: "\F9B8";
-}
-.mdi-arrow-bottom-right-thick::before {
-  content: "\F9B9";
-}
-.mdi-arrow-collapse::before {
-  content: "\F615";
-}
-.mdi-arrow-collapse-all::before {
-  content: "\F044";
-}
-.mdi-arrow-collapse-down::before {
-  content: "\F791";
-}
-.mdi-arrow-collapse-horizontal::before {
-  content: "\F84B";
-}
-.mdi-arrow-collapse-left::before {
-  content: "\F792";
-}
-.mdi-arrow-collapse-right::before {
-  content: "\F793";
-}
-.mdi-arrow-collapse-up::before {
-  content: "\F794";
-}
-.mdi-arrow-collapse-vertical::before {
-  content: "\F84C";
-}
-.mdi-arrow-decision::before {
-  content: "\F9BA";
-}
-.mdi-arrow-decision-auto::before {
-  content: "\F9BB";
-}
-.mdi-arrow-decision-auto-outline::before {
-  content: "\F9BC";
-}
-.mdi-arrow-decision-outline::before {
-  content: "\F9BD";
-}
-.mdi-arrow-down::before {
-  content: "\F045";
-}
-.mdi-arrow-down-bold::before {
-  content: "\F72D";
-}
-.mdi-arrow-down-bold-box::before {
-  content: "\F72E";
-}
-.mdi-arrow-down-bold-box-outline::before {
-  content: "\F72F";
-}
-.mdi-arrow-down-bold-circle::before {
-  content: "\F047";
-}
-.mdi-arrow-down-bold-circle-outline::before {
-  content: "\F048";
-}
-.mdi-arrow-down-bold-hexagon-outline::before {
-  content: "\F049";
-}
-.mdi-arrow-down-bold-outline::before {
-  content: "\F9BE";
-}
-.mdi-arrow-down-box::before {
-  content: "\F6BF";
-}
-.mdi-arrow-down-circle::before {
-  content: "\FCB7";
-}
-.mdi-arrow-down-circle-outline::before {
-  content: "\FCB8";
-}
-.mdi-arrow-down-drop-circle::before {
-  content: "\F04A";
-}
-.mdi-arrow-down-drop-circle-outline::before {
-  content: "\F04B";
-}
-.mdi-arrow-down-thick::before {
-  content: "\F046";
-}
-.mdi-arrow-expand::before {
-  content: "\F616";
-}
-.mdi-arrow-expand-all::before {
-  content: "\F04C";
-}
-.mdi-arrow-expand-down::before {
-  content: "\F795";
-}
-.mdi-arrow-expand-horizontal::before {
-  content: "\F84D";
-}
-.mdi-arrow-expand-left::before {
-  content: "\F796";
-}
-.mdi-arrow-expand-right::before {
-  content: "\F797";
-}
-.mdi-arrow-expand-up::before {
-  content: "\F798";
-}
-.mdi-arrow-expand-vertical::before {
-  content: "\F84E";
-}
-.mdi-arrow-horizontal-lock::before {
-  content: "\F0186";
-}
-.mdi-arrow-left::before {
-  content: "\F04D";
-}
-.mdi-arrow-left-bold::before {
-  content: "\F730";
-}
-.mdi-arrow-left-bold-box::before {
-  content: "\F731";
-}
-.mdi-arrow-left-bold-box-outline::before {
-  content: "\F732";
-}
-.mdi-arrow-left-bold-circle::before {
-  content: "\F04F";
-}
-.mdi-arrow-left-bold-circle-outline::before {
-  content: "\F050";
-}
-.mdi-arrow-left-bold-hexagon-outline::before {
-  content: "\F051";
-}
-.mdi-arrow-left-bold-outline::before {
-  content: "\F9BF";
-}
-.mdi-arrow-left-box::before {
-  content: "\F6C0";
-}
-.mdi-arrow-left-circle::before {
-  content: "\FCB9";
-}
-.mdi-arrow-left-circle-outline::before {
-  content: "\FCBA";
-}
-.mdi-arrow-left-drop-circle::before {
-  content: "\F052";
-}
-.mdi-arrow-left-drop-circle-outline::before {
-  content: "\F053";
-}
-.mdi-arrow-left-right::before {
-  content: "\FE90";
-}
-.mdi-arrow-left-right-bold::before {
-  content: "\FE91";
-}
-.mdi-arrow-left-right-bold-outline::before {
-  content: "\F9C0";
-}
-.mdi-arrow-left-thick::before {
-  content: "\F04E";
-}
-.mdi-arrow-right::before {
-  content: "\F054";
-}
-.mdi-arrow-right-bold::before {
-  content: "\F733";
-}
-.mdi-arrow-right-bold-box::before {
-  content: "\F734";
-}
-.mdi-arrow-right-bold-box-outline::before {
-  content: "\F735";
-}
-.mdi-arrow-right-bold-circle::before {
-  content: "\F056";
-}
-.mdi-arrow-right-bold-circle-outline::before {
-  content: "\F057";
-}
-.mdi-arrow-right-bold-hexagon-outline::before {
-  content: "\F058";
-}
-.mdi-arrow-right-bold-outline::before {
-  content: "\F9C1";
-}
-.mdi-arrow-right-box::before {
-  content: "\F6C1";
-}
-.mdi-arrow-right-circle::before {
-  content: "\FCBB";
-}
-.mdi-arrow-right-circle-outline::before {
-  content: "\FCBC";
-}
-.mdi-arrow-right-drop-circle::before {
-  content: "\F059";
-}
-.mdi-arrow-right-drop-circle-outline::before {
-  content: "\F05A";
-}
-.mdi-arrow-right-thick::before {
-  content: "\F055";
-}
-.mdi-arrow-split-horizontal::before {
-  content: "\F93A";
-}
-.mdi-arrow-split-vertical::before {
-  content: "\F93B";
-}
-.mdi-arrow-top-left::before {
-  content: "\F05B";
-}
-.mdi-arrow-top-left-bold-outline::before {
-  content: "\F9C2";
-}
-.mdi-arrow-top-left-bottom-right::before {
-  content: "\FE92";
-}
-.mdi-arrow-top-left-bottom-right-bold::before {
-  content: "\FE93";
-}
-.mdi-arrow-top-left-thick::before {
-  content: "\F9C3";
-}
-.mdi-arrow-top-right::before {
-  content: "\F05C";
-}
-.mdi-arrow-top-right-bold-outline::before {
-  content: "\F9C4";
-}
-.mdi-arrow-top-right-bottom-left::before {
-  content: "\FE94";
-}
-.mdi-arrow-top-right-bottom-left-bold::before {
-  content: "\FE95";
-}
-.mdi-arrow-top-right-thick::before {
-  content: "\F9C5";
-}
-.mdi-arrow-up::before {
-  content: "\F05D";
-}
-.mdi-arrow-up-bold::before {
-  content: "\F736";
-}
-.mdi-arrow-up-bold-box::before {
-  content: "\F737";
-}
-.mdi-arrow-up-bold-box-outline::before {
-  content: "\F738";
-}
-.mdi-arrow-up-bold-circle::before {
-  content: "\F05F";
-}
-.mdi-arrow-up-bold-circle-outline::before {
-  content: "\F060";
-}
-.mdi-arrow-up-bold-hexagon-outline::before {
-  content: "\F061";
-}
-.mdi-arrow-up-bold-outline::before {
-  content: "\F9C6";
-}
-.mdi-arrow-up-box::before {
-  content: "\F6C2";
-}
-.mdi-arrow-up-circle::before {
-  content: "\FCBD";
-}
-.mdi-arrow-up-circle-outline::before {
-  content: "\FCBE";
-}
-.mdi-arrow-up-down::before {
-  content: "\FE96";
-}
-.mdi-arrow-up-down-bold::before {
-  content: "\FE97";
-}
-.mdi-arrow-up-down-bold-outline::before {
-  content: "\F9C7";
-}
-.mdi-arrow-up-drop-circle::before {
-  content: "\F062";
-}
-.mdi-arrow-up-drop-circle-outline::before {
-  content: "\F063";
-}
-.mdi-arrow-up-thick::before {
-  content: "\F05E";
-}
-.mdi-arrow-vertical-lock::before {
-  content: "\F0187";
-}
-.mdi-artist::before {
-  content: "\F802";
-}
-.mdi-artist-outline::before {
-  content: "\FCC5";
-}
-.mdi-artstation::before {
-  content: "\FB37";
-}
-.mdi-aspect-ratio::before {
-  content: "\FA23";
-}
-.mdi-assistant::before {
-  content: "\F064";
-}
-.mdi-asterisk::before {
-  content: "\F6C3";
-}
-.mdi-at::before {
-  content: "\F065";
-}
-.mdi-atlassian::before {
-  content: "\F803";
-}
-.mdi-atm::before {
-  content: "\FD23";
-}
-.mdi-atom::before {
-  content: "\F767";
-}
-.mdi-atom-variant::before {
-  content: "\FE98";
-}
-.mdi-attachment::before {
-  content: "\F066";
-}
-.mdi-audio-video::before {
-  content: "\F93C";
-}
-.mdi-audio-video-off::before {
-  content: "\F01E1";
-}
-.mdi-audiobook::before {
-  content: "\F067";
-}
-.mdi-augmented-reality::before {
-  content: "\F84F";
-}
-.mdi-auto-download::before {
-  content: "\F03A9";
-}
-.mdi-auto-fix::before {
-  content: "\F068";
-}
-.mdi-auto-upload::before {
-  content: "\F069";
-}
-.mdi-autorenew::before {
-  content: "\F06A";
-}
-.mdi-av-timer::before {
-  content: "\F06B";
-}
-.mdi-aws::before {
-  content: "\FDF2";
-}
-.mdi-axe::before {
-  content: "\F8C7";
-}
-.mdi-axis::before {
-  content: "\FD24";
-}
-.mdi-axis-arrow::before {
-  content: "\FD25";
-}
-.mdi-axis-arrow-lock::before {
-  content: "\FD26";
-}
-.mdi-axis-lock::before {
-  content: "\FD27";
-}
-.mdi-axis-x-arrow::before {
-  content: "\FD28";
-}
-.mdi-axis-x-arrow-lock::before {
-  content: "\FD29";
-}
-.mdi-axis-x-rotate-clockwise::before {
-  content: "\FD2A";
-}
-.mdi-axis-x-rotate-counterclockwise::before {
-  content: "\FD2B";
-}
-.mdi-axis-x-y-arrow-lock::before {
-  content: "\FD2C";
-}
-.mdi-axis-y-arrow::before {
-  content: "\FD2D";
-}
-.mdi-axis-y-arrow-lock::before {
-  content: "\FD2E";
-}
-.mdi-axis-y-rotate-clockwise::before {
-  content: "\FD2F";
-}
-.mdi-axis-y-rotate-counterclockwise::before {
-  content: "\FD30";
-}
-.mdi-axis-z-arrow::before {
-  content: "\FD31";
-}
-.mdi-axis-z-arrow-lock::before {
-  content: "\FD32";
-}
-.mdi-axis-z-rotate-clockwise::before {
-  content: "\FD33";
-}
-.mdi-axis-z-rotate-counterclockwise::before {
-  content: "\FD34";
-}
-.mdi-azure::before {
-  content: "\F804";
-}
-.mdi-azure-devops::before {
-  content: "\F0091";
-}
-.mdi-babel::before {
-  content: "\FA24";
-}
-.mdi-baby::before {
-  content: "\F06C";
-}
-.mdi-baby-bottle::before {
-  content: "\FF56";
-}
-.mdi-baby-bottle-outline::before {
-  content: "\FF57";
-}
-.mdi-baby-carriage::before {
-  content: "\F68E";
-}
-.mdi-baby-carriage-off::before {
-  content: "\FFC0";
-}
-.mdi-baby-face::before {
-  content: "\FE99";
-}
-.mdi-baby-face-outline::before {
-  content: "\FE9A";
-}
-.mdi-backburger::before {
-  content: "\F06D";
-}
-.mdi-backspace::before {
-  content: "\F06E";
-}
-.mdi-backspace-outline::before {
-  content: "\FB38";
-}
-.mdi-backspace-reverse::before {
-  content: "\FE9B";
-}
-.mdi-backspace-reverse-outline::before {
-  content: "\FE9C";
-}
-.mdi-backup-restore::before {
-  content: "\F06F";
-}
-.mdi-bacteria::before {
-  content: "\FEF2";
-}
-.mdi-bacteria-outline::before {
-  content: "\FEF3";
-}
-.mdi-badminton::before {
-  content: "\F850";
-}
-.mdi-bag-carry-on::before {
-  content: "\FF58";
-}
-.mdi-bag-carry-on-check::before {
-  content: "\FD41";
-}
-.mdi-bag-carry-on-off::before {
-  content: "\FF59";
-}
-.mdi-bag-checked::before {
-  content: "\FF5A";
-}
-.mdi-bag-personal::before {
-  content: "\FDF3";
-}
-.mdi-bag-personal-off::before {
-  content: "\FDF4";
-}
-.mdi-bag-personal-off-outline::before {
-  content: "\FDF5";
-}
-.mdi-bag-personal-outline::before {
-  content: "\FDF6";
-}
-.mdi-baguette::before {
-  content: "\FF5B";
-}
-.mdi-balloon::before {
-  content: "\FA25";
-}
-.mdi-ballot::before {
-  content: "\F9C8";
-}
-.mdi-ballot-outline::before {
-  content: "\F9C9";
-}
-.mdi-ballot-recount::before {
-  content: "\FC15";
-}
-.mdi-ballot-recount-outline::before {
-  content: "\FC16";
-}
-.mdi-bandage::before {
-  content: "\FD8B";
-}
-.mdi-bandcamp::before {
-  content: "\F674";
-}
-.mdi-bank::before {
-  content: "\F070";
-}
-.mdi-bank-minus::before {
-  content: "\FD8C";
-}
-.mdi-bank-outline::before {
-  content: "\FE9D";
-}
-.mdi-bank-plus::before {
-  content: "\FD8D";
-}
-.mdi-bank-remove::before {
-  content: "\FD8E";
-}
-.mdi-bank-transfer::before {
-  content: "\FA26";
-}
-.mdi-bank-transfer-in::before {
-  content: "\FA27";
-}
-.mdi-bank-transfer-out::before {
-  content: "\FA28";
-}
-.mdi-barcode::before {
-  content: "\F071";
-}
-.mdi-barcode-off::before {
-  content: "\F0261";
-}
-.mdi-barcode-scan::before {
-  content: "\F072";
-}
-.mdi-barley::before {
-  content: "\F073";
-}
-.mdi-barley-off::before {
-  content: "\FB39";
-}
-.mdi-barn::before {
-  content: "\FB3A";
-}
-.mdi-barrel::before {
-  content: "\F074";
-}
-.mdi-baseball::before {
-  content: "\F851";
-}
-.mdi-baseball-bat::before {
-  content: "\F852";
-}
-.mdi-basecamp::before {
-  content: "\F075";
-}
-.mdi-bash::before {
-  content: "\F01AE";
-}
-.mdi-basket::before {
-  content: "\F076";
-}
-.mdi-basket-fill::before {
-  content: "\F077";
-}
-.mdi-basket-outline::before {
-  content: "\F01AC";
-}
-.mdi-basket-unfill::before {
-  content: "\F078";
-}
-.mdi-basketball::before {
-  content: "\F805";
-}
-.mdi-basketball-hoop::before {
-  content: "\FC17";
-}
-.mdi-basketball-hoop-outline::before {
-  content: "\FC18";
-}
-.mdi-bat::before {
-  content: "\FB3B";
-}
-.mdi-battery::before {
-  content: "\F079";
-}
-.mdi-battery-10::before {
-  content: "\F07A";
-}
-.mdi-battery-10-bluetooth::before {
-  content: "\F93D";
-}
-.mdi-battery-20::before {
-  content: "\F07B";
-}
-.mdi-battery-20-bluetooth::before {
-  content: "\F93E";
-}
-.mdi-battery-30::before {
-  content: "\F07C";
-}
-.mdi-battery-30-bluetooth::before {
-  content: "\F93F";
-}
-.mdi-battery-40::before {
-  content: "\F07D";
-}
-.mdi-battery-40-bluetooth::before {
-  content: "\F940";
-}
-.mdi-battery-50::before {
-  content: "\F07E";
-}
-.mdi-battery-50-bluetooth::before {
-  content: "\F941";
-}
-.mdi-battery-60::before {
-  content: "\F07F";
-}
-.mdi-battery-60-bluetooth::before {
-  content: "\F942";
-}
-.mdi-battery-70::before {
-  content: "\F080";
-}
-.mdi-battery-70-bluetooth::before {
-  content: "\F943";
-}
-.mdi-battery-80::before {
-  content: "\F081";
-}
-.mdi-battery-80-bluetooth::before {
-  content: "\F944";
-}
-.mdi-battery-90::before {
-  content: "\F082";
-}
-.mdi-battery-90-bluetooth::before {
-  content: "\F945";
-}
-.mdi-battery-alert::before {
-  content: "\F083";
-}
-.mdi-battery-alert-bluetooth::before {
-  content: "\F946";
-}
-.mdi-battery-alert-variant::before {
-  content: "\F00F7";
-}
-.mdi-battery-alert-variant-outline::before {
-  content: "\F00F8";
-}
-.mdi-battery-bluetooth::before {
-  content: "\F947";
-}
-.mdi-battery-bluetooth-variant::before {
-  content: "\F948";
-}
-.mdi-battery-charging::before {
-  content: "\F084";
-}
-.mdi-battery-charging-10::before {
-  content: "\F89B";
-}
-.mdi-battery-charging-100::before {
-  content: "\F085";
-}
-.mdi-battery-charging-20::before {
-  content: "\F086";
-}
-.mdi-battery-charging-30::before {
-  content: "\F087";
-}
-.mdi-battery-charging-40::before {
-  content: "\F088";
-}
-.mdi-battery-charging-50::before {
-  content: "\F89C";
-}
-.mdi-battery-charging-60::before {
-  content: "\F089";
-}
-.mdi-battery-charging-70::before {
-  content: "\F89D";
-}
-.mdi-battery-charging-80::before {
-  content: "\F08A";
-}
-.mdi-battery-charging-90::before {
-  content: "\F08B";
-}
-.mdi-battery-charging-high::before {
-  content: "\F02D1";
-}
-.mdi-battery-charging-low::before {
-  content: "\F02CF";
-}
-.mdi-battery-charging-medium::before {
-  content: "\F02D0";
-}
-.mdi-battery-charging-outline::before {
-  content: "\F89E";
-}
-.mdi-battery-charging-wireless::before {
-  content: "\F806";
-}
-.mdi-battery-charging-wireless-10::before {
-  content: "\F807";
-}
-.mdi-battery-charging-wireless-20::before {
-  content: "\F808";
-}
-.mdi-battery-charging-wireless-30::before {
-  content: "\F809";
-}
-.mdi-battery-charging-wireless-40::before {
-  content: "\F80A";
-}
-.mdi-battery-charging-wireless-50::before {
-  content: "\F80B";
-}
-.mdi-battery-charging-wireless-60::before {
-  content: "\F80C";
-}
-.mdi-battery-charging-wireless-70::before {
-  content: "\F80D";
-}
-.mdi-battery-charging-wireless-80::before {
-  content: "\F80E";
-}
-.mdi-battery-charging-wireless-90::before {
-  content: "\F80F";
-}
-.mdi-battery-charging-wireless-alert::before {
-  content: "\F810";
-}
-.mdi-battery-charging-wireless-outline::before {
-  content: "\F811";
-}
-.mdi-battery-heart::before {
-  content: "\F023A";
-}
-.mdi-battery-heart-outline::before {
-  content: "\F023B";
-}
-.mdi-battery-heart-variant::before {
-  content: "\F023C";
-}
-.mdi-battery-high::before {
-  content: "\F02CE";
-}
-.mdi-battery-low::before {
-  content: "\F02CC";
-}
-.mdi-battery-medium::before {
-  content: "\F02CD";
-}
-.mdi-battery-minus::before {
-  content: "\F08C";
-}
-.mdi-battery-negative::before {
-  content: "\F08D";
-}
-.mdi-battery-off::before {
-  content: "\F0288";
-}
-.mdi-battery-off-outline::before {
-  content: "\F0289";
-}
-.mdi-battery-outline::before {
-  content: "\F08E";
-}
-.mdi-battery-plus::before {
-  content: "\F08F";
-}
-.mdi-battery-positive::before {
-  content: "\F090";
-}
-.mdi-battery-unknown::before {
-  content: "\F091";
-}
-.mdi-battery-unknown-bluetooth::before {
-  content: "\F949";
-}
-.mdi-battlenet::before {
-  content: "\FB3C";
-}
-.mdi-beach::before {
-  content: "\F092";
-}
-.mdi-beaker::before {
-  content: "\FCC6";
-}
-.mdi-beaker-alert::before {
-  content: "\F0254";
-}
-.mdi-beaker-alert-outline::before {
-  content: "\F0255";
-}
-.mdi-beaker-check::before {
-  content: "\F0256";
-}
-.mdi-beaker-check-outline::before {
-  content: "\F0257";
-}
-.mdi-beaker-minus::before {
-  content: "\F0258";
-}
-.mdi-beaker-minus-outline::before {
-  content: "\F0259";
-}
-.mdi-beaker-outline::before {
-  content: "\F68F";
-}
-.mdi-beaker-plus::before {
-  content: "\F025A";
-}
-.mdi-beaker-plus-outline::before {
-  content: "\F025B";
-}
-.mdi-beaker-question::before {
-  content: "\F025C";
-}
-.mdi-beaker-question-outline::before {
-  content: "\F025D";
-}
-.mdi-beaker-remove::before {
-  content: "\F025E";
-}
-.mdi-beaker-remove-outline::before {
-  content: "\F025F";
-}
-.mdi-beats::before {
-  content: "\F097";
-}
-.mdi-bed-double::before {
-  content: "\F0092";
-}
-.mdi-bed-double-outline::before {
-  content: "\F0093";
-}
-.mdi-bed-empty::before {
-  content: "\F89F";
-}
-.mdi-bed-king::before {
-  content: "\F0094";
-}
-.mdi-bed-king-outline::before {
-  content: "\F0095";
-}
-.mdi-bed-queen::before {
-  content: "\F0096";
-}
-.mdi-bed-queen-outline::before {
-  content: "\F0097";
-}
-.mdi-bed-single::before {
-  content: "\F0098";
-}
-.mdi-bed-single-outline::before {
-  content: "\F0099";
-}
-.mdi-bee::before {
-  content: "\FFC1";
-}
-.mdi-bee-flower::before {
-  content: "\FFC2";
-}
-.mdi-beehive-outline::before {
-  content: "\F00F9";
-}
-.mdi-beer::before {
-  content: "\F098";
-}
-.mdi-beer-outline::before {
-  content: "\F0337";
-}
-.mdi-behance::before {
-  content: "\F099";
-}
-.mdi-bell::before {
-  content: "\F09A";
-}
-.mdi-bell-alert::before {
-  content: "\FD35";
-}
-.mdi-bell-alert-outline::before {
-  content: "\FE9E";
-}
-.mdi-bell-check::before {
-  content: "\F0210";
-}
-.mdi-bell-check-outline::before {
-  content: "\F0211";
-}
-.mdi-bell-circle::before {
-  content: "\FD36";
-}
-.mdi-bell-circle-outline::before {
-  content: "\FD37";
-}
-.mdi-bell-off::before {
-  content: "\F09B";
-}
-.mdi-bell-off-outline::before {
-  content: "\FA90";
-}
-.mdi-bell-outline::before {
-  content: "\F09C";
-}
-.mdi-bell-plus::before {
-  content: "\F09D";
-}
-.mdi-bell-plus-outline::before {
-  content: "\FA91";
-}
-.mdi-bell-ring::before {
-  content: "\F09E";
-}
-.mdi-bell-ring-outline::before {
-  content: "\F09F";
-}
-.mdi-bell-sleep::before {
-  content: "\F0A0";
-}
-.mdi-bell-sleep-outline::before {
-  content: "\FA92";
-}
-.mdi-beta::before {
-  content: "\F0A1";
-}
-.mdi-betamax::before {
-  content: "\F9CA";
-}
-.mdi-biathlon::before {
-  content: "\FDF7";
-}
-.mdi-bible::before {
-  content: "\F0A2";
-}
-.mdi-bicycle::before {
-  content: "\F00C7";
-}
-.mdi-bicycle-basket::before {
-  content: "\F0260";
-}
-.mdi-bike::before {
-  content: "\F0A3";
-}
-.mdi-bike-fast::before {
-  content: "\F014A";
-}
-.mdi-billboard::before {
-  content: "\F0032";
-}
-.mdi-billiards::before {
-  content: "\FB3D";
-}
-.mdi-billiards-rack::before {
-  content: "\FB3E";
-}
-.mdi-bing::before {
-  content: "\F0A4";
-}
-.mdi-binoculars::before {
-  content: "\F0A5";
-}
-.mdi-bio::before {
-  content: "\F0A6";
-}
-.mdi-biohazard::before {
-  content: "\F0A7";
-}
-.mdi-bitbucket::before {
-  content: "\F0A8";
-}
-.mdi-bitcoin::before {
-  content: "\F812";
-}
-.mdi-black-mesa::before {
-  content: "\F0A9";
-}
-.mdi-blackberry::before {
-  content: "\F0AA";
-}
-.mdi-blender::before {
-  content: "\FCC7";
-}
-.mdi-blender-software::before {
-  content: "\F0AB";
-}
-.mdi-blinds::before {
-  content: "\F0AC";
-}
-.mdi-blinds-open::before {
-  content: "\F0033";
-}
-.mdi-block-helper::before {
-  content: "\F0AD";
-}
-.mdi-blogger::before {
-  content: "\F0AE";
-}
-.mdi-blood-bag::before {
-  content: "\FCC8";
-}
-.mdi-bluetooth::before {
-  content: "\F0AF";
-}
-.mdi-bluetooth-audio::before {
-  content: "\F0B0";
-}
-.mdi-bluetooth-connect::before {
-  content: "\F0B1";
-}
-.mdi-bluetooth-off::before {
-  content: "\F0B2";
-}
-.mdi-bluetooth-settings::before {
-  content: "\F0B3";
-}
-.mdi-bluetooth-transfer::before {
-  content: "\F0B4";
-}
-.mdi-blur::before {
-  content: "\F0B5";
-}
-.mdi-blur-linear::before {
-  content: "\F0B6";
-}
-.mdi-blur-off::before {
-  content: "\F0B7";
-}
-.mdi-blur-radial::before {
-  content: "\F0B8";
-}
-.mdi-bolnisi-cross::before {
-  content: "\FCC9";
-}
-.mdi-bolt::before {
-  content: "\FD8F";
-}
-.mdi-bomb::before {
-  content: "\F690";
-}
-.mdi-bomb-off::before {
-  content: "\F6C4";
-}
-.mdi-bone::before {
-  content: "\F0B9";
-}
-.mdi-book::before {
-  content: "\F0BA";
-}
-.mdi-book-information-variant::before {
-  content: "\F009A";
-}
-.mdi-book-lock::before {
-  content: "\F799";
-}
-.mdi-book-lock-open::before {
-  content: "\F79A";
-}
-.mdi-book-minus::before {
-  content: "\F5D9";
-}
-.mdi-book-minus-multiple::before {
-  content: "\FA93";
-}
-.mdi-book-multiple::before {
-  content: "\F0BB";
-}
-.mdi-book-open::before {
-  content: "\F0BD";
-}
-.mdi-book-open-outline::before {
-  content: "\FB3F";
-}
-.mdi-book-open-page-variant::before {
-  content: "\F5DA";
-}
-.mdi-book-open-variant::before {
-  content: "\F0BE";
-}
-.mdi-book-outline::before {
-  content: "\FB40";
-}
-.mdi-book-play::before {
-  content: "\FE9F";
-}
-.mdi-book-play-outline::before {
-  content: "\FEA0";
-}
-.mdi-book-plus::before {
-  content: "\F5DB";
-}
-.mdi-book-plus-multiple::before {
-  content: "\FA94";
-}
-.mdi-book-remove::before {
-  content: "\FA96";
-}
-.mdi-book-remove-multiple::before {
-  content: "\FA95";
-}
-.mdi-book-search::before {
-  content: "\FEA1";
-}
-.mdi-book-search-outline::before {
-  content: "\FEA2";
-}
-.mdi-book-variant::before {
-  content: "\F0BF";
-}
-.mdi-book-variant-multiple::before {
-  content: "\F0BC";
-}
-.mdi-bookmark::before {
-  content: "\F0C0";
-}
-.mdi-bookmark-check::before {
-  content: "\F0C1";
-}
-.mdi-bookmark-check-outline::before {
-  content: "\F03A6";
-}
-.mdi-bookmark-minus::before {
-  content: "\F9CB";
-}
-.mdi-bookmark-minus-outline::before {
-  content: "\F9CC";
-}
-.mdi-bookmark-multiple::before {
-  content: "\FDF8";
-}
-.mdi-bookmark-multiple-outline::before {
-  content: "\FDF9";
-}
-.mdi-bookmark-music::before {
-  content: "\F0C2";
-}
-.mdi-bookmark-music-outline::before {
-  content: "\F03A4";
-}
-.mdi-bookmark-off::before {
-  content: "\F9CD";
-}
-.mdi-bookmark-off-outline::before {
-  content: "\F9CE";
-}
-.mdi-bookmark-outline::before {
-  content: "\F0C3";
-}
-.mdi-bookmark-plus::before {
-  content: "\F0C5";
-}
-.mdi-bookmark-plus-outline::before {
-  content: "\F0C4";
-}
-.mdi-bookmark-remove::before {
-  content: "\F0C6";
-}
-.mdi-bookmark-remove-outline::before {
-  content: "\F03A5";
-}
-.mdi-bookshelf::before {
-  content: "\F028A";
-}
-.mdi-boom-gate::before {
-  content: "\FEA3";
-}
-.mdi-boom-gate-alert::before {
-  content: "\FEA4";
-}
-.mdi-boom-gate-alert-outline::before {
-  content: "\FEA5";
-}
-.mdi-boom-gate-down::before {
-  content: "\FEA6";
-}
-.mdi-boom-gate-down-outline::before {
-  content: "\FEA7";
-}
-.mdi-boom-gate-outline::before {
-  content: "\FEA8";
-}
-.mdi-boom-gate-up::before {
-  content: "\FEA9";
-}
-.mdi-boom-gate-up-outline::before {
-  content: "\FEAA";
-}
-.mdi-boombox::before {
-  content: "\F5DC";
-}
-.mdi-boomerang::before {
-  content: "\F00FA";
-}
-.mdi-bootstrap::before {
-  content: "\F6C5";
-}
-.mdi-border-all::before {
-  content: "\F0C7";
-}
-.mdi-border-all-variant::before {
-  content: "\F8A0";
-}
-.mdi-border-bottom::before {
-  content: "\F0C8";
-}
-.mdi-border-bottom-variant::before {
-  content: "\F8A1";
-}
-.mdi-border-color::before {
-  content: "\F0C9";
-}
-.mdi-border-horizontal::before {
-  content: "\F0CA";
-}
-.mdi-border-inside::before {
-  content: "\F0CB";
-}
-.mdi-border-left::before {
-  content: "\F0CC";
-}
-.mdi-border-left-variant::before {
-  content: "\F8A2";
-}
-.mdi-border-none::before {
-  content: "\F0CD";
-}
-.mdi-border-none-variant::before {
-  content: "\F8A3";
-}
-.mdi-border-outside::before {
-  content: "\F0CE";
-}
-.mdi-border-right::before {
-  content: "\F0CF";
-}
-.mdi-border-right-variant::before {
-  content: "\F8A4";
-}
-.mdi-border-style::before {
-  content: "\F0D0";
-}
-.mdi-border-top::before {
-  content: "\F0D1";
-}
-.mdi-border-top-variant::before {
-  content: "\F8A5";
-}
-.mdi-border-vertical::before {
-  content: "\F0D2";
-}
-.mdi-bottle-soda::before {
-  content: "\F009B";
-}
-.mdi-bottle-soda-classic::before {
-  content: "\F009C";
-}
-.mdi-bottle-soda-classic-outline::before {
-  content: "\F038E";
-}
-.mdi-bottle-soda-outline::before {
-  content: "\F009D";
-}
-.mdi-bottle-tonic::before {
-  content: "\F0159";
-}
-.mdi-bottle-tonic-outline::before {
-  content: "\F015A";
-}
-.mdi-bottle-tonic-plus::before {
-  content: "\F015B";
-}
-.mdi-bottle-tonic-plus-outline::before {
-  content: "\F015C";
-}
-.mdi-bottle-tonic-skull::before {
-  content: "\F015D";
-}
-.mdi-bottle-tonic-skull-outline::before {
-  content: "\F015E";
-}
-.mdi-bottle-wine::before {
-  content: "\F853";
-}
-.mdi-bottle-wine-outline::before {
-  content: "\F033B";
-}
-.mdi-bow-tie::before {
-  content: "\F677";
-}
-.mdi-bowl::before {
-  content: "\F617";
-}
-.mdi-bowling::before {
-  content: "\F0D3";
-}
-.mdi-box::before {
-  content: "\F0D4";
-}
-.mdi-box-cutter::before {
-  content: "\F0D5";
-}
-.mdi-box-shadow::before {
-  content: "\F637";
-}
-.mdi-boxing-glove::before {
-  content: "\FB41";
-}
-.mdi-braille::before {
-  content: "\F9CF";
-}
-.mdi-brain::before {
-  content: "\F9D0";
-}
-.mdi-bread-slice::before {
-  content: "\FCCA";
-}
-.mdi-bread-slice-outline::before {
-  content: "\FCCB";
-}
-.mdi-bridge::before {
-  content: "\F618";
-}
-.mdi-briefcase::before {
-  content: "\F0D6";
-}
-.mdi-briefcase-account::before {
-  content: "\FCCC";
-}
-.mdi-briefcase-account-outline::before {
-  content: "\FCCD";
-}
-.mdi-briefcase-check::before {
-  content: "\F0D7";
-}
-.mdi-briefcase-check-outline::before {
-  content: "\F0349";
-}
-.mdi-briefcase-clock::before {
-  content: "\F00FB";
-}
-.mdi-briefcase-clock-outline::before {
-  content: "\F00FC";
-}
-.mdi-briefcase-download::before {
-  content: "\F0D8";
-}
-.mdi-briefcase-download-outline::before {
-  content: "\FC19";
-}
-.mdi-briefcase-edit::before {
-  content: "\FA97";
-}
-.mdi-briefcase-edit-outline::before {
-  content: "\FC1A";
-}
-.mdi-briefcase-minus::before {
-  content: "\FA29";
-}
-.mdi-briefcase-minus-outline::before {
-  content: "\FC1B";
-}
-.mdi-briefcase-outline::before {
-  content: "\F813";
-}
-.mdi-briefcase-plus::before {
-  content: "\FA2A";
-}
-.mdi-briefcase-plus-outline::before {
-  content: "\FC1C";
-}
-.mdi-briefcase-remove::before {
-  content: "\FA2B";
-}
-.mdi-briefcase-remove-outline::before {
-  content: "\FC1D";
-}
-.mdi-briefcase-search::before {
-  content: "\FA2C";
-}
-.mdi-briefcase-search-outline::before {
-  content: "\FC1E";
-}
-.mdi-briefcase-upload::before {
-  content: "\F0D9";
-}
-.mdi-briefcase-upload-outline::before {
-  content: "\FC1F";
-}
-.mdi-brightness-1::before {
-  content: "\F0DA";
-}
-.mdi-brightness-2::before {
-  content: "\F0DB";
-}
-.mdi-brightness-3::before {
-  content: "\F0DC";
-}
-.mdi-brightness-4::before {
-  content: "\F0DD";
-}
-.mdi-brightness-5::before {
-  content: "\F0DE";
-}
-.mdi-brightness-6::before {
-  content: "\F0DF";
-}
-.mdi-brightness-7::before {
-  content: "\F0E0";
-}
-.mdi-brightness-auto::before {
-  content: "\F0E1";
-}
-.mdi-brightness-percent::before {
-  content: "\FCCE";
-}
-.mdi-broom::before {
-  content: "\F0E2";
-}
-.mdi-brush::before {
-  content: "\F0E3";
-}
-.mdi-buddhism::before {
-  content: "\F94A";
-}
-.mdi-buffer::before {
-  content: "\F619";
-}
-.mdi-bug::before {
-  content: "\F0E4";
-}
-.mdi-bug-check::before {
-  content: "\FA2D";
-}
-.mdi-bug-check-outline::before {
-  content: "\FA2E";
-}
-.mdi-bug-outline::before {
-  content: "\FA2F";
-}
-.mdi-bugle::before {
-  content: "\FD90";
-}
-.mdi-bulldozer::before {
-  content: "\FB07";
-}
-.mdi-bullet::before {
-  content: "\FCCF";
-}
-.mdi-bulletin-board::before {
-  content: "\F0E5";
-}
-.mdi-bullhorn::before {
-  content: "\F0E6";
-}
-.mdi-bullhorn-outline::before {
-  content: "\FB08";
-}
-.mdi-bullseye::before {
-  content: "\F5DD";
-}
-.mdi-bullseye-arrow::before {
-  content: "\F8C8";
-}
-.mdi-bulma::before {
-  content: "\F0312";
-}
-.mdi-bunk-bed::before {
-  content: "\F032D";
-}
-.mdi-bus::before {
-  content: "\F0E7";
-}
-.mdi-bus-alert::before {
-  content: "\FA98";
-}
-.mdi-bus-articulated-end::before {
-  content: "\F79B";
-}
-.mdi-bus-articulated-front::before {
-  content: "\F79C";
-}
-.mdi-bus-clock::before {
-  content: "\F8C9";
-}
-.mdi-bus-double-decker::before {
-  content: "\F79D";
-}
-.mdi-bus-marker::before {
-  content: "\F023D";
-}
-.mdi-bus-multiple::before {
-  content: "\FF5C";
-}
-.mdi-bus-school::before {
-  content: "\F79E";
-}
-.mdi-bus-side::before {
-  content: "\F79F";
-}
-.mdi-bus-stop::before {
-  content: "\F0034";
-}
-.mdi-bus-stop-covered::before {
-  content: "\F0035";
-}
-.mdi-bus-stop-uncovered::before {
-  content: "\F0036";
-}
-.mdi-cached::before {
-  content: "\F0E8";
-}
-.mdi-cactus::before {
-  content: "\FD91";
-}
-.mdi-cake::before {
-  content: "\F0E9";
-}
-.mdi-cake-layered::before {
-  content: "\F0EA";
-}
-.mdi-cake-variant::before {
-  content: "\F0EB";
-}
-.mdi-calculator::before {
-  content: "\F0EC";
-}
-.mdi-calculator-variant::before {
-  content: "\FA99";
-}
-.mdi-calendar::before {
-  content: "\F0ED";
-}
-.mdi-calendar-account::before {
-  content: "\FEF4";
-}
-.mdi-calendar-account-outline::before {
-  content: "\FEF5";
-}
-.mdi-calendar-alert::before {
-  content: "\FA30";
-}
-.mdi-calendar-arrow-left::before {
-  content: "\F015F";
-}
-.mdi-calendar-arrow-right::before {
-  content: "\F0160";
-}
-.mdi-calendar-blank::before {
-  content: "\F0EE";
-}
-.mdi-calendar-blank-multiple::before {
-  content: "\F009E";
-}
-.mdi-calendar-blank-outline::before {
-  content: "\FB42";
-}
-.mdi-calendar-check::before {
-  content: "\F0EF";
-}
-.mdi-calendar-check-outline::before {
-  content: "\FC20";
-}
-.mdi-calendar-clock::before {
-  content: "\F0F0";
-}
-.mdi-calendar-edit::before {
-  content: "\F8A6";
-}
-.mdi-calendar-export::before {
-  content: "\FB09";
-}
-.mdi-calendar-heart::before {
-  content: "\F9D1";
-}
-.mdi-calendar-import::before {
-  content: "\FB0A";
-}
-.mdi-calendar-minus::before {
-  content: "\FD38";
-}
-.mdi-calendar-month::before {
-  content: "\FDFA";
-}
-.mdi-calendar-month-outline::before {
-  content: "\FDFB";
-}
-.mdi-calendar-multiple::before {
-  content: "\F0F1";
-}
-.mdi-calendar-multiple-check::before {
-  content: "\F0F2";
-}
-.mdi-calendar-multiselect::before {
-  content: "\FA31";
-}
-.mdi-calendar-outline::before {
-  content: "\FB43";
-}
-.mdi-calendar-plus::before {
-  content: "\F0F3";
-}
-.mdi-calendar-question::before {
-  content: "\F691";
-}
-.mdi-calendar-range::before {
-  content: "\F678";
-}
-.mdi-calendar-range-outline::before {
-  content: "\FB44";
-}
-.mdi-calendar-remove::before {
-  content: "\F0F4";
-}
-.mdi-calendar-remove-outline::before {
-  content: "\FC21";
-}
-.mdi-calendar-repeat::before {
-  content: "\FEAB";
-}
-.mdi-calendar-repeat-outline::before {
-  content: "\FEAC";
-}
-.mdi-calendar-search::before {
-  content: "\F94B";
-}
-.mdi-calendar-star::before {
-  content: "\F9D2";
-}
-.mdi-calendar-text::before {
-  content: "\F0F5";
-}
-.mdi-calendar-text-outline::before {
-  content: "\FC22";
-}
-.mdi-calendar-today::before {
-  content: "\F0F6";
-}
-.mdi-calendar-week::before {
-  content: "\FA32";
-}
-.mdi-calendar-week-begin::before {
-  content: "\FA33";
-}
-.mdi-calendar-weekend::before {
-  content: "\FEF6";
-}
-.mdi-calendar-weekend-outline::before {
-  content: "\FEF7";
-}
-.mdi-call-made::before {
-  content: "\F0F7";
-}
-.mdi-call-merge::before {
-  content: "\F0F8";
-}
-.mdi-call-missed::before {
-  content: "\F0F9";
-}
-.mdi-call-received::before {
-  content: "\F0FA";
-}
-.mdi-call-split::before {
-  content: "\F0FB";
-}
-.mdi-camcorder::before {
-  content: "\F0FC";
-}
-.mdi-camcorder-box::before {
-  content: "\F0FD";
-}
-.mdi-camcorder-box-off::before {
-  content: "\F0FE";
-}
-.mdi-camcorder-off::before {
-  content: "\F0FF";
-}
-.mdi-camera::before {
-  content: "\F100";
-}
-.mdi-camera-account::before {
-  content: "\F8CA";
-}
-.mdi-camera-burst::before {
-  content: "\F692";
-}
-.mdi-camera-control::before {
-  content: "\FB45";
-}
-.mdi-camera-enhance::before {
-  content: "\F101";
-}
-.mdi-camera-enhance-outline::before {
-  content: "\FB46";
-}
-.mdi-camera-front::before {
-  content: "\F102";
-}
-.mdi-camera-front-variant::before {
-  content: "\F103";
-}
-.mdi-camera-gopro::before {
-  content: "\F7A0";
-}
-.mdi-camera-image::before {
-  content: "\F8CB";
-}
-.mdi-camera-iris::before {
-  content: "\F104";
-}
-.mdi-camera-metering-center::before {
-  content: "\F7A1";
-}
-.mdi-camera-metering-matrix::before {
-  content: "\F7A2";
-}
-.mdi-camera-metering-partial::before {
-  content: "\F7A3";
-}
-.mdi-camera-metering-spot::before {
-  content: "\F7A4";
-}
-.mdi-camera-off::before {
-  content: "\F5DF";
-}
-.mdi-camera-outline::before {
-  content: "\FD39";
-}
-.mdi-camera-party-mode::before {
-  content: "\F105";
-}
-.mdi-camera-plus::before {
-  content: "\FEF8";
-}
-.mdi-camera-plus-outline::before {
-  content: "\FEF9";
-}
-.mdi-camera-rear::before {
-  content: "\F106";
-}
-.mdi-camera-rear-variant::before {
-  content: "\F107";
-}
-.mdi-camera-retake::before {
-  content: "\FDFC";
-}
-.mdi-camera-retake-outline::before {
-  content: "\FDFD";
-}
-.mdi-camera-switch::before {
-  content: "\F108";
-}
-.mdi-camera-timer::before {
-  content: "\F109";
-}
-.mdi-camera-wireless::before {
-  content: "\FD92";
-}
-.mdi-camera-wireless-outline::before {
-  content: "\FD93";
-}
-.mdi-campfire::before {
-  content: "\FEFA";
-}
-.mdi-cancel::before {
-  content: "\F739";
-}
-.mdi-candle::before {
-  content: "\F5E2";
-}
-.mdi-candycane::before {
-  content: "\F10A";
-}
-.mdi-cannabis::before {
-  content: "\F7A5";
-}
-.mdi-caps-lock::before {
-  content: "\FA9A";
-}
-.mdi-car::before {
-  content: "\F10B";
-}
-.mdi-car-2-plus::before {
-  content: "\F0037";
-}
-.mdi-car-3-plus::before {
-  content: "\F0038";
-}
-.mdi-car-back::before {
-  content: "\FDFE";
-}
-.mdi-car-battery::before {
-  content: "\F10C";
-}
-.mdi-car-brake-abs::before {
-  content: "\FC23";
-}
-.mdi-car-brake-alert::before {
-  content: "\FC24";
-}
-.mdi-car-brake-hold::before {
-  content: "\FD3A";
-}
-.mdi-car-brake-parking::before {
-  content: "\FD3B";
-}
-.mdi-car-brake-retarder::before {
-  content: "\F0039";
-}
-.mdi-car-child-seat::before {
-  content: "\FFC3";
-}
-.mdi-car-clutch::before {
-  content: "\F003A";
-}
-.mdi-car-connected::before {
-  content: "\F10D";
-}
-.mdi-car-convertible::before {
-  content: "\F7A6";
-}
-.mdi-car-coolant-level::before {
-  content: "\F003B";
-}
-.mdi-car-cruise-control::before {
-  content: "\FD3C";
-}
-.mdi-car-defrost-front::before {
-  content: "\FD3D";
-}
-.mdi-car-defrost-rear::before {
-  content: "\FD3E";
-}
-.mdi-car-door::before {
-  content: "\FB47";
-}
-.mdi-car-door-lock::before {
-  content: "\F00C8";
-}
-.mdi-car-electric::before {
-  content: "\FB48";
-}
-.mdi-car-esp::before {
-  content: "\FC25";
-}
-.mdi-car-estate::before {
-  content: "\F7A7";
-}
-.mdi-car-hatchback::before {
-  content: "\F7A8";
-}
-.mdi-car-info::before {
-  content: "\F01E9";
-}
-.mdi-car-key::before {
-  content: "\FB49";
-}
-.mdi-car-light-dimmed::before {
-  content: "\FC26";
-}
-.mdi-car-light-fog::before {
-  content: "\FC27";
-}
-.mdi-car-light-high::before {
-  content: "\FC28";
-}
-.mdi-car-limousine::before {
-  content: "\F8CC";
-}
-.mdi-car-multiple::before {
-  content: "\FB4A";
-}
-.mdi-car-off::before {
-  content: "\FDFF";
-}
-.mdi-car-parking-lights::before {
-  content: "\FD3F";
-}
-.mdi-car-pickup::before {
-  content: "\F7A9";
-}
-.mdi-car-seat::before {
-  content: "\FFC4";
-}
-.mdi-car-seat-cooler::before {
-  content: "\FFC5";
-}
-.mdi-car-seat-heater::before {
-  content: "\FFC6";
-}
-.mdi-car-shift-pattern::before {
-  content: "\FF5D";
-}
-.mdi-car-side::before {
-  content: "\F7AA";
-}
-.mdi-car-sports::before {
-  content: "\F7AB";
-}
-.mdi-car-tire-alert::before {
-  content: "\FC29";
-}
-.mdi-car-traction-control::before {
-  content: "\FD40";
-}
-.mdi-car-turbocharger::before {
-  content: "\F003C";
-}
-.mdi-car-wash::before {
-  content: "\F10E";
-}
-.mdi-car-windshield::before {
-  content: "\F003D";
-}
-.mdi-car-windshield-outline::before {
-  content: "\F003E";
-}
-.mdi-caravan::before {
-  content: "\F7AC";
-}
-.mdi-card::before {
-  content: "\FB4B";
-}
-.mdi-card-bulleted::before {
-  content: "\FB4C";
-}
-.mdi-card-bulleted-off::before {
-  content: "\FB4D";
-}
-.mdi-card-bulleted-off-outline::before {
-  content: "\FB4E";
-}
-.mdi-card-bulleted-outline::before {
-  content: "\FB4F";
-}
-.mdi-card-bulleted-settings::before {
-  content: "\FB50";
-}
-.mdi-card-bulleted-settings-outline::before {
-  content: "\FB51";
-}
-.mdi-card-outline::before {
-  content: "\FB52";
-}
-.mdi-card-plus::before {
-  content: "\F022A";
-}
-.mdi-card-plus-outline::before {
-  content: "\F022B";
-}
-.mdi-card-search::before {
-  content: "\F009F";
-}
-.mdi-card-search-outline::before {
-  content: "\F00A0";
-}
-.mdi-card-text::before {
-  content: "\FB53";
-}
-.mdi-card-text-outline::before {
-  content: "\FB54";
-}
-.mdi-cards::before {
-  content: "\F638";
-}
-.mdi-cards-club::before {
-  content: "\F8CD";
-}
-.mdi-cards-diamond::before {
-  content: "\F8CE";
-}
-.mdi-cards-diamond-outline::before {
-  content: "\F003F";
-}
-.mdi-cards-heart::before {
-  content: "\F8CF";
-}
-.mdi-cards-outline::before {
-  content: "\F639";
-}
-.mdi-cards-playing-outline::before {
-  content: "\F63A";
-}
-.mdi-cards-spade::before {
-  content: "\F8D0";
-}
-.mdi-cards-variant::before {
-  content: "\F6C6";
-}
-.mdi-carrot::before {
-  content: "\F10F";
-}
-.mdi-cart::before {
-  content: "\F110";
-}
-.mdi-cart-arrow-down::before {
-  content: "\FD42";
-}
-.mdi-cart-arrow-right::before {
-  content: "\FC2A";
-}
-.mdi-cart-arrow-up::before {
-  content: "\FD43";
-}
-.mdi-cart-minus::before {
-  content: "\FD44";
-}
-.mdi-cart-off::before {
-  content: "\F66B";
-}
-.mdi-cart-outline::before {
-  content: "\F111";
-}
-.mdi-cart-plus::before {
-  content: "\F112";
-}
-.mdi-cart-remove::before {
-  content: "\FD45";
-}
-.mdi-case-sensitive-alt::before {
-  content: "\F113";
-}
-.mdi-cash::before {
-  content: "\F114";
-}
-.mdi-cash-100::before {
-  content: "\F115";
-}
-.mdi-cash-marker::before {
-  content: "\FD94";
-}
-.mdi-cash-minus::before {
-  content: "\F028B";
-}
-.mdi-cash-multiple::before {
-  content: "\F116";
-}
-.mdi-cash-plus::before {
-  content: "\F028C";
-}
-.mdi-cash-refund::before {
-  content: "\FA9B";
-}
-.mdi-cash-register::before {
-  content: "\FCD0";
-}
-.mdi-cash-remove::before {
-  content: "\F028D";
-}
-.mdi-cash-usd::before {
-  content: "\F01A1";
-}
-.mdi-cash-usd-outline::before {
-  content: "\F117";
-}
-.mdi-cassette::before {
-  content: "\F9D3";
-}
-.mdi-cast::before {
-  content: "\F118";
-}
-.mdi-cast-audio::before {
-  content: "\F0040";
-}
-.mdi-cast-connected::before {
-  content: "\F119";
-}
-.mdi-cast-education::before {
-  content: "\FE6D";
-}
-.mdi-cast-off::before {
-  content: "\F789";
-}
-.mdi-castle::before {
-  content: "\F11A";
-}
-.mdi-cat::before {
-  content: "\F11B";
-}
-.mdi-cctv::before {
-  content: "\F7AD";
-}
-.mdi-ceiling-light::before {
-  content: "\F768";
-}
-.mdi-cellphone::before {
-  content: "\F11C";
-}
-.mdi-cellphone-android::before {
-  content: "\F11D";
-}
-.mdi-cellphone-arrow-down::before {
-  content: "\F9D4";
-}
-.mdi-cellphone-basic::before {
-  content: "\F11E";
-}
-.mdi-cellphone-dock::before {
-  content: "\F11F";
-}
-.mdi-cellphone-erase::before {
-  content: "\F94C";
-}
-.mdi-cellphone-information::before {
-  content: "\FF5E";
-}
-.mdi-cellphone-iphone::before {
-  content: "\F120";
-}
-.mdi-cellphone-key::before {
-  content: "\F94D";
-}
-.mdi-cellphone-link::before {
-  content: "\F121";
-}
-.mdi-cellphone-link-off::before {
-  content: "\F122";
-}
-.mdi-cellphone-lock::before {
-  content: "\F94E";
-}
-.mdi-cellphone-message::before {
-  content: "\F8D2";
-}
-.mdi-cellphone-message-off::before {
-  content: "\F00FD";
-}
-.mdi-cellphone-nfc::before {
-  content: "\FEAD";
-}
-.mdi-cellphone-nfc-off::before {
-  content: "\F0303";
-}
-.mdi-cellphone-off::before {
-  content: "\F94F";
-}
-.mdi-cellphone-play::before {
-  content: "\F0041";
-}
-.mdi-cellphone-screenshot::before {
-  content: "\FA34";
-}
-.mdi-cellphone-settings::before {
-  content: "\F123";
-}
-.mdi-cellphone-settings-variant::before {
-  content: "\F950";
-}
-.mdi-cellphone-sound::before {
-  content: "\F951";
-}
-.mdi-cellphone-text::before {
-  content: "\F8D1";
-}
-.mdi-cellphone-wireless::before {
-  content: "\F814";
-}
-.mdi-celtic-cross::before {
-  content: "\FCD1";
-}
-.mdi-centos::before {
-  content: "\F0145";
-}
-.mdi-certificate::before {
-  content: "\F124";
-}
-.mdi-certificate-outline::before {
-  content: "\F01B3";
-}
-.mdi-chair-rolling::before {
-  content: "\FFBA";
-}
-.mdi-chair-school::before {
-  content: "\F125";
-}
-.mdi-charity::before {
-  content: "\FC2B";
-}
-.mdi-chart-arc::before {
-  content: "\F126";
-}
-.mdi-chart-areaspline::before {
-  content: "\F127";
-}
-.mdi-chart-areaspline-variant::before {
-  content: "\FEAE";
-}
-.mdi-chart-bar::before {
-  content: "\F128";
-}
-.mdi-chart-bar-stacked::before {
-  content: "\F769";
-}
-.mdi-chart-bell-curve::before {
-  content: "\FC2C";
-}
-.mdi-chart-bell-curve-cumulative::before {
-  content: "\FFC7";
-}
-.mdi-chart-bubble::before {
-  content: "\F5E3";
-}
-.mdi-chart-donut::before {
-  content: "\F7AE";
-}
-.mdi-chart-donut-variant::before {
-  content: "\F7AF";
-}
-.mdi-chart-gantt::before {
-  content: "\F66C";
-}
-.mdi-chart-histogram::before {
-  content: "\F129";
-}
-.mdi-chart-line::before {
-  content: "\F12A";
-}
-.mdi-chart-line-stacked::before {
-  content: "\F76A";
-}
-.mdi-chart-line-variant::before {
-  content: "\F7B0";
-}
-.mdi-chart-multiline::before {
-  content: "\F8D3";
-}
-.mdi-chart-multiple::before {
-  content: "\F023E";
-}
-.mdi-chart-pie::before {
-  content: "\F12B";
-}
-.mdi-chart-ppf::before {
-  content: "\F03AB";
-}
-.mdi-chart-scatter-plot::before {
-  content: "\FEAF";
-}
-.mdi-chart-scatter-plot-hexbin::before {
-  content: "\F66D";
-}
-.mdi-chart-snakey::before {
-  content: "\F020A";
-}
-.mdi-chart-snakey-variant::before {
-  content: "\F020B";
-}
-.mdi-chart-timeline::before {
-  content: "\F66E";
-}
-.mdi-chart-timeline-variant::before {
-  content: "\FEB0";
-}
-.mdi-chart-tree::before {
-  content: "\FEB1";
-}
-.mdi-chat::before {
-  content: "\FB55";
-}
-.mdi-chat-alert::before {
-  content: "\FB56";
-}
-.mdi-chat-alert-outline::before {
-  content: "\F02F4";
-}
-.mdi-chat-outline::before {
-  content: "\FEFB";
-}
-.mdi-chat-processing::before {
-  content: "\FB57";
-}
-.mdi-chat-processing-outline::before {
-  content: "\F02F5";
-}
-.mdi-chat-sleep::before {
-  content: "\F02FC";
-}
-.mdi-chat-sleep-outline::before {
-  content: "\F02FD";
-}
-.mdi-check::before {
-  content: "\F12C";
-}
-.mdi-check-all::before {
-  content: "\F12D";
-}
-.mdi-check-bold::before {
-  content: "\FE6E";
-}
-.mdi-check-box-multiple-outline::before {
-  content: "\FC2D";
-}
-.mdi-check-box-outline::before {
-  content: "\FC2E";
-}
-.mdi-check-circle::before {
-  content: "\F5E0";
-}
-.mdi-check-circle-outline::before {
-  content: "\F5E1";
-}
-.mdi-check-decagram::before {
-  content: "\F790";
-}
-.mdi-check-network::before {
-  content: "\FC2F";
-}
-.mdi-check-network-outline::before {
-  content: "\FC30";
-}
-.mdi-check-outline::before {
-  content: "\F854";
-}
-.mdi-check-underline::before {
-  content: "\FE70";
-}
-.mdi-check-underline-circle::before {
-  content: "\FE71";
-}
-.mdi-check-underline-circle-outline::before {
-  content: "\FE72";
-}
-.mdi-checkbook::before {
-  content: "\FA9C";
-}
-.mdi-checkbox-blank::before {
-  content: "\F12E";
-}
-.mdi-checkbox-blank-circle::before {
-  content: "\F12F";
-}
-.mdi-checkbox-blank-circle-outline::before {
-  content: "\F130";
-}
-.mdi-checkbox-blank-off::before {
-  content: "\F0317";
-}
-.mdi-checkbox-blank-off-outline::before {
-  content: "\F0318";
-}
-.mdi-checkbox-blank-outline::before {
-  content: "\F131";
-}
-.mdi-checkbox-intermediate::before {
-  content: "\F855";
-}
-.mdi-checkbox-marked::before {
-  content: "\F132";
-}
-.mdi-checkbox-marked-circle::before {
-  content: "\F133";
-}
-.mdi-checkbox-marked-circle-outline::before {
-  content: "\F134";
-}
-.mdi-checkbox-marked-outline::before {
-  content: "\F135";
-}
-.mdi-checkbox-multiple-blank::before {
-  content: "\F136";
-}
-.mdi-checkbox-multiple-blank-circle::before {
-  content: "\F63B";
-}
-.mdi-checkbox-multiple-blank-circle-outline::before {
-  content: "\F63C";
-}
-.mdi-checkbox-multiple-blank-outline::before {
-  content: "\F137";
-}
-.mdi-checkbox-multiple-marked::before {
-  content: "\F138";
-}
-.mdi-checkbox-multiple-marked-circle::before {
-  content: "\F63D";
-}
-.mdi-checkbox-multiple-marked-circle-outline::before {
-  content: "\F63E";
-}
-.mdi-checkbox-multiple-marked-outline::before {
-  content: "\F139";
-}
-.mdi-checkerboard::before {
-  content: "\F13A";
-}
-.mdi-checkerboard-minus::before {
-  content: "\F022D";
-}
-.mdi-checkerboard-plus::before {
-  content: "\F022C";
-}
-.mdi-checkerboard-remove::before {
-  content: "\F022E";
-}
-.mdi-cheese::before {
-  content: "\F02E4";
-}
-.mdi-chef-hat::before {
-  content: "\FB58";
-}
-.mdi-chemical-weapon::before {
-  content: "\F13B";
-}
-.mdi-chess-bishop::before {
-  content: "\F85B";
-}
-.mdi-chess-king::before {
-  content: "\F856";
-}
-.mdi-chess-knight::before {
-  content: "\F857";
-}
-.mdi-chess-pawn::before {
-  content: "\F858";
-}
-.mdi-chess-queen::before {
-  content: "\F859";
-}
-.mdi-chess-rook::before {
-  content: "\F85A";
-}
-.mdi-chevron-double-down::before {
-  content: "\F13C";
-}
-.mdi-chevron-double-left::before {
-  content: "\F13D";
-}
-.mdi-chevron-double-right::before {
-  content: "\F13E";
-}
-.mdi-chevron-double-up::before {
-  content: "\F13F";
-}
-.mdi-chevron-down::before {
-  content: "\F140";
-}
-.mdi-chevron-down-box::before {
-  content: "\F9D5";
-}
-.mdi-chevron-down-box-outline::before {
-  content: "\F9D6";
-}
-.mdi-chevron-down-circle::before {
-  content: "\FB0B";
-}
-.mdi-chevron-down-circle-outline::before {
-  content: "\FB0C";
-}
-.mdi-chevron-left::before {
-  content: "\F141";
-}
-.mdi-chevron-left-box::before {
-  content: "\F9D7";
-}
-.mdi-chevron-left-box-outline::before {
-  content: "\F9D8";
-}
-.mdi-chevron-left-circle::before {
-  content: "\FB0D";
-}
-.mdi-chevron-left-circle-outline::before {
-  content: "\FB0E";
-}
-.mdi-chevron-right::before {
-  content: "\F142";
-}
-.mdi-chevron-right-box::before {
-  content: "\F9D9";
-}
-.mdi-chevron-right-box-outline::before {
-  content: "\F9DA";
-}
-.mdi-chevron-right-circle::before {
-  content: "\FB0F";
-}
-.mdi-chevron-right-circle-outline::before {
-  content: "\FB10";
-}
-.mdi-chevron-triple-down::before {
-  content: "\FD95";
-}
-.mdi-chevron-triple-left::before {
-  content: "\FD96";
-}
-.mdi-chevron-triple-right::before {
-  content: "\FD97";
-}
-.mdi-chevron-triple-up::before {
-  content: "\FD98";
-}
-.mdi-chevron-up::before {
-  content: "\F143";
-}
-.mdi-chevron-up-box::before {
-  content: "\F9DB";
-}
-.mdi-chevron-up-box-outline::before {
-  content: "\F9DC";
-}
-.mdi-chevron-up-circle::before {
-  content: "\FB11";
-}
-.mdi-chevron-up-circle-outline::before {
-  content: "\FB12";
-}
-.mdi-chili-hot::before {
-  content: "\F7B1";
-}
-.mdi-chili-medium::before {
-  content: "\F7B2";
-}
-.mdi-chili-mild::before {
-  content: "\F7B3";
-}
-.mdi-chip::before {
-  content: "\F61A";
-}
-.mdi-christianity::before {
-  content: "\F952";
-}
-.mdi-christianity-outline::before {
-  content: "\FCD2";
-}
-.mdi-church::before {
-  content: "\F144";
-}
-.mdi-cigar::before {
-  content: "\F01B4";
-}
-.mdi-circle::before {
-  content: "\F764";
-}
-.mdi-circle-double::before {
-  content: "\FEB2";
-}
-.mdi-circle-edit-outline::before {
-  content: "\F8D4";
-}
-.mdi-circle-expand::before {
-  content: "\FEB3";
-}
-.mdi-circle-medium::before {
-  content: "\F9DD";
-}
-.mdi-circle-off-outline::before {
-  content: "\F00FE";
-}
-.mdi-circle-outline::before {
-  content: "\F765";
-}
-.mdi-circle-slice-1::before {
-  content: "\FA9D";
-}
-.mdi-circle-slice-2::before {
-  content: "\FA9E";
-}
-.mdi-circle-slice-3::before {
-  content: "\FA9F";
-}
-.mdi-circle-slice-4::before {
-  content: "\FAA0";
-}
-.mdi-circle-slice-5::before {
-  content: "\FAA1";
-}
-.mdi-circle-slice-6::before {
-  content: "\FAA2";
-}
-.mdi-circle-slice-7::before {
-  content: "\FAA3";
-}
-.mdi-circle-slice-8::before {
-  content: "\FAA4";
-}
-.mdi-circle-small::before {
-  content: "\F9DE";
-}
-.mdi-circular-saw::before {
-  content: "\FE73";
-}
-.mdi-cisco-webex::before {
-  content: "\F145";
-}
-.mdi-city::before {
-  content: "\F146";
-}
-.mdi-city-variant::before {
-  content: "\FA35";
-}
-.mdi-city-variant-outline::before {
-  content: "\FA36";
-}
-.mdi-clipboard::before {
-  content: "\F147";
-}
-.mdi-clipboard-account::before {
-  content: "\F148";
-}
-.mdi-clipboard-account-outline::before {
-  content: "\FC31";
-}
-.mdi-clipboard-alert::before {
-  content: "\F149";
-}
-.mdi-clipboard-alert-outline::before {
-  content: "\FCD3";
-}
-.mdi-clipboard-arrow-down::before {
-  content: "\F14A";
-}
-.mdi-clipboard-arrow-down-outline::before {
-  content: "\FC32";
-}
-.mdi-clipboard-arrow-left::before {
-  content: "\F14B";
-}
-.mdi-clipboard-arrow-left-outline::before {
-  content: "\FCD4";
-}
-.mdi-clipboard-arrow-right::before {
-  content: "\FCD5";
-}
-.mdi-clipboard-arrow-right-outline::before {
-  content: "\FCD6";
-}
-.mdi-clipboard-arrow-up::before {
-  content: "\FC33";
-}
-.mdi-clipboard-arrow-up-outline::before {
-  content: "\FC34";
-}
-.mdi-clipboard-check::before {
-  content: "\F14C";
-}
-.mdi-clipboard-check-multiple::before {
-  content: "\F028E";
-}
-.mdi-clipboard-check-multiple-outline::before {
-  content: "\F028F";
-}
-.mdi-clipboard-check-outline::before {
-  content: "\F8A7";
-}
-.mdi-clipboard-file::before {
-  content: "\F0290";
-}
-.mdi-clipboard-file-outline::before {
-  content: "\F0291";
-}
-.mdi-clipboard-flow::before {
-  content: "\F6C7";
-}
-.mdi-clipboard-flow-outline::before {
-  content: "\F0142";
-}
-.mdi-clipboard-list::before {
-  content: "\F00FF";
-}
-.mdi-clipboard-list-outline::before {
-  content: "\F0100";
-}
-.mdi-clipboard-multiple::before {
-  content: "\F0292";
-}
-.mdi-clipboard-multiple-outline::before {
-  content: "\F0293";
-}
-.mdi-clipboard-outline::before {
-  content: "\F14D";
-}
-.mdi-clipboard-play::before {
-  content: "\FC35";
-}
-.mdi-clipboard-play-multiple::before {
-  content: "\F0294";
-}
-.mdi-clipboard-play-multiple-outline::before {
-  content: "\F0295";
-}
-.mdi-clipboard-play-outline::before {
-  content: "\FC36";
-}
-.mdi-clipboard-plus::before {
-  content: "\F750";
-}
-.mdi-clipboard-plus-outline::before {
-  content: "\F034A";
-}
-.mdi-clipboard-pulse::before {
-  content: "\F85C";
-}
-.mdi-clipboard-pulse-outline::before {
-  content: "\F85D";
-}
-.mdi-clipboard-text::before {
-  content: "\F14E";
-}
-.mdi-clipboard-text-multiple::before {
-  content: "\F0296";
-}
-.mdi-clipboard-text-multiple-outline::before {
-  content: "\F0297";
-}
-.mdi-clipboard-text-outline::before {
-  content: "\FA37";
-}
-.mdi-clipboard-text-play::before {
-  content: "\FC37";
-}
-.mdi-clipboard-text-play-outline::before {
-  content: "\FC38";
-}
-.mdi-clippy::before {
-  content: "\F14F";
-}
-.mdi-clock::before {
-  content: "\F953";
-}
-.mdi-clock-alert::before {
-  content: "\F954";
-}
-.mdi-clock-alert-outline::before {
-  content: "\F5CE";
-}
-.mdi-clock-check::before {
-  content: "\FFC8";
-}
-.mdi-clock-check-outline::before {
-  content: "\FFC9";
-}
-.mdi-clock-digital::before {
-  content: "\FEB4";
-}
-.mdi-clock-end::before {
-  content: "\F151";
-}
-.mdi-clock-fast::before {
-  content: "\F152";
-}
-.mdi-clock-in::before {
-  content: "\F153";
-}
-.mdi-clock-out::before {
-  content: "\F154";
-}
-.mdi-clock-outline::before {
-  content: "\F150";
-}
-.mdi-clock-start::before {
-  content: "\F155";
-}
-.mdi-close::before {
-  content: "\F156";
-}
-.mdi-close-box::before {
-  content: "\F157";
-}
-.mdi-close-box-multiple::before {
-  content: "\FC39";
-}
-.mdi-close-box-multiple-outline::before {
-  content: "\FC3A";
-}
-.mdi-close-box-outline::before {
-  content: "\F158";
-}
-.mdi-close-circle::before {
-  content: "\F159";
-}
-.mdi-close-circle-outline::before {
-  content: "\F15A";
-}
-.mdi-close-network::before {
-  content: "\F15B";
-}
-.mdi-close-network-outline::before {
-  content: "\FC3B";
-}
-.mdi-close-octagon::before {
-  content: "\F15C";
-}
-.mdi-close-octagon-outline::before {
-  content: "\F15D";
-}
-.mdi-close-outline::before {
-  content: "\F6C8";
-}
-.mdi-closed-caption::before {
-  content: "\F15E";
-}
-.mdi-closed-caption-outline::before {
-  content: "\FD99";
-}
-.mdi-cloud::before {
-  content: "\F15F";
-}
-.mdi-cloud-alert::before {
-  content: "\F9DF";
-}
-.mdi-cloud-braces::before {
-  content: "\F7B4";
-}
-.mdi-cloud-check::before {
-  content: "\F160";
-}
-.mdi-cloud-check-outline::before {
-  content: "\F02F7";
-}
-.mdi-cloud-circle::before {
-  content: "\F161";
-}
-.mdi-cloud-download::before {
-  content: "\F162";
-}
-.mdi-cloud-download-outline::before {
-  content: "\FB59";
-}
-.mdi-cloud-lock::before {
-  content: "\F021C";
-}
-.mdi-cloud-lock-outline::before {
-  content: "\F021D";
-}
-.mdi-cloud-off-outline::before {
-  content: "\F164";
-}
-.mdi-cloud-outline::before {
-  content: "\F163";
-}
-.mdi-cloud-print::before {
-  content: "\F165";
-}
-.mdi-cloud-print-outline::before {
-  content: "\F166";
-}
-.mdi-cloud-question::before {
-  content: "\FA38";
-}
-.mdi-cloud-search::before {
-  content: "\F955";
-}
-.mdi-cloud-search-outline::before {
-  content: "\F956";
-}
-.mdi-cloud-sync::before {
-  content: "\F63F";
-}
-.mdi-cloud-sync-outline::before {
-  content: "\F0301";
-}
-.mdi-cloud-tags::before {
-  content: "\F7B5";
-}
-.mdi-cloud-upload::before {
-  content: "\F167";
-}
-.mdi-cloud-upload-outline::before {
-  content: "\FB5A";
-}
-.mdi-clover::before {
-  content: "\F815";
-}
-.mdi-coach-lamp::before {
-  content: "\F0042";
-}
-.mdi-coat-rack::before {
-  content: "\F00C9";
-}
-.mdi-code-array::before {
-  content: "\F168";
-}
-.mdi-code-braces::before {
-  content: "\F169";
-}
-.mdi-code-braces-box::before {
-  content: "\F0101";
-}
-.mdi-code-brackets::before {
-  content: "\F16A";
-}
-.mdi-code-equal::before {
-  content: "\F16B";
-}
-.mdi-code-greater-than::before {
-  content: "\F16C";
-}
-.mdi-code-greater-than-or-equal::before {
-  content: "\F16D";
-}
-.mdi-code-less-than::before {
-  content: "\F16E";
-}
-.mdi-code-less-than-or-equal::before {
-  content: "\F16F";
-}
-.mdi-code-not-equal::before {
-  content: "\F170";
-}
-.mdi-code-not-equal-variant::before {
-  content: "\F171";
-}
-.mdi-code-parentheses::before {
-  content: "\F172";
-}
-.mdi-code-parentheses-box::before {
-  content: "\F0102";
-}
-.mdi-code-string::before {
-  content: "\F173";
-}
-.mdi-code-tags::before {
-  content: "\F174";
-}
-.mdi-code-tags-check::before {
-  content: "\F693";
-}
-.mdi-codepen::before {
-  content: "\F175";
-}
-.mdi-coffee::before {
-  content: "\F176";
-}
-.mdi-coffee-maker::before {
-  content: "\F00CA";
-}
-.mdi-coffee-off::before {
-  content: "\FFCA";
-}
-.mdi-coffee-off-outline::before {
-  content: "\FFCB";
-}
-.mdi-coffee-outline::before {
-  content: "\F6C9";
-}
-.mdi-coffee-to-go::before {
-  content: "\F177";
-}
-.mdi-coffee-to-go-outline::before {
-  content: "\F0339";
-}
-.mdi-coffin::before {
-  content: "\FB5B";
-}
-.mdi-cog-clockwise::before {
-  content: "\F0208";
-}
-.mdi-cog-counterclockwise::before {
-  content: "\F0209";
-}
-.mdi-cogs::before {
-  content: "\F8D5";
-}
-.mdi-coin::before {
-  content: "\F0196";
-}
-.mdi-coin-outline::before {
-  content: "\F178";
-}
-.mdi-coins::before {
-  content: "\F694";
-}
-.mdi-collage::before {
-  content: "\F640";
-}
-.mdi-collapse-all::before {
-  content: "\FAA5";
-}
-.mdi-collapse-all-outline::before {
-  content: "\FAA6";
-}
-.mdi-color-helper::before {
-  content: "\F179";
-}
-.mdi-comma::before {
-  content: "\FE74";
-}
-.mdi-comma-box::before {
-  content: "\FE75";
-}
-.mdi-comma-box-outline::before {
-  content: "\FE76";
-}
-.mdi-comma-circle::before {
-  content: "\FE77";
-}
-.mdi-comma-circle-outline::before {
-  content: "\FE78";
-}
-.mdi-comment::before {
-  content: "\F17A";
-}
-.mdi-comment-account::before {
-  content: "\F17B";
-}
-.mdi-comment-account-outline::before {
-  content: "\F17C";
-}
-.mdi-comment-alert::before {
-  content: "\F17D";
-}
-.mdi-comment-alert-outline::before {
-  content: "\F17E";
-}
-.mdi-comment-arrow-left::before {
-  content: "\F9E0";
-}
-.mdi-comment-arrow-left-outline::before {
-  content: "\F9E1";
-}
-.mdi-comment-arrow-right::before {
-  content: "\F9E2";
-}
-.mdi-comment-arrow-right-outline::before {
-  content: "\F9E3";
-}
-.mdi-comment-check::before {
-  content: "\F17F";
-}
-.mdi-comment-check-outline::before {
-  content: "\F180";
-}
-.mdi-comment-edit::before {
-  content: "\F01EA";
-}
-.mdi-comment-edit-outline::before {
-  content: "\F02EF";
-}
-.mdi-comment-eye::before {
-  content: "\FA39";
-}
-.mdi-comment-eye-outline::before {
-  content: "\FA3A";
-}
-.mdi-comment-multiple::before {
-  content: "\F85E";
-}
-.mdi-comment-multiple-outline::before {
-  content: "\F181";
-}
-.mdi-comment-outline::before {
-  content: "\F182";
-}
-.mdi-comment-plus::before {
-  content: "\F9E4";
-}
-.mdi-comment-plus-outline::before {
-  content: "\F183";
-}
-.mdi-comment-processing::before {
-  content: "\F184";
-}
-.mdi-comment-processing-outline::before {
-  content: "\F185";
-}
-.mdi-comment-question::before {
-  content: "\F816";
-}
-.mdi-comment-question-outline::before {
-  content: "\F186";
-}
-.mdi-comment-quote::before {
-  content: "\F0043";
-}
-.mdi-comment-quote-outline::before {
-  content: "\F0044";
-}
-.mdi-comment-remove::before {
-  content: "\F5DE";
-}
-.mdi-comment-remove-outline::before {
-  content: "\F187";
-}
-.mdi-comment-search::before {
-  content: "\FA3B";
-}
-.mdi-comment-search-outline::before {
-  content: "\FA3C";
-}
-.mdi-comment-text::before {
-  content: "\F188";
-}
-.mdi-comment-text-multiple::before {
-  content: "\F85F";
-}
-.mdi-comment-text-multiple-outline::before {
-  content: "\F860";
-}
-.mdi-comment-text-outline::before {
-  content: "\F189";
-}
-.mdi-compare::before {
-  content: "\F18A";
-}
-.mdi-compass::before {
-  content: "\F18B";
-}
-.mdi-compass-off::before {
-  content: "\FB5C";
-}
-.mdi-compass-off-outline::before {
-  content: "\FB5D";
-}
-.mdi-compass-outline::before {
-  content: "\F18C";
-}
-.mdi-compass-rose::before {
-  content: "\F03AD";
-}
-.mdi-concourse-ci::before {
-  content: "\F00CB";
-}
-.mdi-console::before {
-  content: "\F18D";
-}
-.mdi-console-line::before {
-  content: "\F7B6";
-}
-.mdi-console-network::before {
-  content: "\F8A8";
-}
-.mdi-console-network-outline::before {
-  content: "\FC3C";
-}
-.mdi-consolidate::before {
-  content: "\F0103";
-}
-.mdi-contact-mail::before {
-  content: "\F18E";
-}
-.mdi-contact-mail-outline::before {
-  content: "\FEB5";
-}
-.mdi-contact-phone::before {
-  content: "\FEB6";
-}
-.mdi-contact-phone-outline::before {
-  content: "\FEB7";
-}
-.mdi-contactless-payment::before {
-  content: "\FD46";
-}
-.mdi-contacts::before {
-  content: "\F6CA";
-}
-.mdi-contain::before {
-  content: "\FA3D";
-}
-.mdi-contain-end::before {
-  content: "\FA3E";
-}
-.mdi-contain-start::before {
-  content: "\FA3F";
-}
-.mdi-content-copy::before {
-  content: "\F18F";
-}
-.mdi-content-cut::before {
-  content: "\F190";
-}
-.mdi-content-duplicate::before {
-  content: "\F191";
-}
-.mdi-content-paste::before {
-  content: "\F192";
-}
-.mdi-content-save::before {
-  content: "\F193";
-}
-.mdi-content-save-alert::before {
-  content: "\FF5F";
-}
-.mdi-content-save-alert-outline::before {
-  content: "\FF60";
-}
-.mdi-content-save-all::before {
-  content: "\F194";
-}
-.mdi-content-save-all-outline::before {
-  content: "\FF61";
-}
-.mdi-content-save-edit::before {
-  content: "\FCD7";
-}
-.mdi-content-save-edit-outline::before {
-  content: "\FCD8";
-}
-.mdi-content-save-move::before {
-  content: "\FE79";
-}
-.mdi-content-save-move-outline::before {
-  content: "\FE7A";
-}
-.mdi-content-save-outline::before {
-  content: "\F817";
-}
-.mdi-content-save-settings::before {
-  content: "\F61B";
-}
-.mdi-content-save-settings-outline::before {
-  content: "\FB13";
-}
-.mdi-contrast::before {
-  content: "\F195";
-}
-.mdi-contrast-box::before {
-  content: "\F196";
-}
-.mdi-contrast-circle::before {
-  content: "\F197";
-}
-.mdi-controller-classic::before {
-  content: "\FB5E";
-}
-.mdi-controller-classic-outline::before {
-  content: "\FB5F";
-}
-.mdi-cookie::before {
-  content: "\F198";
-}
-.mdi-coolant-temperature::before {
-  content: "\F3C8";
-}
-.mdi-copyright::before {
-  content: "\F5E6";
-}
-.mdi-cordova::before {
-  content: "\F957";
-}
-.mdi-corn::before {
-  content: "\F7B7";
-}
-.mdi-counter::before {
-  content: "\F199";
-}
-.mdi-cow::before {
-  content: "\F19A";
-}
-.mdi-cowboy::before {
-  content: "\FEB8";
-}
-.mdi-cpu-32-bit::before {
-  content: "\FEFC";
-}
-.mdi-cpu-64-bit::before {
-  content: "\FEFD";
-}
-.mdi-crane::before {
-  content: "\F861";
-}
-.mdi-creation::before {
-  content: "\F1C9";
-}
-.mdi-creative-commons::before {
-  content: "\FD47";
-}
-.mdi-credit-card::before {
-  content: "\F0010";
-}
-.mdi-credit-card-clock::before {
-  content: "\FEFE";
-}
-.mdi-credit-card-clock-outline::before {
-  content: "\FFBC";
-}
-.mdi-credit-card-marker::before {
-  content: "\F6A7";
-}
-.mdi-credit-card-marker-outline::before {
-  content: "\FD9A";
-}
-.mdi-credit-card-minus::before {
-  content: "\FFCC";
-}
-.mdi-credit-card-minus-outline::before {
-  content: "\FFCD";
-}
-.mdi-credit-card-multiple::before {
-  content: "\F0011";
-}
-.mdi-credit-card-multiple-outline::before {
-  content: "\F19C";
-}
-.mdi-credit-card-off::before {
-  content: "\F0012";
-}
-.mdi-credit-card-off-outline::before {
-  content: "\F5E4";
-}
-.mdi-credit-card-outline::before {
-  content: "\F19B";
-}
-.mdi-credit-card-plus::before {
-  content: "\F0013";
-}
-.mdi-credit-card-plus-outline::before {
-  content: "\F675";
-}
-.mdi-credit-card-refund::before {
-  content: "\F0014";
-}
-.mdi-credit-card-refund-outline::before {
-  content: "\FAA7";
-}
-.mdi-credit-card-remove::before {
-  content: "\FFCE";
-}
-.mdi-credit-card-remove-outline::before {
-  content: "\FFCF";
-}
-.mdi-credit-card-scan::before {
-  content: "\F0015";
-}
-.mdi-credit-card-scan-outline::before {
-  content: "\F19D";
-}
-.mdi-credit-card-settings::before {
-  content: "\F0016";
-}
-.mdi-credit-card-settings-outline::before {
-  content: "\F8D6";
-}
-.mdi-credit-card-wireless::before {
-  content: "\F801";
-}
-.mdi-credit-card-wireless-outline::before {
-  content: "\FD48";
-}
-.mdi-cricket::before {
-  content: "\FD49";
-}
-.mdi-crop::before {
-  content: "\F19E";
-}
-.mdi-crop-free::before {
-  content: "\F19F";
-}
-.mdi-crop-landscape::before {
-  content: "\F1A0";
-}
-.mdi-crop-portrait::before {
-  content: "\F1A1";
-}
-.mdi-crop-rotate::before {
-  content: "\F695";
-}
-.mdi-crop-square::before {
-  content: "\F1A2";
-}
-.mdi-crosshairs::before {
-  content: "\F1A3";
-}
-.mdi-crosshairs-gps::before {
-  content: "\F1A4";
-}
-.mdi-crosshairs-off::before {
-  content: "\FF62";
-}
-.mdi-crosshairs-question::before {
-  content: "\F0161";
-}
-.mdi-crown::before {
-  content: "\F1A5";
-}
-.mdi-crown-outline::before {
-  content: "\F01FB";
-}
-.mdi-cryengine::before {
-  content: "\F958";
-}
-.mdi-crystal-ball::before {
-  content: "\FB14";
-}
-.mdi-cube::before {
-  content: "\F1A6";
-}
-.mdi-cube-outline::before {
-  content: "\F1A7";
-}
-.mdi-cube-scan::before {
-  content: "\FB60";
-}
-.mdi-cube-send::before {
-  content: "\F1A8";
-}
-.mdi-cube-unfolded::before {
-  content: "\F1A9";
-}
-.mdi-cup::before {
-  content: "\F1AA";
-}
-.mdi-cup-off::before {
-  content: "\F5E5";
-}
-.mdi-cup-off-outline::before {
-  content: "\F03A8";
-}
-.mdi-cup-outline::before {
-  content: "\F033A";
-}
-.mdi-cup-water::before {
-  content: "\F1AB";
-}
-.mdi-cupboard::before {
-  content: "\FF63";
-}
-.mdi-cupboard-outline::before {
-  content: "\FF64";
-}
-.mdi-cupcake::before {
-  content: "\F959";
-}
-.mdi-curling::before {
-  content: "\F862";
-}
-.mdi-currency-bdt::before {
-  content: "\F863";
-}
-.mdi-currency-brl::before {
-  content: "\FB61";
-}
-.mdi-currency-btc::before {
-  content: "\F1AC";
-}
-.mdi-currency-cny::before {
-  content: "\F7B9";
-}
-.mdi-currency-eth::before {
-  content: "\F7BA";
-}
-.mdi-currency-eur::before {
-  content: "\F1AD";
-}
-.mdi-currency-eur-off::before {
-  content: "\F0340";
-}
-.mdi-currency-gbp::before {
-  content: "\F1AE";
-}
-.mdi-currency-ils::before {
-  content: "\FC3D";
-}
-.mdi-currency-inr::before {
-  content: "\F1AF";
-}
-.mdi-currency-jpy::before {
-  content: "\F7BB";
-}
-.mdi-currency-krw::before {
-  content: "\F7BC";
-}
-.mdi-currency-kzt::before {
-  content: "\F864";
-}
-.mdi-currency-ngn::before {
-  content: "\F1B0";
-}
-.mdi-currency-php::before {
-  content: "\F9E5";
-}
-.mdi-currency-rial::before {
-  content: "\FEB9";
-}
-.mdi-currency-rub::before {
-  content: "\F1B1";
-}
-.mdi-currency-sign::before {
-  content: "\F7BD";
-}
-.mdi-currency-try::before {
-  content: "\F1B2";
-}
-.mdi-currency-twd::before {
-  content: "\F7BE";
-}
-.mdi-currency-usd::before {
-  content: "\F1B3";
-}
-.mdi-currency-usd-off::before {
-  content: "\F679";
-}
-.mdi-current-ac::before {
-  content: "\F95A";
-}
-.mdi-current-dc::before {
-  content: "\F95B";
-}
-.mdi-cursor-default::before {
-  content: "\F1B4";
-}
-.mdi-cursor-default-click::before {
-  content: "\FCD9";
-}
-.mdi-cursor-default-click-outline::before {
-  content: "\FCDA";
-}
-.mdi-cursor-default-gesture::before {
-  content: "\F0152";
-}
-.mdi-cursor-default-gesture-outline::before {
-  content: "\F0153";
-}
-.mdi-cursor-default-outline::before {
-  content: "\F1B5";
-}
-.mdi-cursor-move::before {
-  content: "\F1B6";
-}
-.mdi-cursor-pointer::before {
-  content: "\F1B7";
-}
-.mdi-cursor-text::before {
-  content: "\F5E7";
-}
-.mdi-database::before {
-  content: "\F1B8";
-}
-.mdi-database-check::before {
-  content: "\FAA8";
-}
-.mdi-database-edit::before {
-  content: "\FB62";
-}
-.mdi-database-export::before {
-  content: "\F95D";
-}
-.mdi-database-import::before {
-  content: "\F95C";
-}
-.mdi-database-lock::before {
-  content: "\FAA9";
-}
-.mdi-database-marker::before {
-  content: "\F0321";
-}
-.mdi-database-minus::before {
-  content: "\F1B9";
-}
-.mdi-database-plus::before {
-  content: "\F1BA";
-}
-.mdi-database-refresh::before {
-  content: "\FCDB";
-}
-.mdi-database-remove::before {
-  content: "\FCDC";
-}
-.mdi-database-search::before {
-  content: "\F865";
-}
-.mdi-database-settings::before {
-  content: "\FCDD";
-}
-.mdi-death-star::before {
-  content: "\F8D7";
-}
-.mdi-death-star-variant::before {
-  content: "\F8D8";
-}
-.mdi-deathly-hallows::before {
-  content: "\FB63";
-}
-.mdi-debian::before {
-  content: "\F8D9";
-}
-.mdi-debug-step-into::before {
-  content: "\F1BB";
-}
-.mdi-debug-step-out::before {
-  content: "\F1BC";
-}
-.mdi-debug-step-over::before {
-  content: "\F1BD";
-}
-.mdi-decagram::before {
-  content: "\F76B";
-}
-.mdi-decagram-outline::before {
-  content: "\F76C";
-}
-.mdi-decimal::before {
-  content: "\F00CC";
-}
-.mdi-decimal-comma::before {
-  content: "\F00CD";
-}
-.mdi-decimal-comma-decrease::before {
-  content: "\F00CE";
-}
-.mdi-decimal-comma-increase::before {
-  content: "\F00CF";
-}
-.mdi-decimal-decrease::before {
-  content: "\F1BE";
-}
-.mdi-decimal-increase::before {
-  content: "\F1BF";
-}
-.mdi-delete::before {
-  content: "\F1C0";
-}
-.mdi-delete-alert::before {
-  content: "\F00D0";
-}
-.mdi-delete-alert-outline::before {
-  content: "\F00D1";
-}
-.mdi-delete-circle::before {
-  content: "\F682";
-}
-.mdi-delete-circle-outline::before {
-  content: "\FB64";
-}
-.mdi-delete-empty::before {
-  content: "\F6CB";
-}
-.mdi-delete-empty-outline::before {
-  content: "\FEBA";
-}
-.mdi-delete-forever::before {
-  content: "\F5E8";
-}
-.mdi-delete-forever-outline::before {
-  content: "\FB65";
-}
-.mdi-delete-off::before {
-  content: "\F00D2";
-}
-.mdi-delete-off-outline::before {
-  content: "\F00D3";
-}
-.mdi-delete-outline::before {
-  content: "\F9E6";
-}
-.mdi-delete-restore::before {
-  content: "\F818";
-}
-.mdi-delete-sweep::before {
-  content: "\F5E9";
-}
-.mdi-delete-sweep-outline::before {
-  content: "\FC3E";
-}
-.mdi-delete-variant::before {
-  content: "\F1C1";
-}
-.mdi-delta::before {
-  content: "\F1C2";
-}
-.mdi-desk::before {
-  content: "\F0264";
-}
-.mdi-desk-lamp::before {
-  content: "\F95E";
-}
-.mdi-deskphone::before {
-  content: "\F1C3";
-}
-.mdi-desktop-classic::before {
-  content: "\F7BF";
-}
-.mdi-desktop-mac::before {
-  content: "\F1C4";
-}
-.mdi-desktop-mac-dashboard::before {
-  content: "\F9E7";
-}
-.mdi-desktop-tower::before {
-  content: "\F1C5";
-}
-.mdi-desktop-tower-monitor::before {
-  content: "\FAAA";
-}
-.mdi-details::before {
-  content: "\F1C6";
-}
-.mdi-dev-to::before {
-  content: "\FD4A";
-}
-.mdi-developer-board::before {
-  content: "\F696";
-}
-.mdi-deviantart::before {
-  content: "\F1C7";
-}
-.mdi-devices::before {
-  content: "\FFD0";
-}
-.mdi-diabetes::before {
-  content: "\F0151";
-}
-.mdi-dialpad::before {
-  content: "\F61C";
-}
-.mdi-diameter::before {
-  content: "\FC3F";
-}
-.mdi-diameter-outline::before {
-  content: "\FC40";
-}
-.mdi-diameter-variant::before {
-  content: "\FC41";
-}
-.mdi-diamond::before {
-  content: "\FB66";
-}
-.mdi-diamond-outline::before {
-  content: "\FB67";
-}
-.mdi-diamond-stone::before {
-  content: "\F1C8";
-}
-.mdi-dice-1::before {
-  content: "\F1CA";
-}
-.mdi-dice-1-outline::before {
-  content: "\F0175";
-}
-.mdi-dice-2::before {
-  content: "\F1CB";
-}
-.mdi-dice-2-outline::before {
-  content: "\F0176";
-}
-.mdi-dice-3::before {
-  content: "\F1CC";
-}
-.mdi-dice-3-outline::before {
-  content: "\F0177";
-}
-.mdi-dice-4::before {
-  content: "\F1CD";
-}
-.mdi-dice-4-outline::before {
-  content: "\F0178";
-}
-.mdi-dice-5::before {
-  content: "\F1CE";
-}
-.mdi-dice-5-outline::before {
-  content: "\F0179";
-}
-.mdi-dice-6::before {
-  content: "\F1CF";
-}
-.mdi-dice-6-outline::before {
-  content: "\F017A";
-}
-.mdi-dice-d10::before {
-  content: "\F017E";
-}
-.mdi-dice-d10-outline::before {
-  content: "\F76E";
-}
-.mdi-dice-d12::before {
-  content: "\F017F";
-}
-.mdi-dice-d12-outline::before {
-  content: "\F866";
-}
-.mdi-dice-d20::before {
-  content: "\F0180";
-}
-.mdi-dice-d20-outline::before {
-  content: "\F5EA";
-}
-.mdi-dice-d4::before {
-  content: "\F017B";
-}
-.mdi-dice-d4-outline::before {
-  content: "\F5EB";
-}
-.mdi-dice-d6::before {
-  content: "\F017C";
-}
-.mdi-dice-d6-outline::before {
-  content: "\F5EC";
-}
-.mdi-dice-d8::before {
-  content: "\F017D";
-}
-.mdi-dice-d8-outline::before {
-  content: "\F5ED";
-}
-.mdi-dice-multiple::before {
-  content: "\F76D";
-}
-.mdi-dice-multiple-outline::before {
-  content: "\F0181";
-}
-.mdi-dictionary::before {
-  content: "\F61D";
-}
-.mdi-digital-ocean::before {
-  content: "\F0262";
-}
-.mdi-dip-switch::before {
-  content: "\F7C0";
-}
-.mdi-directions::before {
-  content: "\F1D0";
-}
-.mdi-directions-fork::before {
-  content: "\F641";
-}
-.mdi-disc::before {
-  content: "\F5EE";
-}
-.mdi-disc-alert::before {
-  content: "\F1D1";
-}
-.mdi-disc-player::before {
-  content: "\F95F";
-}
-.mdi-discord::before {
-  content: "\F66F";
-}
-.mdi-dishwasher::before {
-  content: "\FAAB";
-}
-.mdi-dishwasher-alert::before {
-  content: "\F01E3";
-}
-.mdi-dishwasher-off::before {
-  content: "\F01E4";
-}
-.mdi-disqus::before {
-  content: "\F1D2";
-}
-.mdi-disqus-outline::before {
-  content: "\F1D3";
-}
-.mdi-distribute-horizontal-center::before {
-  content: "\F01F4";
-}
-.mdi-distribute-horizontal-left::before {
-  content: "\F01F3";
-}
-.mdi-distribute-horizontal-right::before {
-  content: "\F01F5";
-}
-.mdi-distribute-vertical-bottom::before {
-  content: "\F01F6";
-}
-.mdi-distribute-vertical-center::before {
-  content: "\F01F7";
-}
-.mdi-distribute-vertical-top::before {
-  content: "\F01F8";
-}
-.mdi-diving-flippers::before {
-  content: "\FD9B";
-}
-.mdi-diving-helmet::before {
-  content: "\FD9C";
-}
-.mdi-diving-scuba::before {
-  content: "\FD9D";
-}
-.mdi-diving-scuba-flag::before {
-  content: "\FD9E";
-}
-.mdi-diving-scuba-tank::before {
-  content: "\FD9F";
-}
-.mdi-diving-scuba-tank-multiple::before {
-  content: "\FDA0";
-}
-.mdi-diving-snorkel::before {
-  content: "\FDA1";
-}
-.mdi-division::before {
-  content: "\F1D4";
-}
-.mdi-division-box::before {
-  content: "\F1D5";
-}
-.mdi-dlna::before {
-  content: "\FA40";
-}
-.mdi-dna::before {
-  content: "\F683";
-}
-.mdi-dns::before {
-  content: "\F1D6";
-}
-.mdi-dns-outline::before {
-  content: "\FB68";
-}
-.mdi-do-not-disturb::before {
-  content: "\F697";
-}
-.mdi-do-not-disturb-off::before {
-  content: "\F698";
-}
-.mdi-dock-bottom::before {
-  content: "\F00D4";
-}
-.mdi-dock-left::before {
-  content: "\F00D5";
-}
-.mdi-dock-right::before {
-  content: "\F00D6";
-}
-.mdi-dock-window::before {
-  content: "\F00D7";
-}
-.mdi-docker::before {
-  content: "\F867";
-}
-.mdi-doctor::before {
-  content: "\FA41";
-}
-.mdi-dog::before {
-  content: "\FA42";
-}
-.mdi-dog-service::before {
-  content: "\FAAC";
-}
-.mdi-dog-side::before {
-  content: "\FA43";
-}
-.mdi-dolby::before {
-  content: "\F6B2";
-}
-.mdi-dolly::before {
-  content: "\FEBB";
-}
-.mdi-domain::before {
-  content: "\F1D7";
-}
-.mdi-domain-off::before {
-  content: "\FD4B";
-}
-.mdi-domain-plus::before {
-  content: "\F00D8";
-}
-.mdi-domain-remove::before {
-  content: "\F00D9";
-}
-.mdi-domino-mask::before {
-  content: "\F0045";
-}
-.mdi-donkey::before {
-  content: "\F7C1";
-}
-.mdi-door::before {
-  content: "\F819";
-}
-.mdi-door-closed::before {
-  content: "\F81A";
-}
-.mdi-door-closed-lock::before {
-  content: "\F00DA";
-}
-.mdi-door-open::before {
-  content: "\F81B";
-}
-.mdi-doorbell::before {
-  content: "\F0311";
-}
-.mdi-doorbell-video::before {
-  content: "\F868";
-}
-.mdi-dot-net::before {
-  content: "\FAAD";
-}
-.mdi-dots-horizontal::before {
-  content: "\F1D8";
-}
-.mdi-dots-horizontal-circle::before {
-  content: "\F7C2";
-}
-.mdi-dots-horizontal-circle-outline::before {
-  content: "\FB69";
-}
-.mdi-dots-vertical::before {
-  content: "\F1D9";
-}
-.mdi-dots-vertical-circle::before {
-  content: "\F7C3";
-}
-.mdi-dots-vertical-circle-outline::before {
-  content: "\FB6A";
-}
-.mdi-douban::before {
-  content: "\F699";
-}
-.mdi-download::before {
-  content: "\F1DA";
-}
-.mdi-download-lock::before {
-  content: "\F034B";
-}
-.mdi-download-lock-outline::before {
-  content: "\F034C";
-}
-.mdi-download-multiple::before {
-  content: "\F9E8";
-}
-.mdi-download-network::before {
-  content: "\F6F3";
-}
-.mdi-download-network-outline::before {
-  content: "\FC42";
-}
-.mdi-download-off::before {
-  content: "\F00DB";
-}
-.mdi-download-off-outline::before {
-  content: "\F00DC";
-}
-.mdi-download-outline::before {
-  content: "\FB6B";
-}
-.mdi-drag::before {
-  content: "\F1DB";
-}
-.mdi-drag-horizontal::before {
-  content: "\F1DC";
-}
-.mdi-drag-horizontal-variant::before {
-  content: "\F031B";
-}
-.mdi-drag-variant::before {
-  content: "\FB6C";
-}
-.mdi-drag-vertical::before {
-  content: "\F1DD";
-}
-.mdi-drag-vertical-variant::before {
-  content: "\F031C";
-}
-.mdi-drama-masks::before {
-  content: "\FCDE";
-}
-.mdi-draw::before {
-  content: "\FF66";
-}
-.mdi-drawing::before {
-  content: "\F1DE";
-}
-.mdi-drawing-box::before {
-  content: "\F1DF";
-}
-.mdi-dresser::before {
-  content: "\FF67";
-}
-.mdi-dresser-outline::before {
-  content: "\FF68";
-}
-.mdi-dribbble::before {
-  content: "\F1E0";
-}
-.mdi-dribbble-box::before {
-  content: "\F1E1";
-}
-.mdi-drone::before {
-  content: "\F1E2";
-}
-.mdi-dropbox::before {
-  content: "\F1E3";
-}
-.mdi-drupal::before {
-  content: "\F1E4";
-}
-.mdi-duck::before {
-  content: "\F1E5";
-}
-.mdi-dumbbell::before {
-  content: "\F1E6";
-}
-.mdi-dump-truck::before {
-  content: "\FC43";
-}
-.mdi-ear-hearing::before {
-  content: "\F7C4";
-}
-.mdi-ear-hearing-off::before {
-  content: "\FA44";
-}
-.mdi-earth::before {
-  content: "\F1E7";
-}
-.mdi-earth-arrow-right::before {
-  content: "\F033C";
-}
-.mdi-earth-box::before {
-  content: "\F6CC";
-}
-.mdi-earth-box-off::before {
-  content: "\F6CD";
-}
-.mdi-earth-off::before {
-  content: "\F1E8";
-}
-.mdi-edge::before {
-  content: "\F1E9";
-}
-.mdi-edge-legacy::before {
-  content: "\F027B";
-}
-.mdi-egg::before {
-  content: "\FAAE";
-}
-.mdi-egg-easter::before {
-  content: "\FAAF";
-}
-.mdi-eight-track::before {
-  content: "\F9E9";
-}
-.mdi-eject::before {
-  content: "\F1EA";
-}
-.mdi-eject-outline::before {
-  content: "\FB6D";
-}
-.mdi-electric-switch::before {
-  content: "\FEBC";
-}
-.mdi-electric-switch-closed::before {
-  content: "\F0104";
-}
-.mdi-electron-framework::before {
-  content: "\F0046";
-}
-.mdi-elephant::before {
-  content: "\F7C5";
-}
-.mdi-elevation-decline::before {
-  content: "\F1EB";
-}
-.mdi-elevation-rise::before {
-  content: "\F1EC";
-}
-.mdi-elevator::before {
-  content: "\F1ED";
-}
-.mdi-elevator-down::before {
-  content: "\F02ED";
-}
-.mdi-elevator-passenger::before {
-  content: "\F03AC";
-}
-.mdi-elevator-up::before {
-  content: "\F02EC";
-}
-.mdi-ellipse::before {
-  content: "\FEBD";
-}
-.mdi-ellipse-outline::before {
-  content: "\FEBE";
-}
-.mdi-email::before {
-  content: "\F1EE";
-}
-.mdi-email-alert::before {
-  content: "\F6CE";
-}
-.mdi-email-alert-outline::before {
-  content: "\FD1E";
-}
-.mdi-email-box::before {
-  content: "\FCDF";
-}
-.mdi-email-check::before {
-  content: "\FAB0";
-}
-.mdi-email-check-outline::before {
-  content: "\FAB1";
-}
-.mdi-email-edit::before {
-  content: "\FF00";
-}
-.mdi-email-edit-outline::before {
-  content: "\FF01";
-}
-.mdi-email-lock::before {
-  content: "\F1F1";
-}
-.mdi-email-mark-as-unread::before {
-  content: "\FB6E";
-}
-.mdi-email-minus::before {
-  content: "\FF02";
-}
-.mdi-email-minus-outline::before {
-  content: "\FF03";
-}
-.mdi-email-multiple::before {
-  content: "\FF04";
-}
-.mdi-email-multiple-outline::before {
-  content: "\FF05";
-}
-.mdi-email-newsletter::before {
-  content: "\FFD1";
-}
-.mdi-email-open::before {
-  content: "\F1EF";
-}
-.mdi-email-open-multiple::before {
-  content: "\FF06";
-}
-.mdi-email-open-multiple-outline::before {
-  content: "\FF07";
-}
-.mdi-email-open-outline::before {
-  content: "\F5EF";
-}
-.mdi-email-outline::before {
-  content: "\F1F0";
-}
-.mdi-email-plus::before {
-  content: "\F9EA";
-}
-.mdi-email-plus-outline::before {
-  content: "\F9EB";
-}
-.mdi-email-receive::before {
-  content: "\F0105";
-}
-.mdi-email-receive-outline::before {
-  content: "\F0106";
-}
-.mdi-email-search::before {
-  content: "\F960";
-}
-.mdi-email-search-outline::before {
-  content: "\F961";
-}
-.mdi-email-send::before {
-  content: "\F0107";
-}
-.mdi-email-send-outline::before {
-  content: "\F0108";
-}
-.mdi-email-sync::before {
-  content: "\F02F2";
-}
-.mdi-email-sync-outline::before {
-  content: "\F02F3";
-}
-.mdi-email-variant::before {
-  content: "\F5F0";
-}
-.mdi-ember::before {
-  content: "\FB15";
-}
-.mdi-emby::before {
-  content: "\F6B3";
-}
-.mdi-emoticon::before {
-  content: "\FC44";
-}
-.mdi-emoticon-angry::before {
-  content: "\FC45";
-}
-.mdi-emoticon-angry-outline::before {
-  content: "\FC46";
-}
-.mdi-emoticon-confused::before {
-  content: "\F0109";
-}
-.mdi-emoticon-confused-outline::before {
-  content: "\F010A";
-}
-.mdi-emoticon-cool::before {
-  content: "\FC47";
-}
-.mdi-emoticon-cool-outline::before {
-  content: "\F1F3";
-}
-.mdi-emoticon-cry::before {
-  content: "\FC48";
-}
-.mdi-emoticon-cry-outline::before {
-  content: "\FC49";
-}
-.mdi-emoticon-dead::before {
-  content: "\FC4A";
-}
-.mdi-emoticon-dead-outline::before {
-  content: "\F69A";
-}
-.mdi-emoticon-devil::before {
-  content: "\FC4B";
-}
-.mdi-emoticon-devil-outline::before {
-  content: "\F1F4";
-}
-.mdi-emoticon-excited::before {
-  content: "\FC4C";
-}
-.mdi-emoticon-excited-outline::before {
-  content: "\F69B";
-}
-.mdi-emoticon-frown::before {
-  content: "\FF69";
-}
-.mdi-emoticon-frown-outline::before {
-  content: "\FF6A";
-}
-.mdi-emoticon-happy::before {
-  content: "\FC4D";
-}
-.mdi-emoticon-happy-outline::before {
-  content: "\F1F5";
-}
-.mdi-emoticon-kiss::before {
-  content: "\FC4E";
-}
-.mdi-emoticon-kiss-outline::before {
-  content: "\FC4F";
-}
-.mdi-emoticon-lol::before {
-  content: "\F023F";
-}
-.mdi-emoticon-lol-outline::before {
-  content: "\F0240";
-}
-.mdi-emoticon-neutral::before {
-  content: "\FC50";
-}
-.mdi-emoticon-neutral-outline::before {
-  content: "\F1F6";
-}
-.mdi-emoticon-outline::before {
-  content: "\F1F2";
-}
-.mdi-emoticon-poop::before {
-  content: "\F1F7";
-}
-.mdi-emoticon-poop-outline::before {
-  content: "\FC51";
-}
-.mdi-emoticon-sad::before {
-  content: "\FC52";
-}
-.mdi-emoticon-sad-outline::before {
-  content: "\F1F8";
-}
-.mdi-emoticon-tongue::before {
-  content: "\F1F9";
-}
-.mdi-emoticon-tongue-outline::before {
-  content: "\FC53";
-}
-.mdi-emoticon-wink::before {
-  content: "\FC54";
-}
-.mdi-emoticon-wink-outline::before {
-  content: "\FC55";
-}
-.mdi-engine::before {
-  content: "\F1FA";
-}
-.mdi-engine-off::before {
-  content: "\FA45";
-}
-.mdi-engine-off-outline::before {
-  content: "\FA46";
-}
-.mdi-engine-outline::before {
-  content: "\F1FB";
-}
-.mdi-epsilon::before {
-  content: "\F010B";
-}
-.mdi-equal::before {
-  content: "\F1FC";
-}
-.mdi-equal-box::before {
-  content: "\F1FD";
-}
-.mdi-equalizer::before {
-  content: "\FEBF";
-}
-.mdi-equalizer-outline::before {
-  content: "\FEC0";
-}
-.mdi-eraser::before {
-  content: "\F1FE";
-}
-.mdi-eraser-variant::before {
-  content: "\F642";
-}
-.mdi-escalator::before {
-  content: "\F1FF";
-}
-.mdi-escalator-down::before {
-  content: "\F02EB";
-}
-.mdi-escalator-up::before {
-  content: "\F02EA";
-}
-.mdi-eslint::before {
-  content: "\FC56";
-}
-.mdi-et::before {
-  content: "\FAB2";
-}
-.mdi-ethereum::before {
-  content: "\F869";
-}
-.mdi-ethernet::before {
-  content: "\F200";
-}
-.mdi-ethernet-cable::before {
-  content: "\F201";
-}
-.mdi-ethernet-cable-off::before {
-  content: "\F202";
-}
-.mdi-etsy::before {
-  content: "\F203";
-}
-.mdi-ev-station::before {
-  content: "\F5F1";
-}
-.mdi-eventbrite::before {
-  content: "\F7C6";
-}
-.mdi-evernote::before {
-  content: "\F204";
-}
-.mdi-excavator::before {
-  content: "\F0047";
-}
-.mdi-exclamation::before {
-  content: "\F205";
-}
-.mdi-exclamation-thick::before {
-  content: "\F0263";
-}
-.mdi-exit-run::before {
-  content: "\FA47";
-}
-.mdi-exit-to-app::before {
-  content: "\F206";
-}
-.mdi-expand-all::before {
-  content: "\FAB3";
-}
-.mdi-expand-all-outline::before {
-  content: "\FAB4";
-}
-.mdi-expansion-card::before {
-  content: "\F8AD";
-}
-.mdi-expansion-card-variant::before {
-  content: "\FFD2";
-}
-.mdi-exponent::before {
-  content: "\F962";
-}
-.mdi-exponent-box::before {
-  content: "\F963";
-}
-.mdi-export::before {
-  content: "\F207";
-}
-.mdi-export-variant::before {
-  content: "\FB6F";
-}
-.mdi-eye::before {
-  content: "\F208";
-}
-.mdi-eye-check::before {
-  content: "\FCE0";
-}
-.mdi-eye-check-outline::before {
-  content: "\FCE1";
-}
-.mdi-eye-circle::before {
-  content: "\FB70";
-}
-.mdi-eye-circle-outline::before {
-  content: "\FB71";
-}
-.mdi-eye-minus::before {
-  content: "\F0048";
-}
-.mdi-eye-minus-outline::before {
-  content: "\F0049";
-}
-.mdi-eye-off::before {
-  content: "\F209";
-}
-.mdi-eye-off-outline::before {
-  content: "\F6D0";
-}
-.mdi-eye-outline::before {
-  content: "\F6CF";
-}
-.mdi-eye-plus::before {
-  content: "\F86A";
-}
-.mdi-eye-plus-outline::before {
-  content: "\F86B";
-}
-.mdi-eye-settings::before {
-  content: "\F86C";
-}
-.mdi-eye-settings-outline::before {
-  content: "\F86D";
-}
-.mdi-eyedropper::before {
-  content: "\F20A";
-}
-.mdi-eyedropper-variant::before {
-  content: "\F20B";
-}
-.mdi-face::before {
-  content: "\F643";
-}
-.mdi-face-agent::before {
-  content: "\FD4C";
-}
-.mdi-face-outline::before {
-  content: "\FB72";
-}
-.mdi-face-profile::before {
-  content: "\F644";
-}
-.mdi-face-profile-woman::before {
-  content: "\F00A1";
-}
-.mdi-face-recognition::before {
-  content: "\FC57";
-}
-.mdi-face-woman::before {
-  content: "\F00A2";
-}
-.mdi-face-woman-outline::before {
-  content: "\F00A3";
-}
-.mdi-facebook::before {
-  content: "\F20C";
-}
-.mdi-facebook-box::before {
-  content: "\F20D";
-}
-.mdi-facebook-messenger::before {
-  content: "\F20E";
-}
-.mdi-facebook-workplace::before {
-  content: "\FB16";
-}
-.mdi-factory::before {
-  content: "\F20F";
-}
-.mdi-fan::before {
-  content: "\F210";
-}
-.mdi-fan-off::before {
-  content: "\F81C";
-}
-.mdi-fast-forward::before {
-  content: "\F211";
-}
-.mdi-fast-forward-10::before {
-  content: "\FD4D";
-}
-.mdi-fast-forward-30::before {
-  content: "\FCE2";
-}
-.mdi-fast-forward-5::before {
-  content: "\F0223";
-}
-.mdi-fast-forward-outline::before {
-  content: "\F6D1";
-}
-.mdi-fax::before {
-  content: "\F212";
-}
-.mdi-feather::before {
-  content: "\F6D2";
-}
-.mdi-feature-search::before {
-  content: "\FA48";
-}
-.mdi-feature-search-outline::before {
-  content: "\FA49";
-}
-.mdi-fedora::before {
-  content: "\F8DA";
-}
-.mdi-ferris-wheel::before {
-  content: "\FEC1";
-}
-.mdi-ferry::before {
-  content: "\F213";
-}
-.mdi-file::before {
-  content: "\F214";
-}
-.mdi-file-account::before {
-  content: "\F73A";
-}
-.mdi-file-account-outline::before {
-  content: "\F004A";
-}
-.mdi-file-alert::before {
-  content: "\FA4A";
-}
-.mdi-file-alert-outline::before {
-  content: "\FA4B";
-}
-.mdi-file-cabinet::before {
-  content: "\FAB5";
-}
-.mdi-file-cad::before {
-  content: "\FF08";
-}
-.mdi-file-cad-box::before {
-  content: "\FF09";
-}
-.mdi-file-cancel::before {
-  content: "\FDA2";
-}
-.mdi-file-cancel-outline::before {
-  content: "\FDA3";
-}
-.mdi-file-certificate::before {
-  content: "\F01B1";
-}
-.mdi-file-certificate-outline::before {
-  content: "\F01B2";
-}
-.mdi-file-chart::before {
-  content: "\F215";
-}
-.mdi-file-chart-outline::before {
-  content: "\F004B";
-}
-.mdi-file-check::before {
-  content: "\F216";
-}
-.mdi-file-check-outline::before {
-  content: "\FE7B";
-}
-.mdi-file-clock::before {
-  content: "\F030C";
-}
-.mdi-file-clock-outline::before {
-  content: "\F030D";
-}
-.mdi-file-cloud::before {
-  content: "\F217";
-}
-.mdi-file-cloud-outline::before {
-  content: "\F004C";
-}
-.mdi-file-code::before {
-  content: "\F22E";
-}
-.mdi-file-code-outline::before {
-  content: "\F004D";
-}
-.mdi-file-compare::before {
-  content: "\F8A9";
-}
-.mdi-file-delimited::before {
-  content: "\F218";
-}
-.mdi-file-delimited-outline::before {
-  content: "\FEC2";
-}
-.mdi-file-document::before {
-  content: "\F219";
-}
-.mdi-file-document-box::before {
-  content: "\F21A";
-}
-.mdi-file-document-box-check::before {
-  content: "\FEC3";
-}
-.mdi-file-document-box-check-outline::before {
-  content: "\FEC4";
-}
-.mdi-file-document-box-minus::before {
-  content: "\FEC5";
-}
-.mdi-file-document-box-minus-outline::before {
-  content: "\FEC6";
-}
-.mdi-file-document-box-multiple::before {
-  content: "\FAB6";
-}
-.mdi-file-document-box-multiple-outline::before {
-  content: "\FAB7";
-}
-.mdi-file-document-box-outline::before {
-  content: "\F9EC";
-}
-.mdi-file-document-box-plus::before {
-  content: "\FEC7";
-}
-.mdi-file-document-box-plus-outline::before {
-  content: "\FEC8";
-}
-.mdi-file-document-box-remove::before {
-  content: "\FEC9";
-}
-.mdi-file-document-box-remove-outline::before {
-  content: "\FECA";
-}
-.mdi-file-document-box-search::before {
-  content: "\FECB";
-}
-.mdi-file-document-box-search-outline::before {
-  content: "\FECC";
-}
-.mdi-file-document-edit::before {
-  content: "\FDA4";
-}
-.mdi-file-document-edit-outline::before {
-  content: "\FDA5";
-}
-.mdi-file-document-outline::before {
-  content: "\F9ED";
-}
-.mdi-file-download::before {
-  content: "\F964";
-}
-.mdi-file-download-outline::before {
-  content: "\F965";
-}
-.mdi-file-edit::before {
-  content: "\F0212";
-}
-.mdi-file-edit-outline::before {
-  content: "\F0213";
-}
-.mdi-file-excel::before {
-  content: "\F21B";
-}
-.mdi-file-excel-box::before {
-  content: "\F21C";
-}
-.mdi-file-excel-box-outline::before {
-  content: "\F004E";
-}
-.mdi-file-excel-outline::before {
-  content: "\F004F";
-}
-.mdi-file-export::before {
-  content: "\F21D";
-}
-.mdi-file-export-outline::before {
-  content: "\F0050";
-}
-.mdi-file-eye::before {
-  content: "\FDA6";
-}
-.mdi-file-eye-outline::before {
-  content: "\FDA7";
-}
-.mdi-file-find::before {
-  content: "\F21E";
-}
-.mdi-file-find-outline::before {
-  content: "\FB73";
-}
-.mdi-file-hidden::before {
-  content: "\F613";
-}
-.mdi-file-image::before {
-  content: "\F21F";
-}
-.mdi-file-image-outline::before {
-  content: "\FECD";
-}
-.mdi-file-import::before {
-  content: "\F220";
-}
-.mdi-file-import-outline::before {
-  content: "\F0051";
-}
-.mdi-file-key::before {
-  content: "\F01AF";
-}
-.mdi-file-key-outline::before {
-  content: "\F01B0";
-}
-.mdi-file-link::before {
-  content: "\F01A2";
-}
-.mdi-file-link-outline::before {
-  content: "\F01A3";
-}
-.mdi-file-lock::before {
-  content: "\F221";
-}
-.mdi-file-lock-outline::before {
-  content: "\F0052";
-}
-.mdi-file-move::before {
-  content: "\FAB8";
-}
-.mdi-file-move-outline::before {
-  content: "\F0053";
-}
-.mdi-file-multiple::before {
-  content: "\F222";
-}
-.mdi-file-multiple-outline::before {
-  content: "\F0054";
-}
-.mdi-file-music::before {
-  content: "\F223";
-}
-.mdi-file-music-outline::before {
-  content: "\FE7C";
-}
-.mdi-file-outline::before {
-  content: "\F224";
-}
-.mdi-file-pdf::before {
-  content: "\F225";
-}
-.mdi-file-pdf-box::before {
-  content: "\F226";
-}
-.mdi-file-pdf-box-outline::before {
-  content: "\FFD3";
-}
-.mdi-file-pdf-outline::before {
-  content: "\FE7D";
-}
-.mdi-file-percent::before {
-  content: "\F81D";
-}
-.mdi-file-percent-outline::before {
-  content: "\F0055";
-}
-.mdi-file-phone::before {
-  content: "\F01A4";
-}
-.mdi-file-phone-outline::before {
-  content: "\F01A5";
-}
-.mdi-file-plus::before {
-  content: "\F751";
-}
-.mdi-file-plus-outline::before {
-  content: "\FF0A";
-}
-.mdi-file-powerpoint::before {
-  content: "\F227";
-}
-.mdi-file-powerpoint-box::before {
-  content: "\F228";
-}
-.mdi-file-powerpoint-box-outline::before {
-  content: "\F0056";
-}
-.mdi-file-powerpoint-outline::before {
-  content: "\F0057";
-}
-.mdi-file-presentation-box::before {
-  content: "\F229";
-}
-.mdi-file-question::before {
-  content: "\F86E";
-}
-.mdi-file-question-outline::before {
-  content: "\F0058";
-}
-.mdi-file-remove::before {
-  content: "\FB74";
-}
-.mdi-file-remove-outline::before {
-  content: "\F0059";
-}
-.mdi-file-replace::before {
-  content: "\FB17";
-}
-.mdi-file-replace-outline::before {
-  content: "\FB18";
-}
-.mdi-file-restore::before {
-  content: "\F670";
-}
-.mdi-file-restore-outline::before {
-  content: "\F005A";
-}
-.mdi-file-search::before {
-  content: "\FC58";
-}
-.mdi-file-search-outline::before {
-  content: "\FC59";
-}
-.mdi-file-send::before {
-  content: "\F22A";
-}
-.mdi-file-send-outline::before {
-  content: "\F005B";
-}
-.mdi-file-settings::before {
-  content: "\F00A4";
-}
-.mdi-file-settings-outline::before {
-  content: "\F00A5";
-}
-.mdi-file-settings-variant::before {
-  content: "\F00A6";
-}
-.mdi-file-settings-variant-outline::before {
-  content: "\F00A7";
-}
-.mdi-file-star::before {
-  content: "\F005C";
-}
-.mdi-file-star-outline::before {
-  content: "\F005D";
-}
-.mdi-file-swap::before {
-  content: "\FFD4";
-}
-.mdi-file-swap-outline::before {
-  content: "\FFD5";
-}
-.mdi-file-sync::before {
-  content: "\F0241";
-}
-.mdi-file-sync-outline::before {
-  content: "\F0242";
-}
-.mdi-file-table::before {
-  content: "\FC5A";
-}
-.mdi-file-table-box::before {
-  content: "\F010C";
-}
-.mdi-file-table-box-multiple::before {
-  content: "\F010D";
-}
-.mdi-file-table-box-multiple-outline::before {
-  content: "\F010E";
-}
-.mdi-file-table-box-outline::before {
-  content: "\F010F";
-}
-.mdi-file-table-outline::before {
-  content: "\FC5B";
-}
-.mdi-file-tree::before {
-  content: "\F645";
-}
-.mdi-file-undo::before {
-  content: "\F8DB";
-}
-.mdi-file-undo-outline::before {
-  content: "\F005E";
-}
-.mdi-file-upload::before {
-  content: "\FA4C";
-}
-.mdi-file-upload-outline::before {
-  content: "\FA4D";
-}
-.mdi-file-video::before {
-  content: "\F22B";
-}
-.mdi-file-video-outline::before {
-  content: "\FE10";
-}
-.mdi-file-word::before {
-  content: "\F22C";
-}
-.mdi-file-word-box::before {
-  content: "\F22D";
-}
-.mdi-file-word-box-outline::before {
-  content: "\F005F";
-}
-.mdi-file-word-outline::before {
-  content: "\F0060";
-}
-.mdi-film::before {
-  content: "\F22F";
-}
-.mdi-filmstrip::before {
-  content: "\F230";
-}
-.mdi-filmstrip-off::before {
-  content: "\F231";
-}
-.mdi-filter::before {
-  content: "\F232";
-}
-.mdi-filter-menu::before {
-  content: "\F0110";
-}
-.mdi-filter-menu-outline::before {
-  content: "\F0111";
-}
-.mdi-filter-minus::before {
-  content: "\FF0B";
-}
-.mdi-filter-minus-outline::before {
-  content: "\FF0C";
-}
-.mdi-filter-outline::before {
-  content: "\F233";
-}
-.mdi-filter-plus::before {
-  content: "\FF0D";
-}
-.mdi-filter-plus-outline::before {
-  content: "\FF0E";
-}
-.mdi-filter-remove::before {
-  content: "\F234";
-}
-.mdi-filter-remove-outline::before {
-  content: "\F235";
-}
-.mdi-filter-variant::before {
-  content: "\F236";
-}
-.mdi-filter-variant-minus::before {
-  content: "\F013D";
-}
-.mdi-filter-variant-plus::before {
-  content: "\F013E";
-}
-.mdi-filter-variant-remove::before {
-  content: "\F0061";
-}
-.mdi-finance::before {
-  content: "\F81E";
-}
-.mdi-find-replace::before {
-  content: "\F6D3";
-}
-.mdi-fingerprint::before {
-  content: "\F237";
-}
-.mdi-fingerprint-off::before {
-  content: "\FECE";
-}
-.mdi-fire::before {
-  content: "\F238";
-}
-.mdi-fire-extinguisher::before {
-  content: "\FF0F";
-}
-.mdi-fire-hydrant::before {
-  content: "\F0162";
-}
-.mdi-fire-hydrant-alert::before {
-  content: "\F0163";
-}
-.mdi-fire-hydrant-off::before {
-  content: "\F0164";
-}
-.mdi-fire-truck::before {
-  content: "\F8AA";
-}
-.mdi-firebase::before {
-  content: "\F966";
-}
-.mdi-firefox::before {
-  content: "\F239";
-}
-.mdi-fireplace::before {
-  content: "\FE11";
-}
-.mdi-fireplace-off::before {
-  content: "\FE12";
-}
-.mdi-firework::before {
-  content: "\FE13";
-}
-.mdi-fish::before {
-  content: "\F23A";
-}
-.mdi-fishbowl::before {
-  content: "\FF10";
-}
-.mdi-fishbowl-outline::before {
-  content: "\FF11";
-}
-.mdi-fit-to-page::before {
-  content: "\FF12";
-}
-.mdi-fit-to-page-outline::before {
-  content: "\FF13";
-}
-.mdi-flag::before {
-  content: "\F23B";
-}
-.mdi-flag-checkered::before {
-  content: "\F23C";
-}
-.mdi-flag-minus::before {
-  content: "\FB75";
-}
-.mdi-flag-minus-outline::before {
-  content: "\F00DD";
-}
-.mdi-flag-outline::before {
-  content: "\F23D";
-}
-.mdi-flag-plus::before {
-  content: "\FB76";
-}
-.mdi-flag-plus-outline::before {
-  content: "\F00DE";
-}
-.mdi-flag-remove::before {
-  content: "\FB77";
-}
-.mdi-flag-remove-outline::before {
-  content: "\F00DF";
-}
-.mdi-flag-triangle::before {
-  content: "\F23F";
-}
-.mdi-flag-variant::before {
-  content: "\F240";
-}
-.mdi-flag-variant-outline::before {
-  content: "\F23E";
-}
-.mdi-flare::before {
-  content: "\FD4E";
-}
-.mdi-flash::before {
-  content: "\F241";
-}
-.mdi-flash-alert::before {
-  content: "\FF14";
-}
-.mdi-flash-alert-outline::before {
-  content: "\FF15";
-}
-.mdi-flash-auto::before {
-  content: "\F242";
-}
-.mdi-flash-circle::before {
-  content: "\F81F";
-}
-.mdi-flash-off::before {
-  content: "\F243";
-}
-.mdi-flash-outline::before {
-  content: "\F6D4";
-}
-.mdi-flash-red-eye::before {
-  content: "\F67A";
-}
-.mdi-flashlight::before {
-  content: "\F244";
-}
-.mdi-flashlight-off::before {
-  content: "\F245";
-}
-.mdi-flask::before {
-  content: "\F093";
-}
-.mdi-flask-empty::before {
-  content: "\F094";
-}
-.mdi-flask-empty-minus::before {
-  content: "\F0265";
-}
-.mdi-flask-empty-minus-outline::before {
-  content: "\F0266";
-}
-.mdi-flask-empty-outline::before {
-  content: "\F095";
-}
-.mdi-flask-empty-plus::before {
-  content: "\F0267";
-}
-.mdi-flask-empty-plus-outline::before {
-  content: "\F0268";
-}
-.mdi-flask-empty-remove::before {
-  content: "\F0269";
-}
-.mdi-flask-empty-remove-outline::before {
-  content: "\F026A";
-}
-.mdi-flask-minus::before {
-  content: "\F026B";
-}
-.mdi-flask-minus-outline::before {
-  content: "\F026C";
-}
-.mdi-flask-outline::before {
-  content: "\F096";
-}
-.mdi-flask-plus::before {
-  content: "\F026D";
-}
-.mdi-flask-plus-outline::before {
-  content: "\F026E";
-}
-.mdi-flask-remove::before {
-  content: "\F026F";
-}
-.mdi-flask-remove-outline::before {
-  content: "\F0270";
-}
-.mdi-flask-round-bottom::before {
-  content: "\F0276";
-}
-.mdi-flask-round-bottom-empty::before {
-  content: "\F0277";
-}
-.mdi-flask-round-bottom-empty-outline::before {
-  content: "\F0278";
-}
-.mdi-flask-round-bottom-outline::before {
-  content: "\F0279";
-}
-.mdi-flattr::before {
-  content: "\F246";
-}
-.mdi-fleur-de-lis::before {
-  content: "\F032E";
-}
-.mdi-flickr::before {
-  content: "\FCE3";
-}
-.mdi-flip-horizontal::before {
-  content: "\F0112";
-}
-.mdi-flip-to-back::before {
-  content: "\F247";
-}
-.mdi-flip-to-front::before {
-  content: "\F248";
-}
-.mdi-flip-vertical::before {
-  content: "\F0113";
-}
-.mdi-floor-lamp::before {
-  content: "\F8DC";
-}
-.mdi-floor-lamp-dual::before {
-  content: "\F0062";
-}
-.mdi-floor-lamp-variant::before {
-  content: "\F0063";
-}
-.mdi-floor-plan::before {
-  content: "\F820";
-}
-.mdi-floppy::before {
-  content: "\F249";
-}
-.mdi-floppy-variant::before {
-  content: "\F9EE";
-}
-.mdi-flower::before {
-  content: "\F24A";
-}
-.mdi-flower-outline::before {
-  content: "\F9EF";
-}
-.mdi-flower-poppy::before {
-  content: "\FCE4";
-}
-.mdi-flower-tulip::before {
-  content: "\F9F0";
-}
-.mdi-flower-tulip-outline::before {
-  content: "\F9F1";
-}
-.mdi-focus-auto::before {
-  content: "\FF6B";
-}
-.mdi-focus-field::before {
-  content: "\FF6C";
-}
-.mdi-focus-field-horizontal::before {
-  content: "\FF6D";
-}
-.mdi-focus-field-vertical::before {
-  content: "\FF6E";
-}
-.mdi-folder::before {
-  content: "\F24B";
-}
-.mdi-folder-account::before {
-  content: "\F24C";
-}
-.mdi-folder-account-outline::before {
-  content: "\FB78";
-}
-.mdi-folder-alert::before {
-  content: "\FDA8";
-}
-.mdi-folder-alert-outline::before {
-  content: "\FDA9";
-}
-.mdi-folder-clock::before {
-  content: "\FAB9";
-}
-.mdi-folder-clock-outline::before {
-  content: "\FABA";
-}
-.mdi-folder-download::before {
-  content: "\F24D";
-}
-.mdi-folder-download-outline::before {
-  content: "\F0114";
-}
-.mdi-folder-edit::before {
-  content: "\F8DD";
-}
-.mdi-folder-edit-outline::before {
-  content: "\FDAA";
-}
-.mdi-folder-google-drive::before {
-  content: "\F24E";
-}
-.mdi-folder-heart::before {
-  content: "\F0115";
-}
-.mdi-folder-heart-outline::before {
-  content: "\F0116";
-}
-.mdi-folder-home::before {
-  content: "\F00E0";
-}
-.mdi-folder-home-outline::before {
-  content: "\F00E1";
-}
-.mdi-folder-image::before {
-  content: "\F24F";
-}
-.mdi-folder-information::before {
-  content: "\F00E2";
-}
-.mdi-folder-information-outline::before {
-  content: "\F00E3";
-}
-.mdi-folder-key::before {
-  content: "\F8AB";
-}
-.mdi-folder-key-network::before {
-  content: "\F8AC";
-}
-.mdi-folder-key-network-outline::before {
-  content: "\FC5C";
-}
-.mdi-folder-key-outline::before {
-  content: "\F0117";
-}
-.mdi-folder-lock::before {
-  content: "\F250";
-}
-.mdi-folder-lock-open::before {
-  content: "\F251";
-}
-.mdi-folder-marker::before {
-  content: "\F0298";
-}
-.mdi-folder-marker-outline::before {
-  content: "\F0299";
-}
-.mdi-folder-move::before {
-  content: "\F252";
-}
-.mdi-folder-move-outline::before {
-  content: "\F0271";
-}
-.mdi-folder-multiple::before {
-  content: "\F253";
-}
-.mdi-folder-multiple-image::before {
-  content: "\F254";
-}
-.mdi-folder-multiple-outline::before {
-  content: "\F255";
-}
-.mdi-folder-music::before {
-  content: "\F0384";
-}
-.mdi-folder-music-outline::before {
-  content: "\F0385";
-}
-.mdi-folder-network::before {
-  content: "\F86F";
-}
-.mdi-folder-network-outline::before {
-  content: "\FC5D";
-}
-.mdi-folder-open::before {
-  content: "\F76F";
-}
-.mdi-folder-open-outline::before {
-  content: "\FDAB";
-}
-.mdi-folder-outline::before {
-  content: "\F256";
-}
-.mdi-folder-plus::before {
-  content: "\F257";
-}
-.mdi-folder-plus-outline::before {
-  content: "\FB79";
-}
-.mdi-folder-pound::before {
-  content: "\FCE5";
-}
-.mdi-folder-pound-outline::before {
-  content: "\FCE6";
-}
-.mdi-folder-remove::before {
-  content: "\F258";
-}
-.mdi-folder-remove-outline::before {
-  content: "\FB7A";
-}
-.mdi-folder-search::before {
-  content: "\F967";
-}
-.mdi-folder-search-outline::before {
-  content: "\F968";
-}
-.mdi-folder-settings::before {
-  content: "\F00A8";
-}
-.mdi-folder-settings-outline::before {
-  content: "\F00A9";
-}
-.mdi-folder-settings-variant::before {
-  content: "\F00AA";
-}
-.mdi-folder-settings-variant-outline::before {
-  content: "\F00AB";
-}
-.mdi-folder-star::before {
-  content: "\F69C";
-}
-.mdi-folder-star-outline::before {
-  content: "\FB7B";
-}
-.mdi-folder-swap::before {
-  content: "\FFD6";
-}
-.mdi-folder-swap-outline::before {
-  content: "\FFD7";
-}
-.mdi-folder-sync::before {
-  content: "\FCE7";
-}
-.mdi-folder-sync-outline::before {
-  content: "\FCE8";
-}
-.mdi-folder-table::before {
-  content: "\F030E";
-}
-.mdi-folder-table-outline::before {
-  content: "\F030F";
-}
-.mdi-folder-text::before {
-  content: "\FC5E";
-}
-.mdi-folder-text-outline::before {
-  content: "\FC5F";
-}
-.mdi-folder-upload::before {
-  content: "\F259";
-}
-.mdi-folder-upload-outline::before {
-  content: "\F0118";
-}
-.mdi-folder-zip::before {
-  content: "\F6EA";
-}
-.mdi-folder-zip-outline::before {
-  content: "\F7B8";
-}
-.mdi-font-awesome::before {
-  content: "\F03A";
-}
-.mdi-food::before {
-  content: "\F25A";
-}
-.mdi-food-apple::before {
-  content: "\F25B";
-}
-.mdi-food-apple-outline::before {
-  content: "\FC60";
-}
-.mdi-food-croissant::before {
-  content: "\F7C7";
-}
-.mdi-food-fork-drink::before {
-  content: "\F5F2";
-}
-.mdi-food-off::before {
-  content: "\F5F3";
-}
-.mdi-food-variant::before {
-  content: "\F25C";
-}
-.mdi-foot-print::before {
-  content: "\FF6F";
-}
-.mdi-football::before {
-  content: "\F25D";
-}
-.mdi-football-australian::before {
-  content: "\F25E";
-}
-.mdi-football-helmet::before {
-  content: "\F25F";
-}
-.mdi-forklift::before {
-  content: "\F7C8";
-}
-.mdi-format-align-bottom::before {
-  content: "\F752";
-}
-.mdi-format-align-center::before {
-  content: "\F260";
-}
-.mdi-format-align-justify::before {
-  content: "\F261";
-}
-.mdi-format-align-left::before {
-  content: "\F262";
-}
-.mdi-format-align-middle::before {
-  content: "\F753";
-}
-.mdi-format-align-right::before {
-  content: "\F263";
-}
-.mdi-format-align-top::before {
-  content: "\F754";
-}
-.mdi-format-annotation-minus::before {
-  content: "\FABB";
-}
-.mdi-format-annotation-plus::before {
-  content: "\F646";
-}
-.mdi-format-bold::before {
-  content: "\F264";
-}
-.mdi-format-clear::before {
-  content: "\F265";
-}
-.mdi-format-color-fill::before {
-  content: "\F266";
-}
-.mdi-format-color-highlight::before {
-  content: "\FE14";
-}
-.mdi-format-color-marker-cancel::before {
-  content: "\F033E";
-}
-.mdi-format-color-text::before {
-  content: "\F69D";
-}
-.mdi-format-columns::before {
-  content: "\F8DE";
-}
-.mdi-format-float-center::before {
-  content: "\F267";
-}
-.mdi-format-float-left::before {
-  content: "\F268";
-}
-.mdi-format-float-none::before {
-  content: "\F269";
-}
-.mdi-format-float-right::before {
-  content: "\F26A";
-}
-.mdi-format-font::before {
-  content: "\F6D5";
-}
-.mdi-format-font-size-decrease::before {
-  content: "\F9F2";
-}
-.mdi-format-font-size-increase::before {
-  content: "\F9F3";
-}
-.mdi-format-header-1::before {
-  content: "\F26B";
-}
-.mdi-format-header-2::before {
-  content: "\F26C";
-}
-.mdi-format-header-3::before {
-  content: "\F26D";
-}
-.mdi-format-header-4::before {
-  content: "\F26E";
-}
-.mdi-format-header-5::before {
-  content: "\F26F";
-}
-.mdi-format-header-6::before {
-  content: "\F270";
-}
-.mdi-format-header-decrease::before {
-  content: "\F271";
-}
-.mdi-format-header-equal::before {
-  content: "\F272";
-}
-.mdi-format-header-increase::before {
-  content: "\F273";
-}
-.mdi-format-header-pound::before {
-  content: "\F274";
-}
-.mdi-format-horizontal-align-center::before {
-  content: "\F61E";
-}
-.mdi-format-horizontal-align-left::before {
-  content: "\F61F";
-}
-.mdi-format-horizontal-align-right::before {
-  content: "\F620";
-}
-.mdi-format-indent-decrease::before {
-  content: "\F275";
-}
-.mdi-format-indent-increase::before {
-  content: "\F276";
-}
-.mdi-format-italic::before {
-  content: "\F277";
-}
-.mdi-format-letter-case::before {
-  content: "\FB19";
-}
-.mdi-format-letter-case-lower::before {
-  content: "\FB1A";
-}
-.mdi-format-letter-case-upper::before {
-  content: "\FB1B";
-}
-.mdi-format-letter-ends-with::before {
-  content: "\FFD8";
-}
-.mdi-format-letter-matches::before {
-  content: "\FFD9";
-}
-.mdi-format-letter-starts-with::before {
-  content: "\FFDA";
-}
-.mdi-format-line-spacing::before {
-  content: "\F278";
-}
-.mdi-format-line-style::before {
-  content: "\F5C8";
-}
-.mdi-format-line-weight::before {
-  content: "\F5C9";
-}
-.mdi-format-list-bulleted::before {
-  content: "\F279";
-}
-.mdi-format-list-bulleted-square::before {
-  content: "\FDAC";
-}
-.mdi-format-list-bulleted-triangle::before {
-  content: "\FECF";
-}
-.mdi-format-list-bulleted-type::before {
-  content: "\F27A";
-}
-.mdi-format-list-checkbox::before {
-  content: "\F969";
-}
-.mdi-format-list-checks::before {
-  content: "\F755";
-}
-.mdi-format-list-numbered::before {
-  content: "\F27B";
-}
-.mdi-format-list-numbered-rtl::before {
-  content: "\FCE9";
-}
-.mdi-format-list-text::before {
-  content: "\F029A";
-}
-.mdi-format-overline::before {
-  content: "\FED0";
-}
-.mdi-format-page-break::before {
-  content: "\F6D6";
-}
-.mdi-format-paint::before {
-  content: "\F27C";
-}
-.mdi-format-paragraph::before {
-  content: "\F27D";
-}
-.mdi-format-pilcrow::before {
-  content: "\F6D7";
-}
-.mdi-format-quote-close::before {
-  content: "\F27E";
-}
-.mdi-format-quote-close-outline::before {
-  content: "\F01D3";
-}
-.mdi-format-quote-open::before {
-  content: "\F756";
-}
-.mdi-format-quote-open-outline::before {
-  content: "\F01D2";
-}
-.mdi-format-rotate-90::before {
-  content: "\F6A9";
-}
-.mdi-format-section::before {
-  content: "\F69E";
-}
-.mdi-format-size::before {
-  content: "\F27F";
-}
-.mdi-format-strikethrough::before {
-  content: "\F280";
-}
-.mdi-format-strikethrough-variant::before {
-  content: "\F281";
-}
-.mdi-format-subscript::before {
-  content: "\F282";
-}
-.mdi-format-superscript::before {
-  content: "\F283";
-}
-.mdi-format-text::before {
-  content: "\F284";
-}
-.mdi-format-text-rotation-angle-down::before {
-  content: "\FFDB";
-}
-.mdi-format-text-rotation-angle-up::before {
-  content: "\FFDC";
-}
-.mdi-format-text-rotation-down::before {
-  content: "\FD4F";
-}
-.mdi-format-text-rotation-down-vertical::before {
-  content: "\FFDD";
-}
-.mdi-format-text-rotation-none::before {
-  content: "\FD50";
-}
-.mdi-format-text-rotation-up::before {
-  content: "\FFDE";
-}
-.mdi-format-text-rotation-vertical::before {
-  content: "\FFDF";
-}
-.mdi-format-text-variant::before {
-  content: "\FE15";
-}
-.mdi-format-text-wrapping-clip::before {
-  content: "\FCEA";
-}
-.mdi-format-text-wrapping-overflow::before {
-  content: "\FCEB";
-}
-.mdi-format-text-wrapping-wrap::before {
-  content: "\FCEC";
-}
-.mdi-format-textbox::before {
-  content: "\FCED";
-}
-.mdi-format-textdirection-l-to-r::before {
-  content: "\F285";
-}
-.mdi-format-textdirection-r-to-l::before {
-  content: "\F286";
-}
-.mdi-format-title::before {
-  content: "\F5F4";
-}
-.mdi-format-underline::before {
-  content: "\F287";
-}
-.mdi-format-vertical-align-bottom::before {
-  content: "\F621";
-}
-.mdi-format-vertical-align-center::before {
-  content: "\F622";
-}
-.mdi-format-vertical-align-top::before {
-  content: "\F623";
-}
-.mdi-format-wrap-inline::before {
-  content: "\F288";
-}
-.mdi-format-wrap-square::before {
-  content: "\F289";
-}
-.mdi-format-wrap-tight::before {
-  content: "\F28A";
-}
-.mdi-format-wrap-top-bottom::before {
-  content: "\F28B";
-}
-.mdi-forum::before {
-  content: "\F28C";
-}
-.mdi-forum-outline::before {
-  content: "\F821";
-}
-.mdi-forward::before {
-  content: "\F28D";
-}
-.mdi-forwardburger::before {
-  content: "\FD51";
-}
-.mdi-fountain::before {
-  content: "\F96A";
-}
-.mdi-fountain-pen::before {
-  content: "\FCEE";
-}
-.mdi-fountain-pen-tip::before {
-  content: "\FCEF";
-}
-.mdi-foursquare::before {
-  content: "\F28E";
-}
-.mdi-freebsd::before {
-  content: "\F8DF";
-}
-.mdi-frequently-asked-questions::before {
-  content: "\FED1";
-}
-.mdi-fridge::before {
-  content: "\F290";
-}
-.mdi-fridge-alert::before {
-  content: "\F01DC";
-}
-.mdi-fridge-alert-outline::before {
-  content: "\F01DD";
-}
-.mdi-fridge-bottom::before {
-  content: "\F292";
-}
-.mdi-fridge-off::before {
-  content: "\F01DA";
-}
-.mdi-fridge-off-outline::before {
-  content: "\F01DB";
-}
-.mdi-fridge-outline::before {
-  content: "\F28F";
-}
-.mdi-fridge-top::before {
-  content: "\F291";
-}
-.mdi-fruit-cherries::before {
-  content: "\F0064";
-}
-.mdi-fruit-citrus::before {
-  content: "\F0065";
-}
-.mdi-fruit-grapes::before {
-  content: "\F0066";
-}
-.mdi-fruit-grapes-outline::before {
-  content: "\F0067";
-}
-.mdi-fruit-pineapple::before {
-  content: "\F0068";
-}
-.mdi-fruit-watermelon::before {
-  content: "\F0069";
-}
-.mdi-fuel::before {
-  content: "\F7C9";
-}
-.mdi-fullscreen::before {
-  content: "\F293";
-}
-.mdi-fullscreen-exit::before {
-  content: "\F294";
-}
-.mdi-function::before {
-  content: "\F295";
-}
-.mdi-function-variant::before {
-  content: "\F870";
-}
-.mdi-furigana-horizontal::before {
-  content: "\F00AC";
-}
-.mdi-furigana-vertical::before {
-  content: "\F00AD";
-}
-.mdi-fuse::before {
-  content: "\FC61";
-}
-.mdi-fuse-blade::before {
-  content: "\FC62";
-}
-.mdi-gamepad::before {
-  content: "\F296";
-}
-.mdi-gamepad-circle::before {
-  content: "\FE16";
-}
-.mdi-gamepad-circle-down::before {
-  content: "\FE17";
-}
-.mdi-gamepad-circle-left::before {
-  content: "\FE18";
-}
-.mdi-gamepad-circle-outline::before {
-  content: "\FE19";
-}
-.mdi-gamepad-circle-right::before {
-  content: "\FE1A";
-}
-.mdi-gamepad-circle-up::before {
-  content: "\FE1B";
-}
-.mdi-gamepad-down::before {
-  content: "\FE1C";
-}
-.mdi-gamepad-left::before {
-  content: "\FE1D";
-}
-.mdi-gamepad-right::before {
-  content: "\FE1E";
-}
-.mdi-gamepad-round::before {
-  content: "\FE1F";
-}
-.mdi-gamepad-round-down::before {
-  content: "\FE7E";
-}
-.mdi-gamepad-round-left::before {
-  content: "\FE7F";
-}
-.mdi-gamepad-round-outline::before {
-  content: "\FE80";
-}
-.mdi-gamepad-round-right::before {
-  content: "\FE81";
-}
-.mdi-gamepad-round-up::before {
-  content: "\FE82";
-}
-.mdi-gamepad-square::before {
-  content: "\FED2";
-}
-.mdi-gamepad-square-outline::before {
-  content: "\FED3";
-}
-.mdi-gamepad-up::before {
-  content: "\FE83";
-}
-.mdi-gamepad-variant::before {
-  content: "\F297";
-}
-.mdi-gamepad-variant-outline::before {
-  content: "\FED4";
-}
-.mdi-gamma::before {
-  content: "\F0119";
-}
-.mdi-gantry-crane::before {
-  content: "\FDAD";
-}
-.mdi-garage::before {
-  content: "\F6D8";
-}
-.mdi-garage-alert::before {
-  content: "\F871";
-}
-.mdi-garage-alert-variant::before {
-  content: "\F0300";
-}
-.mdi-garage-open::before {
-  content: "\F6D9";
-}
-.mdi-garage-open-variant::before {
-  content: "\F02FF";
-}
-.mdi-garage-variant::before {
-  content: "\F02FE";
-}
-.mdi-gas-cylinder::before {
-  content: "\F647";
-}
-.mdi-gas-station::before {
-  content: "\F298";
-}
-.mdi-gas-station-outline::before {
-  content: "\FED5";
-}
-.mdi-gate::before {
-  content: "\F299";
-}
-.mdi-gate-and::before {
-  content: "\F8E0";
-}
-.mdi-gate-arrow-right::before {
-  content: "\F0194";
-}
-.mdi-gate-nand::before {
-  content: "\F8E1";
-}
-.mdi-gate-nor::before {
-  content: "\F8E2";
-}
-.mdi-gate-not::before {
-  content: "\F8E3";
-}
-.mdi-gate-open::before {
-  content: "\F0195";
-}
-.mdi-gate-or::before {
-  content: "\F8E4";
-}
-.mdi-gate-xnor::before {
-  content: "\F8E5";
-}
-.mdi-gate-xor::before {
-  content: "\F8E6";
-}
-.mdi-gatsby::before {
-  content: "\FE84";
-}
-.mdi-gauge::before {
-  content: "\F29A";
-}
-.mdi-gauge-empty::before {
-  content: "\F872";
-}
-.mdi-gauge-full::before {
-  content: "\F873";
-}
-.mdi-gauge-low::before {
-  content: "\F874";
-}
-.mdi-gavel::before {
-  content: "\F29B";
-}
-.mdi-gender-female::before {
-  content: "\F29C";
-}
-.mdi-gender-male::before {
-  content: "\F29D";
-}
-.mdi-gender-male-female::before {
-  content: "\F29E";
-}
-.mdi-gender-male-female-variant::before {
-  content: "\F016A";
-}
-.mdi-gender-non-binary::before {
-  content: "\F016B";
-}
-.mdi-gender-transgender::before {
-  content: "\F29F";
-}
-.mdi-gentoo::before {
-  content: "\F8E7";
-}
-.mdi-gesture::before {
-  content: "\F7CA";
-}
-.mdi-gesture-double-tap::before {
-  content: "\F73B";
-}
-.mdi-gesture-pinch::before {
-  content: "\FABC";
-}
-.mdi-gesture-spread::before {
-  content: "\FABD";
-}
-.mdi-gesture-swipe::before {
-  content: "\FD52";
-}
-.mdi-gesture-swipe-down::before {
-  content: "\F73C";
-}
-.mdi-gesture-swipe-horizontal::before {
-  content: "\FABE";
-}
-.mdi-gesture-swipe-left::before {
-  content: "\F73D";
-}
-.mdi-gesture-swipe-right::before {
-  content: "\F73E";
-}
-.mdi-gesture-swipe-up::before {
-  content: "\F73F";
-}
-.mdi-gesture-swipe-vertical::before {
-  content: "\FABF";
-}
-.mdi-gesture-tap::before {
-  content: "\F740";
-}
-.mdi-gesture-tap-box::before {
-  content: "\F02D4";
-}
-.mdi-gesture-tap-button::before {
-  content: "\F02D3";
-}
-.mdi-gesture-tap-hold::before {
-  content: "\FD53";
-}
-.mdi-gesture-two-double-tap::before {
-  content: "\F741";
-}
-.mdi-gesture-two-tap::before {
-  content: "\F742";
-}
-.mdi-ghost::before {
-  content: "\F2A0";
-}
-.mdi-ghost-off::before {
-  content: "\F9F4";
-}
-.mdi-gif::before {
-  content: "\FD54";
-}
-.mdi-gift::before {
-  content: "\FE85";
-}
-.mdi-gift-outline::before {
-  content: "\F2A1";
-}
-.mdi-git::before {
-  content: "\F2A2";
-}
-.mdi-github-box::before {
-  content: "\F2A3";
-}
-.mdi-github-circle::before {
-  content: "\F2A4";
-}
-.mdi-github-face::before {
-  content: "\F6DA";
-}
-.mdi-gitlab::before {
-  content: "\FB7C";
-}
-.mdi-glass-cocktail::before {
-  content: "\F356";
-}
-.mdi-glass-flute::before {
-  content: "\F2A5";
-}
-.mdi-glass-mug::before {
-  content: "\F2A6";
-}
-.mdi-glass-mug-variant::before {
-  content: "\F0141";
-}
-.mdi-glass-pint-outline::before {
-  content: "\F0338";
-}
-.mdi-glass-stange::before {
-  content: "\F2A7";
-}
-.mdi-glass-tulip::before {
-  content: "\F2A8";
-}
-.mdi-glass-wine::before {
-  content: "\F875";
-}
-.mdi-glassdoor::before {
-  content: "\F2A9";
-}
-.mdi-glasses::before {
-  content: "\F2AA";
-}
-.mdi-globe-light::before {
-  content: "\F0302";
-}
-.mdi-globe-model::before {
-  content: "\F8E8";
-}
-.mdi-gmail::before {
-  content: "\F2AB";
-}
-.mdi-gnome::before {
-  content: "\F2AC";
-}
-.mdi-go-kart::before {
-  content: "\FD55";
-}
-.mdi-go-kart-track::before {
-  content: "\FD56";
-}
-.mdi-gog::before {
-  content: "\FB7D";
-}
-.mdi-gold::before {
-  content: "\F027A";
-}
-.mdi-golf::before {
-  content: "\F822";
-}
-.mdi-golf-cart::before {
-  content: "\F01CF";
-}
-.mdi-golf-tee::before {
-  content: "\F00AE";
-}
-.mdi-gondola::before {
-  content: "\F685";
-}
-.mdi-goodreads::before {
-  content: "\FD57";
-}
-.mdi-google::before {
-  content: "\F2AD";
-}
-.mdi-google-adwords::before {
-  content: "\FC63";
-}
-.mdi-google-analytics::before {
-  content: "\F7CB";
-}
-.mdi-google-assistant::before {
-  content: "\F7CC";
-}
-.mdi-google-cardboard::before {
-  content: "\F2AE";
-}
-.mdi-google-chrome::before {
-  content: "\F2AF";
-}
-.mdi-google-circles::before {
-  content: "\F2B0";
-}
-.mdi-google-circles-communities::before {
-  content: "\F2B1";
-}
-.mdi-google-circles-extended::before {
-  content: "\F2B2";
-}
-.mdi-google-circles-group::before {
-  content: "\F2B3";
-}
-.mdi-google-classroom::before {
-  content: "\F2C0";
-}
-.mdi-google-cloud::before {
-  content: "\F0221";
-}
-.mdi-google-controller::before {
-  content: "\F2B4";
-}
-.mdi-google-controller-off::before {
-  content: "\F2B5";
-}
-.mdi-google-downasaur::before {
-  content: "\F038D";
-}
-.mdi-google-drive::before {
-  content: "\F2B6";
-}
-.mdi-google-earth::before {
-  content: "\F2B7";
-}
-.mdi-google-fit::before {
-  content: "\F96B";
-}
-.mdi-google-glass::before {
-  content: "\F2B8";
-}
-.mdi-google-hangouts::before {
-  content: "\F2C9";
-}
-.mdi-google-home::before {
-  content: "\F823";
-}
-.mdi-google-keep::before {
-  content: "\F6DB";
-}
-.mdi-google-lens::before {
-  content: "\F9F5";
-}
-.mdi-google-maps::before {
-  content: "\F5F5";
-}
-.mdi-google-my-business::before {
-  content: "\F006A";
-}
-.mdi-google-nearby::before {
-  content: "\F2B9";
-}
-.mdi-google-pages::before {
-  content: "\F2BA";
-}
-.mdi-google-photos::before {
-  content: "\F6DC";
-}
-.mdi-google-physical-web::before {
-  content: "\F2BB";
-}
-.mdi-google-play::before {
-  content: "\F2BC";
-}
-.mdi-google-plus::before {
-  content: "\F2BD";
-}
-.mdi-google-plus-box::before {
-  content: "\F2BE";
-}
-.mdi-google-podcast::before {
-  content: "\FED6";
-}
-.mdi-google-spreadsheet::before {
-  content: "\F9F6";
-}
-.mdi-google-street-view::before {
-  content: "\FC64";
-}
-.mdi-google-translate::before {
-  content: "\F2BF";
-}
-.mdi-gradient::before {
-  content: "\F69F";
-}
-.mdi-grain::before {
-  content: "\FD58";
-}
-.mdi-graph::before {
-  content: "\F006B";
-}
-.mdi-graph-outline::before {
-  content: "\F006C";
-}
-.mdi-graphql::before {
-  content: "\F876";
-}
-.mdi-grave-stone::before {
-  content: "\FB7E";
-}
-.mdi-grease-pencil::before {
-  content: "\F648";
-}
-.mdi-greater-than::before {
-  content: "\F96C";
-}
-.mdi-greater-than-or-equal::before {
-  content: "\F96D";
-}
-.mdi-grid::before {
-  content: "\F2C1";
-}
-.mdi-grid-large::before {
-  content: "\F757";
-}
-.mdi-grid-off::before {
-  content: "\F2C2";
-}
-.mdi-grill::before {
-  content: "\FE86";
-}
-.mdi-grill-outline::before {
-  content: "\F01B5";
-}
-.mdi-group::before {
-  content: "\F2C3";
-}
-.mdi-guitar-acoustic::before {
-  content: "\F770";
-}
-.mdi-guitar-electric::before {
-  content: "\F2C4";
-}
-.mdi-guitar-pick::before {
-  content: "\F2C5";
-}
-.mdi-guitar-pick-outline::before {
-  content: "\F2C6";
-}
-.mdi-guy-fawkes-mask::before {
-  content: "\F824";
-}
-.mdi-hackernews::before {
-  content: "\F624";
-}
-.mdi-hail::before {
-  content: "\FAC0";
-}
-.mdi-hair-dryer::before {
-  content: "\F011A";
-}
-.mdi-hair-dryer-outline::before {
-  content: "\F011B";
-}
-.mdi-halloween::before {
-  content: "\FB7F";
-}
-.mdi-hamburger::before {
-  content: "\F684";
-}
-.mdi-hammer::before {
-  content: "\F8E9";
-}
-.mdi-hammer-screwdriver::before {
-  content: "\F034D";
-}
-.mdi-hammer-wrench::before {
-  content: "\F034E";
-}
-.mdi-hand::before {
-  content: "\FA4E";
-}
-.mdi-hand-heart::before {
-  content: "\F011C";
-}
-.mdi-hand-left::before {
-  content: "\FE87";
-}
-.mdi-hand-okay::before {
-  content: "\FA4F";
-}
-.mdi-hand-peace::before {
-  content: "\FA50";
-}
-.mdi-hand-peace-variant::before {
-  content: "\FA51";
-}
-.mdi-hand-pointing-down::before {
-  content: "\FA52";
-}
-.mdi-hand-pointing-left::before {
-  content: "\FA53";
-}
-.mdi-hand-pointing-right::before {
-  content: "\F2C7";
-}
-.mdi-hand-pointing-up::before {
-  content: "\FA54";
-}
-.mdi-hand-right::before {
-  content: "\FE88";
-}
-.mdi-hand-saw::before {
-  content: "\FE89";
-}
-.mdi-handball::before {
-  content: "\FF70";
-}
-.mdi-handcuffs::before {
-  content: "\F0169";
-}
-.mdi-handshake::before {
-  content: "\F0243";
-}
-.mdi-hanger::before {
-  content: "\F2C8";
-}
-.mdi-hard-hat::before {
-  content: "\F96E";
-}
-.mdi-harddisk::before {
-  content: "\F2CA";
-}
-.mdi-harddisk-plus::before {
-  content: "\F006D";
-}
-.mdi-harddisk-remove::before {
-  content: "\F006E";
-}
-.mdi-hat-fedora::before {
-  content: "\FB80";
-}
-.mdi-hazard-lights::before {
-  content: "\FC65";
-}
-.mdi-hdr::before {
-  content: "\FD59";
-}
-.mdi-hdr-off::before {
-  content: "\FD5A";
-}
-.mdi-head::before {
-  content: "\F0389";
-}
-.mdi-head-alert::before {
-  content: "\F0363";
-}
-.mdi-head-alert-outline::before {
-  content: "\F0364";
-}
-.mdi-head-check::before {
-  content: "\F0365";
-}
-.mdi-head-check-outline::before {
-  content: "\F0366";
-}
-.mdi-head-cog::before {
-  content: "\F0367";
-}
-.mdi-head-cog-outline::before {
-  content: "\F0368";
-}
-.mdi-head-dots-horizontal::before {
-  content: "\F0369";
-}
-.mdi-head-dots-horizontal-outline::before {
-  content: "\F036A";
-}
-.mdi-head-flash::before {
-  content: "\F036B";
-}
-.mdi-head-flash-outline::before {
-  content: "\F036C";
-}
-.mdi-head-heart::before {
-  content: "\F036D";
-}
-.mdi-head-heart-outline::before {
-  content: "\F036E";
-}
-.mdi-head-lightbulb::before {
-  content: "\F036F";
-}
-.mdi-head-lightbulb-outline::before {
-  content: "\F0370";
-}
-.mdi-head-minus::before {
-  content: "\F0371";
-}
-.mdi-head-minus-outline::before {
-  content: "\F0372";
-}
-.mdi-head-outline::before {
-  content: "\F038A";
-}
-.mdi-head-plus::before {
-  content: "\F0373";
-}
-.mdi-head-plus-outline::before {
-  content: "\F0374";
-}
-.mdi-head-question::before {
-  content: "\F0375";
-}
-.mdi-head-question-outline::before {
-  content: "\F0376";
-}
-.mdi-head-remove::before {
-  content: "\F0377";
-}
-.mdi-head-remove-outline::before {
-  content: "\F0378";
-}
-.mdi-head-snowflake::before {
-  content: "\F0379";
-}
-.mdi-head-snowflake-outline::before {
-  content: "\F037A";
-}
-.mdi-head-sync::before {
-  content: "\F037B";
-}
-.mdi-head-sync-outline::before {
-  content: "\F037C";
-}
-.mdi-headphones::before {
-  content: "\F2CB";
-}
-.mdi-headphones-bluetooth::before {
-  content: "\F96F";
-}
-.mdi-headphones-box::before {
-  content: "\F2CC";
-}
-.mdi-headphones-off::before {
-  content: "\F7CD";
-}
-.mdi-headphones-settings::before {
-  content: "\F2CD";
-}
-.mdi-headset::before {
-  content: "\F2CE";
-}
-.mdi-headset-dock::before {
-  content: "\F2CF";
-}
-.mdi-headset-off::before {
-  content: "\F2D0";
-}
-.mdi-heart::before {
-  content: "\F2D1";
-}
-.mdi-heart-box::before {
-  content: "\F2D2";
-}
-.mdi-heart-box-outline::before {
-  content: "\F2D3";
-}
-.mdi-heart-broken::before {
-  content: "\F2D4";
-}
-.mdi-heart-broken-outline::before {
-  content: "\FCF0";
-}
-.mdi-heart-circle::before {
-  content: "\F970";
-}
-.mdi-heart-circle-outline::before {
-  content: "\F971";
-}
-.mdi-heart-flash::before {
-  content: "\FF16";
-}
-.mdi-heart-half::before {
-  content: "\F6DE";
-}
-.mdi-heart-half-full::before {
-  content: "\F6DD";
-}
-.mdi-heart-half-outline::before {
-  content: "\F6DF";
-}
-.mdi-heart-multiple::before {
-  content: "\FA55";
-}
-.mdi-heart-multiple-outline::before {
-  content: "\FA56";
-}
-.mdi-heart-off::before {
-  content: "\F758";
-}
-.mdi-heart-outline::before {
-  content: "\F2D5";
-}
-.mdi-heart-pulse::before {
-  content: "\F5F6";
-}
-.mdi-helicopter::before {
-  content: "\FAC1";
-}
-.mdi-help::before {
-  content: "\F2D6";
-}
-.mdi-help-box::before {
-  content: "\F78A";
-}
-.mdi-help-circle::before {
-  content: "\F2D7";
-}
-.mdi-help-circle-outline::before {
-  content: "\F625";
-}
-.mdi-help-network::before {
-  content: "\F6F4";
-}
-.mdi-help-network-outline::before {
-  content: "\FC66";
-}
-.mdi-help-rhombus::before {
-  content: "\FB81";
-}
-.mdi-help-rhombus-outline::before {
-  content: "\FB82";
-}
-.mdi-hexadecimal::before {
-  content: "\F02D2";
-}
-.mdi-hexagon::before {
-  content: "\F2D8";
-}
-.mdi-hexagon-multiple::before {
-  content: "\F6E0";
-}
-.mdi-hexagon-multiple-outline::before {
-  content: "\F011D";
-}
-.mdi-hexagon-outline::before {
-  content: "\F2D9";
-}
-.mdi-hexagon-slice-1::before {
-  content: "\FAC2";
-}
-.mdi-hexagon-slice-2::before {
-  content: "\FAC3";
-}
-.mdi-hexagon-slice-3::before {
-  content: "\FAC4";
-}
-.mdi-hexagon-slice-4::before {
-  content: "\FAC5";
-}
-.mdi-hexagon-slice-5::before {
-  content: "\FAC6";
-}
-.mdi-hexagon-slice-6::before {
-  content: "\FAC7";
-}
-.mdi-hexagram::before {
-  content: "\FAC8";
-}
-.mdi-hexagram-outline::before {
-  content: "\FAC9";
-}
-.mdi-high-definition::before {
-  content: "\F7CE";
-}
-.mdi-high-definition-box::before {
-  content: "\F877";
-}
-.mdi-highway::before {
-  content: "\F5F7";
-}
-.mdi-hiking::before {
-  content: "\FD5B";
-}
-.mdi-hinduism::before {
-  content: "\F972";
-}
-.mdi-history::before {
-  content: "\F2DA";
-}
-.mdi-hockey-puck::before {
-  content: "\F878";
-}
-.mdi-hockey-sticks::before {
-  content: "\F879";
-}
-.mdi-hololens::before {
-  content: "\F2DB";
-}
-.mdi-home::before {
-  content: "\F2DC";
-}
-.mdi-home-account::before {
-  content: "\F825";
-}
-.mdi-home-alert::before {
-  content: "\F87A";
-}
-.mdi-home-analytics::before {
-  content: "\FED7";
-}
-.mdi-home-assistant::before {
-  content: "\F7CF";
-}
-.mdi-home-automation::before {
-  content: "\F7D0";
-}
-.mdi-home-circle::before {
-  content: "\F7D1";
-}
-.mdi-home-circle-outline::before {
-  content: "\F006F";
-}
-.mdi-home-city::before {
-  content: "\FCF1";
-}
-.mdi-home-city-outline::before {
-  content: "\FCF2";
-}
-.mdi-home-currency-usd::before {
-  content: "\F8AE";
-}
-.mdi-home-edit::before {
-  content: "\F0184";
-}
-.mdi-home-edit-outline::before {
-  content: "\F0185";
-}
-.mdi-home-export-outline::before {
-  content: "\FFB8";
-}
-.mdi-home-flood::before {
-  content: "\FF17";
-}
-.mdi-home-floor-0::before {
-  content: "\FDAE";
-}
-.mdi-home-floor-1::before {
-  content: "\FD5C";
-}
-.mdi-home-floor-2::before {
-  content: "\FD5D";
-}
-.mdi-home-floor-3::before {
-  content: "\FD5E";
-}
-.mdi-home-floor-a::before {
-  content: "\FD5F";
-}
-.mdi-home-floor-b::before {
-  content: "\FD60";
-}
-.mdi-home-floor-g::before {
-  content: "\FD61";
-}
-.mdi-home-floor-l::before {
-  content: "\FD62";
-}
-.mdi-home-floor-negative-1::before {
-  content: "\FDAF";
-}
-.mdi-home-group::before {
-  content: "\FDB0";
-}
-.mdi-home-heart::before {
-  content: "\F826";
-}
-.mdi-home-import-outline::before {
-  content: "\FFB9";
-}
-.mdi-home-lightbulb::before {
-  content: "\F027C";
-}
-.mdi-home-lightbulb-outline::before {
-  content: "\F027D";
-}
-.mdi-home-lock::before {
-  content: "\F8EA";
-}
-.mdi-home-lock-open::before {
-  content: "\F8EB";
-}
-.mdi-home-map-marker::before {
-  content: "\F5F8";
-}
-.mdi-home-minus::before {
-  content: "\F973";
-}
-.mdi-home-modern::before {
-  content: "\F2DD";
-}
-.mdi-home-outline::before {
-  content: "\F6A0";
-}
-.mdi-home-plus::before {
-  content: "\F974";
-}
-.mdi-home-remove::before {
-  content: "\F0272";
-}
-.mdi-home-roof::before {
-  content: "\F0156";
-}
-.mdi-home-thermometer::before {
-  content: "\FF71";
-}
-.mdi-home-thermometer-outline::before {
-  content: "\FF72";
-}
-.mdi-home-variant::before {
-  content: "\F2DE";
-}
-.mdi-home-variant-outline::before {
-  content: "\FB83";
-}
-.mdi-hook::before {
-  content: "\F6E1";
-}
-.mdi-hook-off::before {
-  content: "\F6E2";
-}
-.mdi-hops::before {
-  content: "\F2DF";
-}
-.mdi-horizontal-rotate-clockwise::before {
-  content: "\F011E";
-}
-.mdi-horizontal-rotate-counterclockwise::before {
-  content: "\F011F";
-}
-.mdi-horseshoe::before {
-  content: "\FA57";
-}
-.mdi-hospital::before {
-  content: "\F0017";
-}
-.mdi-hospital-box::before {
-  content: "\F2E0";
-}
-.mdi-hospital-box-outline::before {
-  content: "\F0018";
-}
-.mdi-hospital-building::before {
-  content: "\F2E1";
-}
-.mdi-hospital-marker::before {
-  content: "\F2E2";
-}
-.mdi-hot-tub::before {
-  content: "\F827";
-}
-.mdi-hotel::before {
-  content: "\F2E3";
-}
-.mdi-houzz::before {
-  content: "\F2E4";
-}
-.mdi-houzz-box::before {
-  content: "\F2E5";
-}
-.mdi-hubspot::before {
-  content: "\FCF3";
-}
-.mdi-hulu::before {
-  content: "\F828";
-}
-.mdi-human::before {
-  content: "\F2E6";
-}
-.mdi-human-child::before {
-  content: "\F2E7";
-}
-.mdi-human-female::before {
-  content: "\F649";
-}
-.mdi-human-female-boy::before {
-  content: "\FA58";
-}
-.mdi-human-female-female::before {
-  content: "\FA59";
-}
-.mdi-human-female-girl::before {
-  content: "\FA5A";
-}
-.mdi-human-greeting::before {
-  content: "\F64A";
-}
-.mdi-human-handsdown::before {
-  content: "\F64B";
-}
-.mdi-human-handsup::before {
-  content: "\F64C";
-}
-.mdi-human-male::before {
-  content: "\F64D";
-}
-.mdi-human-male-boy::before {
-  content: "\FA5B";
-}
-.mdi-human-male-female::before {
-  content: "\F2E8";
-}
-.mdi-human-male-girl::before {
-  content: "\FA5C";
-}
-.mdi-human-male-height::before {
-  content: "\FF18";
-}
-.mdi-human-male-height-variant::before {
-  content: "\FF19";
-}
-.mdi-human-male-male::before {
-  content: "\FA5D";
-}
-.mdi-human-pregnant::before {
-  content: "\F5CF";
-}
-.mdi-humble-bundle::before {
-  content: "\F743";
-}
-.mdi-hvac::before {
-  content: "\F037D";
-}
-.mdi-hydraulic-oil-level::before {
-  content: "\F034F";
-}
-.mdi-hydraulic-oil-temperature::before {
-  content: "\F0350";
-}
-.mdi-hydro-power::before {
-  content: "\F0310";
-}
-.mdi-ice-cream::before {
-  content: "\F829";
-}
-.mdi-ice-pop::before {
-  content: "\FF1A";
-}
-.mdi-id-card::before {
-  content: "\FFE0";
-}
-.mdi-identifier::before {
-  content: "\FF1B";
-}
-.mdi-ideogram-cjk::before {
-  content: "\F035C";
-}
-.mdi-ideogram-cjk-variant::before {
-  content: "\F035D";
-}
-.mdi-iframe::before {
-  content: "\FC67";
-}
-.mdi-iframe-array::before {
-  content: "\F0120";
-}
-.mdi-iframe-array-outline::before {
-  content: "\F0121";
-}
-.mdi-iframe-braces::before {
-  content: "\F0122";
-}
-.mdi-iframe-braces-outline::before {
-  content: "\F0123";
-}
-.mdi-iframe-outline::before {
-  content: "\FC68";
-}
-.mdi-iframe-parentheses::before {
-  content: "\F0124";
-}
-.mdi-iframe-parentheses-outline::before {
-  content: "\F0125";
-}
-.mdi-iframe-variable::before {
-  content: "\F0126";
-}
-.mdi-iframe-variable-outline::before {
-  content: "\F0127";
-}
-.mdi-image::before {
-  content: "\F2E9";
-}
-.mdi-image-album::before {
-  content: "\F2EA";
-}
-.mdi-image-area::before {
-  content: "\F2EB";
-}
-.mdi-image-area-close::before {
-  content: "\F2EC";
-}
-.mdi-image-auto-adjust::before {
-  content: "\FFE1";
-}
-.mdi-image-broken::before {
-  content: "\F2ED";
-}
-.mdi-image-broken-variant::before {
-  content: "\F2EE";
-}
-.mdi-image-edit::before {
-  content: "\F020E";
-}
-.mdi-image-edit-outline::before {
-  content: "\F020F";
-}
-.mdi-image-filter::before {
-  content: "\F2EF";
-}
-.mdi-image-filter-black-white::before {
-  content: "\F2F0";
-}
-.mdi-image-filter-center-focus::before {
-  content: "\F2F1";
-}
-.mdi-image-filter-center-focus-strong::before {
-  content: "\FF1C";
-}
-.mdi-image-filter-center-focus-strong-outline::before {
-  content: "\FF1D";
-}
-.mdi-image-filter-center-focus-weak::before {
-  content: "\F2F2";
-}
-.mdi-image-filter-drama::before {
-  content: "\F2F3";
-}
-.mdi-image-filter-frames::before {
-  content: "\F2F4";
-}
-.mdi-image-filter-hdr::before {
-  content: "\F2F5";
-}
-.mdi-image-filter-none::before {
-  content: "\F2F6";
-}
-.mdi-image-filter-tilt-shift::before {
-  content: "\F2F7";
-}
-.mdi-image-filter-vintage::before {
-  content: "\F2F8";
-}
-.mdi-image-frame::before {
-  content: "\FE8A";
-}
-.mdi-image-move::before {
-  content: "\F9F7";
-}
-.mdi-image-multiple::before {
-  content: "\F2F9";
-}
-.mdi-image-off::before {
-  content: "\F82A";
-}
-.mdi-image-off-outline::before {
-  content: "\F01FC";
-}
-.mdi-image-outline::before {
-  content: "\F975";
-}
-.mdi-image-plus::before {
-  content: "\F87B";
-}
-.mdi-image-search::before {
-  content: "\F976";
-}
-.mdi-image-search-outline::before {
-  content: "\F977";
-}
-.mdi-image-size-select-actual::before {
-  content: "\FC69";
-}
-.mdi-image-size-select-large::before {
-  content: "\FC6A";
-}
-.mdi-image-size-select-small::before {
-  content: "\FC6B";
-}
-.mdi-import::before {
-  content: "\F2FA";
-}
-.mdi-inbox::before {
-  content: "\F686";
-}
-.mdi-inbox-arrow-down::before {
-  content: "\F2FB";
-}
-.mdi-inbox-arrow-down-outline::before {
-  content: "\F029B";
-}
-.mdi-inbox-arrow-up::before {
-  content: "\F3D1";
-}
-.mdi-inbox-arrow-up-outline::before {
-  content: "\F029C";
-}
-.mdi-inbox-full::before {
-  content: "\F029D";
-}
-.mdi-inbox-full-outline::before {
-  content: "\F029E";
-}
-.mdi-inbox-multiple::before {
-  content: "\F8AF";
-}
-.mdi-inbox-multiple-outline::before {
-  content: "\FB84";
-}
-.mdi-inbox-outline::before {
-  content: "\F029F";
-}
-.mdi-incognito::before {
-  content: "\F5F9";
-}
-.mdi-infinity::before {
-  content: "\F6E3";
-}
-.mdi-information::before {
-  content: "\F2FC";
-}
-.mdi-information-outline::before {
-  content: "\F2FD";
-}
-.mdi-information-variant::before {
-  content: "\F64E";
-}
-.mdi-instagram::before {
-  content: "\F2FE";
-}
-.mdi-instapaper::before {
-  content: "\F2FF";
-}
-.mdi-instrument-triangle::before {
-  content: "\F0070";
-}
-.mdi-internet-explorer::before {
-  content: "\F300";
-}
-.mdi-invert-colors::before {
-  content: "\F301";
-}
-.mdi-invert-colors-off::before {
-  content: "\FE8B";
-}
-.mdi-iobroker::before {
-  content: "\F0313";
-}
-.mdi-ip::before {
-  content: "\FA5E";
-}
-.mdi-ip-network::before {
-  content: "\FA5F";
-}
-.mdi-ip-network-outline::before {
-  content: "\FC6C";
-}
-.mdi-ipod::before {
-  content: "\FC6D";
-}
-.mdi-islam::before {
-  content: "\F978";
-}
-.mdi-island::before {
-  content: "\F0071";
-}
-.mdi-itunes::before {
-  content: "\F676";
-}
-.mdi-iv-bag::before {
-  content: "\F00E4";
-}
-.mdi-jabber::before {
-  content: "\FDB1";
-}
-.mdi-jeepney::before {
-  content: "\F302";
-}
-.mdi-jellyfish::before {
-  content: "\FF1E";
-}
-.mdi-jellyfish-outline::before {
-  content: "\FF1F";
-}
-.mdi-jira::before {
-  content: "\F303";
-}
-.mdi-jquery::before {
-  content: "\F87C";
-}
-.mdi-jsfiddle::before {
-  content: "\F304";
-}
-.mdi-json::before {
-  content: "\F626";
-}
-.mdi-judaism::before {
-  content: "\F979";
-}
-.mdi-jump-rope::before {
-  content: "\F032A";
-}
-.mdi-kabaddi::before {
-  content: "\FD63";
-}
-.mdi-karate::before {
-  content: "\F82B";
-}
-.mdi-keg::before {
-  content: "\F305";
-}
-.mdi-kettle::before {
-  content: "\F5FA";
-}
-.mdi-kettle-alert::before {
-  content: "\F0342";
-}
-.mdi-kettle-alert-outline::before {
-  content: "\F0343";
-}
-.mdi-kettle-off::before {
-  content: "\F0346";
-}
-.mdi-kettle-off-outline::before {
-  content: "\F0347";
-}
-.mdi-kettle-outline::before {
-  content: "\FF73";
-}
-.mdi-kettle-steam::before {
-  content: "\F0344";
-}
-.mdi-kettle-steam-outline::before {
-  content: "\F0345";
-}
-.mdi-kettlebell::before {
-  content: "\F032B";
-}
-.mdi-key::before {
-  content: "\F306";
-}
-.mdi-key-arrow-right::before {
-  content: "\F033D";
-}
-.mdi-key-change::before {
-  content: "\F307";
-}
-.mdi-key-link::before {
-  content: "\F01CA";
-}
-.mdi-key-minus::before {
-  content: "\F308";
-}
-.mdi-key-outline::before {
-  content: "\FDB2";
-}
-.mdi-key-plus::before {
-  content: "\F309";
-}
-.mdi-key-remove::before {
-  content: "\F30A";
-}
-.mdi-key-star::before {
-  content: "\F01C9";
-}
-.mdi-key-variant::before {
-  content: "\F30B";
-}
-.mdi-key-wireless::before {
-  content: "\FFE2";
-}
-.mdi-keyboard::before {
-  content: "\F30C";
-}
-.mdi-keyboard-backspace::before {
-  content: "\F30D";
-}
-.mdi-keyboard-caps::before {
-  content: "\F30E";
-}
-.mdi-keyboard-close::before {
-  content: "\F30F";
-}
-.mdi-keyboard-esc::before {
-  content: "\F02E2";
-}
-.mdi-keyboard-f1::before {
-  content: "\F02D6";
-}
-.mdi-keyboard-f10::before {
-  content: "\F02DF";
-}
-.mdi-keyboard-f11::before {
-  content: "\F02E0";
-}
-.mdi-keyboard-f12::before {
-  content: "\F02E1";
-}
-.mdi-keyboard-f2::before {
-  content: "\F02D7";
-}
-.mdi-keyboard-f3::before {
-  content: "\F02D8";
-}
-.mdi-keyboard-f4::before {
-  content: "\F02D9";
-}
-.mdi-keyboard-f5::before {
-  content: "\F02DA";
-}
-.mdi-keyboard-f6::before {
-  content: "\F02DB";
-}
-.mdi-keyboard-f7::before {
-  content: "\F02DC";
-}
-.mdi-keyboard-f8::before {
-  content: "\F02DD";
-}
-.mdi-keyboard-f9::before {
-  content: "\F02DE";
-}
-.mdi-keyboard-off::before {
-  content: "\F310";
-}
-.mdi-keyboard-off-outline::before {
-  content: "\FE8C";
-}
-.mdi-keyboard-outline::before {
-  content: "\F97A";
-}
-.mdi-keyboard-return::before {
-  content: "\F311";
-}
-.mdi-keyboard-settings::before {
-  content: "\F9F8";
-}
-.mdi-keyboard-settings-outline::before {
-  content: "\F9F9";
-}
-.mdi-keyboard-space::before {
-  content: "\F0072";
-}
-.mdi-keyboard-tab::before {
-  content: "\F312";
-}
-.mdi-keyboard-variant::before {
-  content: "\F313";
-}
-.mdi-khanda::before {
-  content: "\F0128";
-}
-.mdi-kickstarter::before {
-  content: "\F744";
-}
-.mdi-klingon::before {
-  content: "\F0386";
-}
-.mdi-knife::before {
-  content: "\F9FA";
-}
-.mdi-knife-military::before {
-  content: "\F9FB";
-}
-.mdi-kodi::before {
-  content: "\F314";
-}
-.mdi-kotlin::before {
-  content: "\F0244";
-}
-.mdi-kubernetes::before {
-  content: "\F0129";
-}
-.mdi-label::before {
-  content: "\F315";
-}
-.mdi-label-multiple::before {
-  content: "\F03A0";
-}
-.mdi-label-multiple-outline::before {
-  content: "\F03A1";
-}
-.mdi-label-off::before {
-  content: "\FACA";
-}
-.mdi-label-off-outline::before {
-  content: "\FACB";
-}
-.mdi-label-outline::before {
-  content: "\F316";
-}
-.mdi-label-percent::before {
-  content: "\F0315";
-}
-.mdi-label-percent-outline::before {
-  content: "\F0316";
-}
-.mdi-label-variant::before {
-  content: "\FACC";
-}
-.mdi-label-variant-outline::before {
-  content: "\FACD";
-}
-.mdi-ladybug::before {
-  content: "\F82C";
-}
-.mdi-lambda::before {
-  content: "\F627";
-}
-.mdi-lamp::before {
-  content: "\F6B4";
-}
-.mdi-lan::before {
-  content: "\F317";
-}
-.mdi-lan-check::before {
-  content: "\F02D5";
-}
-.mdi-lan-connect::before {
-  content: "\F318";
-}
-.mdi-lan-disconnect::before {
-  content: "\F319";
-}
-.mdi-lan-pending::before {
-  content: "\F31A";
-}
-.mdi-language-c::before {
-  content: "\F671";
-}
-.mdi-language-cpp::before {
-  content: "\F672";
-}
-.mdi-language-csharp::before {
-  content: "\F31B";
-}
-.mdi-language-css3::before {
-  content: "\F31C";
-}
-.mdi-language-fortran::before {
-  content: "\F0245";
-}
-.mdi-language-go::before {
-  content: "\F7D2";
-}
-.mdi-language-haskell::before {
-  content: "\FC6E";
-}
-.mdi-language-html5::before {
-  content: "\F31D";
-}
-.mdi-language-java::before {
-  content: "\FB1C";
-}
-.mdi-language-javascript::before {
-  content: "\F31E";
-}
-.mdi-language-lua::before {
-  content: "\F8B0";
-}
-.mdi-language-php::before {
-  content: "\F31F";
-}
-.mdi-language-python::before {
-  content: "\F320";
-}
-.mdi-language-python-text::before {
-  content: "\F321";
-}
-.mdi-language-r::before {
-  content: "\F7D3";
-}
-.mdi-language-ruby-on-rails::before {
-  content: "\FACE";
-}
-.mdi-language-swift::before {
-  content: "\F6E4";
-}
-.mdi-language-typescript::before {
-  content: "\F6E5";
-}
-.mdi-laptop::before {
-  content: "\F322";
-}
-.mdi-laptop-chromebook::before {
-  content: "\F323";
-}
-.mdi-laptop-mac::before {
-  content: "\F324";
-}
-.mdi-laptop-off::before {
-  content: "\F6E6";
-}
-.mdi-laptop-windows::before {
-  content: "\F325";
-}
-.mdi-laravel::before {
-  content: "\FACF";
-}
-.mdi-lasso::before {
-  content: "\FF20";
-}
-.mdi-lastfm::before {
-  content: "\F326";
-}
-.mdi-lastpass::before {
-  content: "\F446";
-}
-.mdi-latitude::before {
-  content: "\FF74";
-}
-.mdi-launch::before {
-  content: "\F327";
-}
-.mdi-lava-lamp::before {
-  content: "\F7D4";
-}
-.mdi-layers::before {
-  content: "\F328";
-}
-.mdi-layers-minus::before {
-  content: "\FE8D";
-}
-.mdi-layers-off::before {
-  content: "\F329";
-}
-.mdi-layers-off-outline::before {
-  content: "\F9FC";
-}
-.mdi-layers-outline::before {
-  content: "\F9FD";
-}
-.mdi-layers-plus::before {
-  content: "\FE30";
-}
-.mdi-layers-remove::before {
-  content: "\FE31";
-}
-.mdi-layers-search::before {
-  content: "\F0231";
-}
-.mdi-layers-search-outline::before {
-  content: "\F0232";
-}
-.mdi-layers-triple::before {
-  content: "\FF75";
-}
-.mdi-layers-triple-outline::before {
-  content: "\FF76";
-}
-.mdi-lead-pencil::before {
-  content: "\F64F";
-}
-.mdi-leaf::before {
-  content: "\F32A";
-}
-.mdi-leaf-maple::before {
-  content: "\FC6F";
-}
-.mdi-leaf-maple-off::before {
-  content: "\F0305";
-}
-.mdi-leaf-off::before {
-  content: "\F0304";
-}
-.mdi-leak::before {
-  content: "\FDB3";
-}
-.mdi-leak-off::before {
-  content: "\FDB4";
-}
-.mdi-led-off::before {
-  content: "\F32B";
-}
-.mdi-led-on::before {
-  content: "\F32C";
-}
-.mdi-led-outline::before {
-  content: "\F32D";
-}
-.mdi-led-strip::before {
-  content: "\F7D5";
-}
-.mdi-led-strip-variant::before {
-  content: "\F0073";
-}
-.mdi-led-variant-off::before {
-  content: "\F32E";
-}
-.mdi-led-variant-on::before {
-  content: "\F32F";
-}
-.mdi-led-variant-outline::before {
-  content: "\F330";
-}
-.mdi-leek::before {
-  content: "\F01A8";
-}
-.mdi-less-than::before {
-  content: "\F97B";
-}
-.mdi-less-than-or-equal::before {
-  content: "\F97C";
-}
-.mdi-library::before {
-  content: "\F331";
-}
-.mdi-library-books::before {
-  content: "\F332";
-}
-.mdi-library-movie::before {
-  content: "\FCF4";
-}
-.mdi-library-music::before {
-  content: "\F333";
-}
-.mdi-library-music-outline::before {
-  content: "\FF21";
-}
-.mdi-library-shelves::before {
-  content: "\FB85";
-}
-.mdi-library-video::before {
-  content: "\FCF5";
-}
-.mdi-license::before {
-  content: "\FFE3";
-}
-.mdi-lifebuoy::before {
-  content: "\F87D";
-}
-.mdi-light-switch::before {
-  content: "\F97D";
-}
-.mdi-lightbulb::before {
-  content: "\F335";
-}
-.mdi-lightbulb-cfl::before {
-  content: "\F0233";
-}
-.mdi-lightbulb-cfl-off::before {
-  content: "\F0234";
-}
-.mdi-lightbulb-cfl-spiral::before {
-  content: "\F02A0";
-}
-.mdi-lightbulb-cfl-spiral-off::before {
-  content: "\F02EE";
-}
-.mdi-lightbulb-group::before {
-  content: "\F027E";
-}
-.mdi-lightbulb-group-off::before {
-  content: "\F02F8";
-}
-.mdi-lightbulb-group-off-outline::before {
-  content: "\F02F9";
-}
-.mdi-lightbulb-group-outline::before {
-  content: "\F027F";
-}
-.mdi-lightbulb-multiple::before {
-  content: "\F0280";
-}
-.mdi-lightbulb-multiple-off::before {
-  content: "\F02FA";
-}
-.mdi-lightbulb-multiple-off-outline::before {
-  content: "\F02FB";
-}
-.mdi-lightbulb-multiple-outline::before {
-  content: "\F0281";
-}
-.mdi-lightbulb-off::before {
-  content: "\FE32";
-}
-.mdi-lightbulb-off-outline::before {
-  content: "\FE33";
-}
-.mdi-lightbulb-on::before {
-  content: "\F6E7";
-}
-.mdi-lightbulb-on-outline::before {
-  content: "\F6E8";
-}
-.mdi-lightbulb-outline::before {
-  content: "\F336";
-}
-.mdi-lighthouse::before {
-  content: "\F9FE";
-}
-.mdi-lighthouse-on::before {
-  content: "\F9FF";
-}
-.mdi-link::before {
-  content: "\F337";
-}
-.mdi-link-box::before {
-  content: "\FCF6";
-}
-.mdi-link-box-outline::before {
-  content: "\FCF7";
-}
-.mdi-link-box-variant::before {
-  content: "\FCF8";
-}
-.mdi-link-box-variant-outline::before {
-  content: "\FCF9";
-}
-.mdi-link-lock::before {
-  content: "\F00E5";
-}
-.mdi-link-off::before {
-  content: "\F338";
-}
-.mdi-link-plus::before {
-  content: "\FC70";
-}
-.mdi-link-variant::before {
-  content: "\F339";
-}
-.mdi-link-variant-minus::before {
-  content: "\F012A";
-}
-.mdi-link-variant-off::before {
-  content: "\F33A";
-}
-.mdi-link-variant-plus::before {
-  content: "\F012B";
-}
-.mdi-link-variant-remove::before {
-  content: "\F012C";
-}
-.mdi-linkedin::before {
-  content: "\F33B";
-}
-.mdi-linkedin-box::before {
-  content: "\F33C";
-}
-.mdi-linux::before {
-  content: "\F33D";
-}
-.mdi-linux-mint::before {
-  content: "\F8EC";
-}
-.mdi-litecoin::before {
-  content: "\FA60";
-}
-.mdi-loading::before {
-  content: "\F771";
-}
-.mdi-location-enter::before {
-  content: "\FFE4";
-}
-.mdi-location-exit::before {
-  content: "\FFE5";
-}
-.mdi-lock::before {
-  content: "\F33E";
-}
-.mdi-lock-alert::before {
-  content: "\F8ED";
-}
-.mdi-lock-clock::before {
-  content: "\F97E";
-}
-.mdi-lock-open::before {
-  content: "\F33F";
-}
-.mdi-lock-open-outline::before {
-  content: "\F340";
-}
-.mdi-lock-open-variant::before {
-  content: "\FFE6";
-}
-.mdi-lock-open-variant-outline::before {
-  content: "\FFE7";
-}
-.mdi-lock-outline::before {
-  content: "\F341";
-}
-.mdi-lock-pattern::before {
-  content: "\F6E9";
-}
-.mdi-lock-plus::before {
-  content: "\F5FB";
-}
-.mdi-lock-question::before {
-  content: "\F8EE";
-}
-.mdi-lock-reset::before {
-  content: "\F772";
-}
-.mdi-lock-smart::before {
-  content: "\F8B1";
-}
-.mdi-locker::before {
-  content: "\F7D6";
-}
-.mdi-locker-multiple::before {
-  content: "\F7D7";
-}
-.mdi-login::before {
-  content: "\F342";
-}
-.mdi-login-variant::before {
-  content: "\F5FC";
-}
-.mdi-logout::before {
-  content: "\F343";
-}
-.mdi-logout-variant::before {
-  content: "\F5FD";
-}
-.mdi-longitude::before {
-  content: "\FF77";
-}
-.mdi-looks::before {
-  content: "\F344";
-}
-.mdi-loupe::before {
-  content: "\F345";
-}
-.mdi-lumx::before {
-  content: "\F346";
-}
-.mdi-lungs::before {
-  content: "\F00AF";
-}
-.mdi-lyft::before {
-  content: "\FB1D";
-}
-.mdi-magnet::before {
-  content: "\F347";
-}
-.mdi-magnet-on::before {
-  content: "\F348";
-}
-.mdi-magnify::before {
-  content: "\F349";
-}
-.mdi-magnify-close::before {
-  content: "\F97F";
-}
-.mdi-magnify-minus::before {
-  content: "\F34A";
-}
-.mdi-magnify-minus-cursor::before {
-  content: "\FA61";
-}
-.mdi-magnify-minus-outline::before {
-  content: "\F6EB";
-}
-.mdi-magnify-plus::before {
-  content: "\F34B";
-}
-.mdi-magnify-plus-cursor::before {
-  content: "\FA62";
-}
-.mdi-magnify-plus-outline::before {
-  content: "\F6EC";
-}
-.mdi-magnify-remove-cursor::before {
-  content: "\F0237";
-}
-.mdi-magnify-remove-outline::before {
-  content: "\F0238";
-}
-.mdi-magnify-scan::before {
-  content: "\F02A1";
-}
-.mdi-mail::before {
-  content: "\FED8";
-}
-.mdi-mail-ru::before {
-  content: "\F34C";
-}
-.mdi-mailbox::before {
-  content: "\F6ED";
-}
-.mdi-mailbox-open::before {
-  content: "\FD64";
-}
-.mdi-mailbox-open-outline::before {
-  content: "\FD65";
-}
-.mdi-mailbox-open-up::before {
-  content: "\FD66";
-}
-.mdi-mailbox-open-up-outline::before {
-  content: "\FD67";
-}
-.mdi-mailbox-outline::before {
-  content: "\FD68";
-}
-.mdi-mailbox-up::before {
-  content: "\FD69";
-}
-.mdi-mailbox-up-outline::before {
-  content: "\FD6A";
-}
-.mdi-map::before {
-  content: "\F34D";
-}
-.mdi-map-check::before {
-  content: "\FED9";
-}
-.mdi-map-check-outline::before {
-  content: "\FEDA";
-}
-.mdi-map-clock::before {
-  content: "\FCFA";
-}
-.mdi-map-clock-outline::before {
-  content: "\FCFB";
-}
-.mdi-map-legend::before {
-  content: "\FA00";
-}
-.mdi-map-marker::before {
-  content: "\F34E";
-}
-.mdi-map-marker-alert::before {
-  content: "\FF22";
-}
-.mdi-map-marker-alert-outline::before {
-  content: "\FF23";
-}
-.mdi-map-marker-check::before {
-  content: "\FC71";
-}
-.mdi-map-marker-check-outline::before {
-  content: "\F0326";
-}
-.mdi-map-marker-circle::before {
-  content: "\F34F";
-}
-.mdi-map-marker-distance::before {
-  content: "\F8EF";
-}
-.mdi-map-marker-down::before {
-  content: "\F012D";
-}
-.mdi-map-marker-left::before {
-  content: "\F0306";
-}
-.mdi-map-marker-left-outline::before {
-  content: "\F0308";
-}
-.mdi-map-marker-minus::before {
-  content: "\F650";
-}
-.mdi-map-marker-minus-outline::before {
-  content: "\F0324";
-}
-.mdi-map-marker-multiple::before {
-  content: "\F350";
-}
-.mdi-map-marker-multiple-outline::before {
-  content: "\F02A2";
-}
-.mdi-map-marker-off::before {
-  content: "\F351";
-}
-.mdi-map-marker-off-outline::before {
-  content: "\F0328";
-}
-.mdi-map-marker-outline::before {
-  content: "\F7D8";
-}
-.mdi-map-marker-path::before {
-  content: "\FCFC";
-}
-.mdi-map-marker-plus::before {
-  content: "\F651";
-}
-.mdi-map-marker-plus-outline::before {
-  content: "\F0323";
-}
-.mdi-map-marker-question::before {
-  content: "\FF24";
-}
-.mdi-map-marker-question-outline::before {
-  content: "\FF25";
-}
-.mdi-map-marker-radius::before {
-  content: "\F352";
-}
-.mdi-map-marker-radius-outline::before {
-  content: "\F0327";
-}
-.mdi-map-marker-remove::before {
-  content: "\FF26";
-}
-.mdi-map-marker-remove-outline::before {
-  content: "\F0325";
-}
-.mdi-map-marker-remove-variant::before {
-  content: "\FF27";
-}
-.mdi-map-marker-right::before {
-  content: "\F0307";
-}
-.mdi-map-marker-right-outline::before {
-  content: "\F0309";
-}
-.mdi-map-marker-up::before {
-  content: "\F012E";
-}
-.mdi-map-minus::before {
-  content: "\F980";
-}
-.mdi-map-outline::before {
-  content: "\F981";
-}
-.mdi-map-plus::before {
-  content: "\F982";
-}
-.mdi-map-search::before {
-  content: "\F983";
-}
-.mdi-map-search-outline::before {
-  content: "\F984";
-}
-.mdi-mapbox::before {
-  content: "\FB86";
-}
-.mdi-margin::before {
-  content: "\F353";
-}
-.mdi-markdown::before {
-  content: "\F354";
-}
-.mdi-markdown-outline::before {
-  content: "\FF78";
-}
-.mdi-marker::before {
-  content: "\F652";
-}
-.mdi-marker-cancel::before {
-  content: "\FDB5";
-}
-.mdi-marker-check::before {
-  content: "\F355";
-}
-.mdi-mastodon::before {
-  content: "\FAD0";
-}
-.mdi-mastodon-variant::before {
-  content: "\FAD1";
-}
-.mdi-material-design::before {
-  content: "\F985";
-}
-.mdi-material-ui::before {
-  content: "\F357";
-}
-.mdi-math-compass::before {
-  content: "\F358";
-}
-.mdi-math-cos::before {
-  content: "\FC72";
-}
-.mdi-math-integral::before {
-  content: "\FFE8";
-}
-.mdi-math-integral-box::before {
-  content: "\FFE9";
-}
-.mdi-math-log::before {
-  content: "\F00B0";
-}
-.mdi-math-norm::before {
-  content: "\FFEA";
-}
-.mdi-math-norm-box::before {
-  content: "\FFEB";
-}
-.mdi-math-sin::before {
-  content: "\FC73";
-}
-.mdi-math-tan::before {
-  content: "\FC74";
-}
-.mdi-matrix::before {
-  content: "\F628";
-}
-.mdi-medal::before {
-  content: "\F986";
-}
-.mdi-medal-outline::before {
-  content: "\F0351";
-}
-.mdi-medical-bag::before {
-  content: "\F6EE";
-}
-.mdi-meditation::before {
-  content: "\F01A6";
-}
-.mdi-medium::before {
-  content: "\F35A";
-}
-.mdi-meetup::before {
-  content: "\FAD2";
-}
-.mdi-memory::before {
-  content: "\F35B";
-}
-.mdi-menu::before {
-  content: "\F35C";
-}
-.mdi-menu-down::before {
-  content: "\F35D";
-}
-.mdi-menu-down-outline::before {
-  content: "\F6B5";
-}
-.mdi-menu-left::before {
-  content: "\F35E";
-}
-.mdi-menu-left-outline::before {
-  content: "\FA01";
-}
-.mdi-menu-open::before {
-  content: "\FB87";
-}
-.mdi-menu-right::before {
-  content: "\F35F";
-}
-.mdi-menu-right-outline::before {
-  content: "\FA02";
-}
-.mdi-menu-swap::before {
-  content: "\FA63";
-}
-.mdi-menu-swap-outline::before {
-  content: "\FA64";
-}
-.mdi-menu-up::before {
-  content: "\F360";
-}
-.mdi-menu-up-outline::before {
-  content: "\F6B6";
-}
-.mdi-merge::before {
-  content: "\FF79";
-}
-.mdi-message::before {
-  content: "\F361";
-}
-.mdi-message-alert::before {
-  content: "\F362";
-}
-.mdi-message-alert-outline::before {
-  content: "\FA03";
-}
-.mdi-message-arrow-left::before {
-  content: "\F031D";
-}
-.mdi-message-arrow-left-outline::before {
-  content: "\F031E";
-}
-.mdi-message-arrow-right::before {
-  content: "\F031F";
-}
-.mdi-message-arrow-right-outline::before {
-  content: "\F0320";
-}
-.mdi-message-bulleted::before {
-  content: "\F6A1";
-}
-.mdi-message-bulleted-off::before {
-  content: "\F6A2";
-}
-.mdi-message-draw::before {
-  content: "\F363";
-}
-.mdi-message-image::before {
-  content: "\F364";
-}
-.mdi-message-image-outline::before {
-  content: "\F0197";
-}
-.mdi-message-lock::before {
-  content: "\FFEC";
-}
-.mdi-message-lock-outline::before {
-  content: "\F0198";
-}
-.mdi-message-minus::before {
-  content: "\F0199";
-}
-.mdi-message-minus-outline::before {
-  content: "\F019A";
-}
-.mdi-message-outline::before {
-  content: "\F365";
-}
-.mdi-message-plus::before {
-  content: "\F653";
-}
-.mdi-message-plus-outline::before {
-  content: "\F00E6";
-}
-.mdi-message-processing::before {
-  content: "\F366";
-}
-.mdi-message-processing-outline::before {
-  content: "\F019B";
-}
-.mdi-message-reply::before {
-  content: "\F367";
-}
-.mdi-message-reply-text::before {
-  content: "\F368";
-}
-.mdi-message-settings::before {
-  content: "\F6EF";
-}
-.mdi-message-settings-outline::before {
-  content: "\F019C";
-}
-.mdi-message-settings-variant::before {
-  content: "\F6F0";
-}
-.mdi-message-settings-variant-outline::before {
-  content: "\F019D";
-}
-.mdi-message-text::before {
-  content: "\F369";
-}
-.mdi-message-text-clock::before {
-  content: "\F019E";
-}
-.mdi-message-text-clock-outline::before {
-  content: "\F019F";
-}
-.mdi-message-text-lock::before {
-  content: "\FFED";
-}
-.mdi-message-text-lock-outline::before {
-  content: "\F01A0";
-}
-.mdi-message-text-outline::before {
-  content: "\F36A";
-}
-.mdi-message-video::before {
-  content: "\F36B";
-}
-.mdi-meteor::before {
-  content: "\F629";
-}
-.mdi-metronome::before {
-  content: "\F7D9";
-}
-.mdi-metronome-tick::before {
-  content: "\F7DA";
-}
-.mdi-micro-sd::before {
-  content: "\F7DB";
-}
-.mdi-microphone::before {
-  content: "\F36C";
-}
-.mdi-microphone-minus::before {
-  content: "\F8B2";
-}
-.mdi-microphone-off::before {
-  content: "\F36D";
-}
-.mdi-microphone-outline::before {
-  content: "\F36E";
-}
-.mdi-microphone-plus::before {
-  content: "\F8B3";
-}
-.mdi-microphone-settings::before {
-  content: "\F36F";
-}
-.mdi-microphone-variant::before {
-  content: "\F370";
-}
-.mdi-microphone-variant-off::before {
-  content: "\F371";
-}
-.mdi-microscope::before {
-  content: "\F654";
-}
-.mdi-microsoft::before {
-  content: "\F372";
-}
-.mdi-microsoft-dynamics::before {
-  content: "\F987";
-}
-.mdi-microwave::before {
-  content: "\FC75";
-}
-.mdi-middleware::before {
-  content: "\FF7A";
-}
-.mdi-middleware-outline::before {
-  content: "\FF7B";
-}
-.mdi-midi::before {
-  content: "\F8F0";
-}
-.mdi-midi-port::before {
-  content: "\F8F1";
-}
-.mdi-mine::before {
-  content: "\FDB6";
-}
-.mdi-minecraft::before {
-  content: "\F373";
-}
-.mdi-mini-sd::before {
-  content: "\FA04";
-}
-.mdi-minidisc::before {
-  content: "\FA05";
-}
-.mdi-minus::before {
-  content: "\F374";
-}
-.mdi-minus-box::before {
-  content: "\F375";
-}
-.mdi-minus-box-multiple::before {
-  content: "\F016C";
-}
-.mdi-minus-box-multiple-outline::before {
-  content: "\F016D";
-}
-.mdi-minus-box-outline::before {
-  content: "\F6F1";
-}
-.mdi-minus-circle::before {
-  content: "\F376";
-}
-.mdi-minus-circle-outline::before {
-  content: "\F377";
-}
-.mdi-minus-network::before {
-  content: "\F378";
-}
-.mdi-minus-network-outline::before {
-  content: "\FC76";
-}
-.mdi-mirror::before {
-  content: "\F0228";
-}
-.mdi-mixcloud::before {
-  content: "\F62A";
-}
-.mdi-mixed-martial-arts::before {
-  content: "\FD6B";
-}
-.mdi-mixed-reality::before {
-  content: "\F87E";
-}
-.mdi-mixer::before {
-  content: "\F7DC";
-}
-.mdi-molecule::before {
-  content: "\FB88";
-}
-.mdi-monitor::before {
-  content: "\F379";
-}
-.mdi-monitor-cellphone::before {
-  content: "\F988";
-}
-.mdi-monitor-cellphone-star::before {
-  content: "\F989";
-}
-.mdi-monitor-clean::before {
-  content: "\F012F";
-}
-.mdi-monitor-dashboard::before {
-  content: "\FA06";
-}
-.mdi-monitor-edit::before {
-  content: "\F02F1";
-}
-.mdi-monitor-lock::before {
-  content: "\FDB7";
-}
-.mdi-monitor-multiple::before {
-  content: "\F37A";
-}
-.mdi-monitor-off::before {
-  content: "\FD6C";
-}
-.mdi-monitor-screenshot::before {
-  content: "\FE34";
-}
-.mdi-monitor-speaker::before {
-  content: "\FF7C";
-}
-.mdi-monitor-speaker-off::before {
-  content: "\FF7D";
-}
-.mdi-monitor-star::before {
-  content: "\FDB8";
-}
-.mdi-moon-first-quarter::before {
-  content: "\FF7E";
-}
-.mdi-moon-full::before {
-  content: "\FF7F";
-}
-.mdi-moon-last-quarter::before {
-  content: "\FF80";
-}
-.mdi-moon-new::before {
-  content: "\FF81";
-}
-.mdi-moon-waning-crescent::before {
-  content: "\FF82";
-}
-.mdi-moon-waning-gibbous::before {
-  content: "\FF83";
-}
-.mdi-moon-waxing-crescent::before {
-  content: "\FF84";
-}
-.mdi-moon-waxing-gibbous::before {
-  content: "\FF85";
-}
-.mdi-moped::before {
-  content: "\F00B1";
-}
-.mdi-more::before {
-  content: "\F37B";
-}
-.mdi-mother-heart::before {
-  content: "\F033F";
-}
-.mdi-mother-nurse::before {
-  content: "\FCFD";
-}
-.mdi-motion-sensor::before {
-  content: "\FD6D";
-}
-.mdi-motorbike::before {
-  content: "\F37C";
-}
-.mdi-mouse::before {
-  content: "\F37D";
-}
-.mdi-mouse-bluetooth::before {
-  content: "\F98A";
-}
-.mdi-mouse-off::before {
-  content: "\F37E";
-}
-.mdi-mouse-variant::before {
-  content: "\F37F";
-}
-.mdi-mouse-variant-off::before {
-  content: "\F380";
-}
-.mdi-move-resize::before {
-  content: "\F655";
-}
-.mdi-move-resize-variant::before {
-  content: "\F656";
-}
-.mdi-movie::before {
-  content: "\F381";
-}
-.mdi-movie-edit::before {
-  content: "\F014D";
-}
-.mdi-movie-edit-outline::before {
-  content: "\F014E";
-}
-.mdi-movie-filter::before {
-  content: "\F014F";
-}
-.mdi-movie-filter-outline::before {
-  content: "\F0150";
-}
-.mdi-movie-open::before {
-  content: "\FFEE";
-}
-.mdi-movie-open-outline::before {
-  content: "\FFEF";
-}
-.mdi-movie-outline::before {
-  content: "\FDB9";
-}
-.mdi-movie-roll::before {
-  content: "\F7DD";
-}
-.mdi-movie-search::before {
-  content: "\F01FD";
-}
-.mdi-movie-search-outline::before {
-  content: "\F01FE";
-}
-.mdi-muffin::before {
-  content: "\F98B";
-}
-.mdi-multiplication::before {
-  content: "\F382";
-}
-.mdi-multiplication-box::before {
-  content: "\F383";
-}
-.mdi-mushroom::before {
-  content: "\F7DE";
-}
-.mdi-mushroom-outline::before {
-  content: "\F7DF";
-}
-.mdi-music::before {
-  content: "\F759";
-}
-.mdi-music-accidental-double-flat::before {
-  content: "\FF86";
-}
-.mdi-music-accidental-double-sharp::before {
-  content: "\FF87";
-}
-.mdi-music-accidental-flat::before {
-  content: "\FF88";
-}
-.mdi-music-accidental-natural::before {
-  content: "\FF89";
-}
-.mdi-music-accidental-sharp::before {
-  content: "\FF8A";
-}
-.mdi-music-box::before {
-  content: "\F384";
-}
-.mdi-music-box-outline::before {
-  content: "\F385";
-}
-.mdi-music-circle::before {
-  content: "\F386";
-}
-.mdi-music-circle-outline::before {
-  content: "\FAD3";
-}
-.mdi-music-clef-alto::before {
-  content: "\FF8B";
-}
-.mdi-music-clef-bass::before {
-  content: "\FF8C";
-}
-.mdi-music-clef-treble::before {
-  content: "\FF8D";
-}
-.mdi-music-note::before {
-  content: "\F387";
-}
-.mdi-music-note-bluetooth::before {
-  content: "\F5FE";
-}
-.mdi-music-note-bluetooth-off::before {
-  content: "\F5FF";
-}
-.mdi-music-note-eighth::before {
-  content: "\F388";
-}
-.mdi-music-note-eighth-dotted::before {
-  content: "\FF8E";
-}
-.mdi-music-note-half::before {
-  content: "\F389";
-}
-.mdi-music-note-half-dotted::before {
-  content: "\FF8F";
-}
-.mdi-music-note-off::before {
-  content: "\F38A";
-}
-.mdi-music-note-off-outline::before {
-  content: "\FF90";
-}
-.mdi-music-note-outline::before {
-  content: "\FF91";
-}
-.mdi-music-note-plus::before {
-  content: "\FDBA";
-}
-.mdi-music-note-quarter::before {
-  content: "\F38B";
-}
-.mdi-music-note-quarter-dotted::before {
-  content: "\FF92";
-}
-.mdi-music-note-sixteenth::before {
-  content: "\F38C";
-}
-.mdi-music-note-sixteenth-dotted::before {
-  content: "\FF93";
-}
-.mdi-music-note-whole::before {
-  content: "\F38D";
-}
-.mdi-music-note-whole-dotted::before {
-  content: "\FF94";
-}
-.mdi-music-off::before {
-  content: "\F75A";
-}
-.mdi-music-rest-eighth::before {
-  content: "\FF95";
-}
-.mdi-music-rest-half::before {
-  content: "\FF96";
-}
-.mdi-music-rest-quarter::before {
-  content: "\FF97";
-}
-.mdi-music-rest-sixteenth::before {
-  content: "\FF98";
-}
-.mdi-music-rest-whole::before {
-  content: "\FF99";
-}
-.mdi-nail::before {
-  content: "\FDBB";
-}
-.mdi-nas::before {
-  content: "\F8F2";
-}
-.mdi-nativescript::before {
-  content: "\F87F";
-}
-.mdi-nature::before {
-  content: "\F38E";
-}
-.mdi-nature-people::before {
-  content: "\F38F";
-}
-.mdi-navigation::before {
-  content: "\F390";
-}
-.mdi-near-me::before {
-  content: "\F5CD";
-}
-.mdi-necklace::before {
-  content: "\FF28";
-}
-.mdi-needle::before {
-  content: "\F391";
-}
-.mdi-netflix::before {
-  content: "\F745";
-}
-.mdi-network::before {
-  content: "\F6F2";
-}
-.mdi-network-off::before {
-  content: "\FC77";
-}
-.mdi-network-off-outline::before {
-  content: "\FC78";
-}
-.mdi-network-outline::before {
-  content: "\FC79";
-}
-.mdi-network-router::before {
-  content: "\F00B2";
-}
-.mdi-network-strength-1::before {
-  content: "\F8F3";
-}
-.mdi-network-strength-1-alert::before {
-  content: "\F8F4";
-}
-.mdi-network-strength-2::before {
-  content: "\F8F5";
-}
-.mdi-network-strength-2-alert::before {
-  content: "\F8F6";
-}
-.mdi-network-strength-3::before {
-  content: "\F8F7";
-}
-.mdi-network-strength-3-alert::before {
-  content: "\F8F8";
-}
-.mdi-network-strength-4::before {
-  content: "\F8F9";
-}
-.mdi-network-strength-4-alert::before {
-  content: "\F8FA";
-}
-.mdi-network-strength-off::before {
-  content: "\F8FB";
-}
-.mdi-network-strength-off-outline::before {
-  content: "\F8FC";
-}
-.mdi-network-strength-outline::before {
-  content: "\F8FD";
-}
-.mdi-new-box::before {
-  content: "\F394";
-}
-.mdi-newspaper::before {
-  content: "\F395";
-}
-.mdi-newspaper-minus::before {
-  content: "\FF29";
-}
-.mdi-newspaper-plus::before {
-  content: "\FF2A";
-}
-.mdi-newspaper-variant::before {
-  content: "\F0023";
-}
-.mdi-newspaper-variant-multiple::before {
-  content: "\F0024";
-}
-.mdi-newspaper-variant-multiple-outline::before {
-  content: "\F0025";
-}
-.mdi-newspaper-variant-outline::before {
-  content: "\F0026";
-}
-.mdi-nfc::before {
-  content: "\F396";
-}
-.mdi-nfc-off::before {
-  content: "\FE35";
-}
-.mdi-nfc-search-variant::before {
-  content: "\FE36";
-}
-.mdi-nfc-tap::before {
-  content: "\F397";
-}
-.mdi-nfc-variant::before {
-  content: "\F398";
-}
-.mdi-nfc-variant-off::before {
-  content: "\FE37";
-}
-.mdi-ninja::before {
-  content: "\F773";
-}
-.mdi-nintendo-switch::before {
-  content: "\F7E0";
-}
-.mdi-nix::before {
-  content: "\F0130";
-}
-.mdi-nodejs::before {
-  content: "\F399";
-}
-.mdi-noodles::before {
-  content: "\F01A9";
-}
-.mdi-not-equal::before {
-  content: "\F98C";
-}
-.mdi-not-equal-variant::before {
-  content: "\F98D";
-}
-.mdi-note::before {
-  content: "\F39A";
-}
-.mdi-note-multiple::before {
-  content: "\F6B7";
-}
-.mdi-note-multiple-outline::before {
-  content: "\F6B8";
-}
-.mdi-note-outline::before {
-  content: "\F39B";
-}
-.mdi-note-plus::before {
-  content: "\F39C";
-}
-.mdi-note-plus-outline::before {
-  content: "\F39D";
-}
-.mdi-note-text::before {
-  content: "\F39E";
-}
-.mdi-note-text-outline::before {
-  content: "\F0202";
-}
-.mdi-notebook::before {
-  content: "\F82D";
-}
-.mdi-notebook-multiple::before {
-  content: "\FE38";
-}
-.mdi-notebook-outline::before {
-  content: "\FEDC";
-}
-.mdi-notification-clear-all::before {
-  content: "\F39F";
-}
-.mdi-npm::before {
-  content: "\F6F6";
-}
-.mdi-npm-variant::before {
-  content: "\F98E";
-}
-.mdi-npm-variant-outline::before {
-  content: "\F98F";
-}
-.mdi-nuke::before {
-  content: "\F6A3";
-}
-.mdi-null::before {
-  content: "\F7E1";
-}
-.mdi-numeric::before {
-  content: "\F3A0";
-}
-.mdi-numeric-0::before {
-  content: "\30";
-}
-.mdi-numeric-0-box::before {
-  content: "\F3A1";
-}
-.mdi-numeric-0-box-multiple::before {
-  content: "\FF2B";
-}
-.mdi-numeric-0-box-multiple-outline::before {
-  content: "\F3A2";
-}
-.mdi-numeric-0-box-outline::before {
-  content: "\F3A3";
-}
-.mdi-numeric-0-circle::before {
-  content: "\FC7A";
-}
-.mdi-numeric-0-circle-outline::before {
-  content: "\FC7B";
-}
-.mdi-numeric-1::before {
-  content: "\31";
-}
-.mdi-numeric-1-box::before {
-  content: "\F3A4";
-}
-.mdi-numeric-1-box-multiple::before {
-  content: "\FF2C";
-}
-.mdi-numeric-1-box-multiple-outline::before {
-  content: "\F3A5";
-}
-.mdi-numeric-1-box-outline::before {
-  content: "\F3A6";
-}
-.mdi-numeric-1-circle::before {
-  content: "\FC7C";
-}
-.mdi-numeric-1-circle-outline::before {
-  content: "\FC7D";
-}
-.mdi-numeric-10::before {
-  content: "\F000A";
-}
-.mdi-numeric-10-box::before {
-  content: "\FF9A";
-}
-.mdi-numeric-10-box-multiple::before {
-  content: "\F000B";
-}
-.mdi-numeric-10-box-multiple-outline::before {
-  content: "\F000C";
-}
-.mdi-numeric-10-box-outline::before {
-  content: "\FF9B";
-}
-.mdi-numeric-10-circle::before {
-  content: "\F000D";
-}
-.mdi-numeric-10-circle-outline::before {
-  content: "\F000E";
-}
-.mdi-numeric-2::before {
-  content: "\32";
-}
-.mdi-numeric-2-box::before {
-  content: "\F3A7";
-}
-.mdi-numeric-2-box-multiple::before {
-  content: "\FF2D";
-}
-.mdi-numeric-2-box-multiple-outline::before {
-  content: "\F3A8";
-}
-.mdi-numeric-2-box-outline::before {
-  content: "\F3A9";
-}
-.mdi-numeric-2-circle::before {
-  content: "\FC7E";
-}
-.mdi-numeric-2-circle-outline::before {
-  content: "\FC7F";
-}
-.mdi-numeric-3::before {
-  content: "\33";
-}
-.mdi-numeric-3-box::before {
-  content: "\F3AA";
-}
-.mdi-numeric-3-box-multiple::before {
-  content: "\FF2E";
-}
-.mdi-numeric-3-box-multiple-outline::before {
-  content: "\F3AB";
-}
-.mdi-numeric-3-box-outline::before {
-  content: "\F3AC";
-}
-.mdi-numeric-3-circle::before {
-  content: "\FC80";
-}
-.mdi-numeric-3-circle-outline::before {
-  content: "\FC81";
-}
-.mdi-numeric-4::before {
-  content: "\34";
-}
-.mdi-numeric-4-box::before {
-  content: "\F3AD";
-}
-.mdi-numeric-4-box-multiple::before {
-  content: "\FF2F";
-}
-.mdi-numeric-4-box-multiple-outline::before {
-  content: "\F3AE";
-}
-.mdi-numeric-4-box-outline::before {
-  content: "\F3AF";
-}
-.mdi-numeric-4-circle::before {
-  content: "\FC82";
-}
-.mdi-numeric-4-circle-outline::before {
-  content: "\FC83";
-}
-.mdi-numeric-5::before {
-  content: "\35";
-}
-.mdi-numeric-5-box::before {
-  content: "\F3B0";
-}
-.mdi-numeric-5-box-multiple::before {
-  content: "\FF30";
-}
-.mdi-numeric-5-box-multiple-outline::before {
-  content: "\F3B1";
-}
-.mdi-numeric-5-box-outline::before {
-  content: "\F3B2";
-}
-.mdi-numeric-5-circle::before {
-  content: "\FC84";
-}
-.mdi-numeric-5-circle-outline::before {
-  content: "\FC85";
-}
-.mdi-numeric-6::before {
-  content: "\36";
-}
-.mdi-numeric-6-box::before {
-  content: "\F3B3";
-}
-.mdi-numeric-6-box-multiple::before {
-  content: "\FF31";
-}
-.mdi-numeric-6-box-multiple-outline::before {
-  content: "\F3B4";
-}
-.mdi-numeric-6-box-outline::before {
-  content: "\F3B5";
-}
-.mdi-numeric-6-circle::before {
-  content: "\FC86";
-}
-.mdi-numeric-6-circle-outline::before {
-  content: "\FC87";
-}
-.mdi-numeric-7::before {
-  content: "\37";
-}
-.mdi-numeric-7-box::before {
-  content: "\F3B6";
-}
-.mdi-numeric-7-box-multiple::before {
-  content: "\FF32";
-}
-.mdi-numeric-7-box-multiple-outline::before {
-  content: "\F3B7";
-}
-.mdi-numeric-7-box-outline::before {
-  content: "\F3B8";
-}
-.mdi-numeric-7-circle::before {
-  content: "\FC88";
-}
-.mdi-numeric-7-circle-outline::before {
-  content: "\FC89";
-}
-.mdi-numeric-8::before {
-  content: "\38";
-}
-.mdi-numeric-8-box::before {
-  content: "\F3B9";
-}
-.mdi-numeric-8-box-multiple::before {
-  content: "\FF33";
-}
-.mdi-numeric-8-box-multiple-outline::before {
-  content: "\F3BA";
-}
-.mdi-numeric-8-box-outline::before {
-  content: "\F3BB";
-}
-.mdi-numeric-8-circle::before {
-  content: "\FC8A";
-}
-.mdi-numeric-8-circle-outline::before {
-  content: "\FC8B";
-}
-.mdi-numeric-9::before {
-  content: "\39";
-}
-.mdi-numeric-9-box::before {
-  content: "\F3BC";
-}
-.mdi-numeric-9-box-multiple::before {
-  content: "\FF34";
-}
-.mdi-numeric-9-box-multiple-outline::before {
-  content: "\F3BD";
-}
-.mdi-numeric-9-box-outline::before {
-  content: "\F3BE";
-}
-.mdi-numeric-9-circle::before {
-  content: "\FC8C";
-}
-.mdi-numeric-9-circle-outline::before {
-  content: "\FC8D";
-}
-.mdi-numeric-9-plus::before {
-  content: "\F000F";
-}
-.mdi-numeric-9-plus-box::before {
-  content: "\F3BF";
-}
-.mdi-numeric-9-plus-box-multiple::before {
-  content: "\FF35";
-}
-.mdi-numeric-9-plus-box-multiple-outline::before {
-  content: "\F3C0";
-}
-.mdi-numeric-9-plus-box-outline::before {
-  content: "\F3C1";
-}
-.mdi-numeric-9-plus-circle::before {
-  content: "\FC8E";
-}
-.mdi-numeric-9-plus-circle-outline::before {
-  content: "\FC8F";
-}
-.mdi-numeric-negative-1::before {
-  content: "\F0074";
-}
-.mdi-nut::before {
-  content: "\F6F7";
-}
-.mdi-nutrition::before {
-  content: "\F3C2";
-}
-.mdi-nuxt::before {
-  content: "\F0131";
-}
-.mdi-oar::before {
-  content: "\F67B";
-}
-.mdi-ocarina::before {
-  content: "\FDBC";
-}
-.mdi-oci::before {
-  content: "\F0314";
-}
-.mdi-ocr::before {
-  content: "\F0165";
-}
-.mdi-octagon::before {
-  content: "\F3C3";
-}
-.mdi-octagon-outline::before {
-  content: "\F3C4";
-}
-.mdi-octagram::before {
-  content: "\F6F8";
-}
-.mdi-octagram-outline::before {
-  content: "\F774";
-}
-.mdi-odnoklassniki::before {
-  content: "\F3C5";
-}
-.mdi-offer::before {
-  content: "\F0246";
-}
-.mdi-office::before {
-  content: "\F3C6";
-}
-.mdi-office-building::before {
-  content: "\F990";
-}
-.mdi-oil::before {
-  content: "\F3C7";
-}
-.mdi-oil-lamp::before {
-  content: "\FF36";
-}
-.mdi-oil-level::before {
-  content: "\F0075";
-}
-.mdi-oil-temperature::before {
-  content: "\F0019";
-}
-.mdi-omega::before {
-  content: "\F3C9";
-}
-.mdi-one-up::before {
-  content: "\FB89";
-}
-.mdi-onedrive::before {
-  content: "\F3CA";
-}
-.mdi-onenote::before {
-  content: "\F746";
-}
-.mdi-onepassword::before {
-  content: "\F880";
-}
-.mdi-opacity::before {
-  content: "\F5CC";
-}
-.mdi-open-in-app::before {
-  content: "\F3CB";
-}
-.mdi-open-in-new::before {
-  content: "\F3CC";
-}
-.mdi-open-source-initiative::before {
-  content: "\FB8A";
-}
-.mdi-openid::before {
-  content: "\F3CD";
-}
-.mdi-opera::before {
-  content: "\F3CE";
-}
-.mdi-orbit::before {
-  content: "\F018";
-}
-.mdi-origin::before {
-  content: "\FB2B";
-}
-.mdi-ornament::before {
-  content: "\F3CF";
-}
-.mdi-ornament-variant::before {
-  content: "\F3D0";
-}
-.mdi-outdoor-lamp::before {
-  content: "\F0076";
-}
-.mdi-outlook::before {
-  content: "\FCFE";
-}
-.mdi-overscan::before {
-  content: "\F0027";
-}
-.mdi-owl::before {
-  content: "\F3D2";
-}
-.mdi-pac-man::before {
-  content: "\FB8B";
-}
-.mdi-package::before {
-  content: "\F3D3";
-}
-.mdi-package-down::before {
-  content: "\F3D4";
-}
-.mdi-package-up::before {
-  content: "\F3D5";
-}
-.mdi-package-variant::before {
-  content: "\F3D6";
-}
-.mdi-package-variant-closed::before {
-  content: "\F3D7";
-}
-.mdi-page-first::before {
-  content: "\F600";
-}
-.mdi-page-last::before {
-  content: "\F601";
-}
-.mdi-page-layout-body::before {
-  content: "\F6F9";
-}
-.mdi-page-layout-footer::before {
-  content: "\F6FA";
-}
-.mdi-page-layout-header::before {
-  content: "\F6FB";
-}
-.mdi-page-layout-header-footer::before {
-  content: "\FF9C";
-}
-.mdi-page-layout-sidebar-left::before {
-  content: "\F6FC";
-}
-.mdi-page-layout-sidebar-right::before {
-  content: "\F6FD";
-}
-.mdi-page-next::before {
-  content: "\FB8C";
-}
-.mdi-page-next-outline::before {
-  content: "\FB8D";
-}
-.mdi-page-previous::before {
-  content: "\FB8E";
-}
-.mdi-page-previous-outline::before {
-  content: "\FB8F";
-}
-.mdi-palette::before {
-  content: "\F3D8";
-}
-.mdi-palette-advanced::before {
-  content: "\F3D9";
-}
-.mdi-palette-outline::before {
-  content: "\FE6C";
-}
-.mdi-palette-swatch::before {
-  content: "\F8B4";
-}
-.mdi-palette-swatch-outline::before {
-  content: "\F0387";
-}
-.mdi-palm-tree::before {
-  content: "\F0077";
-}
-.mdi-pan::before {
-  content: "\FB90";
-}
-.mdi-pan-bottom-left::before {
-  content: "\FB91";
-}
-.mdi-pan-bottom-right::before {
-  content: "\FB92";
-}
-.mdi-pan-down::before {
-  content: "\FB93";
-}
-.mdi-pan-horizontal::before {
-  content: "\FB94";
-}
-.mdi-pan-left::before {
-  content: "\FB95";
-}
-.mdi-pan-right::before {
-  content: "\FB96";
-}
-.mdi-pan-top-left::before {
-  content: "\FB97";
-}
-.mdi-pan-top-right::before {
-  content: "\FB98";
-}
-.mdi-pan-up::before {
-  content: "\FB99";
-}
-.mdi-pan-vertical::before {
-  content: "\FB9A";
-}
-.mdi-panda::before {
-  content: "\F3DA";
-}
-.mdi-pandora::before {
-  content: "\F3DB";
-}
-.mdi-panorama::before {
-  content: "\F3DC";
-}
-.mdi-panorama-fisheye::before {
-  content: "\F3DD";
-}
-.mdi-panorama-horizontal::before {
-  content: "\F3DE";
-}
-.mdi-panorama-vertical::before {
-  content: "\F3DF";
-}
-.mdi-panorama-wide-angle::before {
-  content: "\F3E0";
-}
-.mdi-paper-cut-vertical::before {
-  content: "\F3E1";
-}
-.mdi-paper-roll::before {
-  content: "\F0182";
-}
-.mdi-paper-roll-outline::before {
-  content: "\F0183";
-}
-.mdi-paperclip::before {
-  content: "\F3E2";
-}
-.mdi-parachute::before {
-  content: "\FC90";
-}
-.mdi-parachute-outline::before {
-  content: "\FC91";
-}
-.mdi-parking::before {
-  content: "\F3E3";
-}
-.mdi-party-popper::before {
-  content: "\F0078";
-}
-.mdi-passport::before {
-  content: "\F7E2";
-}
-.mdi-passport-biometric::before {
-  content: "\FDBD";
-}
-.mdi-pasta::before {
-  content: "\F018B";
-}
-.mdi-patio-heater::before {
-  content: "\FF9D";
-}
-.mdi-patreon::before {
-  content: "\F881";
-}
-.mdi-pause::before {
-  content: "\F3E4";
-}
-.mdi-pause-circle::before {
-  content: "\F3E5";
-}
-.mdi-pause-circle-outline::before {
-  content: "\F3E6";
-}
-.mdi-pause-octagon::before {
-  content: "\F3E7";
-}
-.mdi-pause-octagon-outline::before {
-  content: "\F3E8";
-}
-.mdi-paw::before {
-  content: "\F3E9";
-}
-.mdi-paw-off::before {
-  content: "\F657";
-}
-.mdi-paypal::before {
-  content: "\F882";
-}
-.mdi-pdf-box::before {
-  content: "\FE39";
-}
-.mdi-peace::before {
-  content: "\F883";
-}
-.mdi-peanut::before {
-  content: "\F001E";
-}
-.mdi-peanut-off::before {
-  content: "\F001F";
-}
-.mdi-peanut-off-outline::before {
-  content: "\F0021";
-}
-.mdi-peanut-outline::before {
-  content: "\F0020";
-}
-.mdi-pen::before {
-  content: "\F3EA";
-}
-.mdi-pen-lock::before {
-  content: "\FDBE";
-}
-.mdi-pen-minus::before {
-  content: "\FDBF";
-}
-.mdi-pen-off::before {
-  content: "\FDC0";
-}
-.mdi-pen-plus::before {
-  content: "\FDC1";
-}
-.mdi-pen-remove::before {
-  content: "\FDC2";
-}
-.mdi-pencil::before {
-  content: "\F3EB";
-}
-.mdi-pencil-box::before {
-  content: "\F3EC";
-}
-.mdi-pencil-box-multiple::before {
-  content: "\F016F";
-}
-.mdi-pencil-box-multiple-outline::before {
-  content: "\F0170";
-}
-.mdi-pencil-box-outline::before {
-  content: "\F3ED";
-}
-.mdi-pencil-circle::before {
-  content: "\F6FE";
-}
-.mdi-pencil-circle-outline::before {
-  content: "\F775";
-}
-.mdi-pencil-lock::before {
-  content: "\F3EE";
-}
-.mdi-pencil-lock-outline::before {
-  content: "\FDC3";
-}
-.mdi-pencil-minus::before {
-  content: "\FDC4";
-}
-.mdi-pencil-minus-outline::before {
-  content: "\FDC5";
-}
-.mdi-pencil-off::before {
-  content: "\F3EF";
-}
-.mdi-pencil-off-outline::before {
-  content: "\FDC6";
-}
-.mdi-pencil-outline::before {
-  content: "\FC92";
-}
-.mdi-pencil-plus::before {
-  content: "\FDC7";
-}
-.mdi-pencil-plus-outline::before {
-  content: "\FDC8";
-}
-.mdi-pencil-remove::before {
-  content: "\FDC9";
-}
-.mdi-pencil-remove-outline::before {
-  content: "\FDCA";
-}
-.mdi-pencil-ruler::before {
-  content: "\F037E";
-}
-.mdi-penguin::before {
-  content: "\FEDD";
-}
-.mdi-pentagon::before {
-  content: "\F6FF";
-}
-.mdi-pentagon-outline::before {
-  content: "\F700";
-}
-.mdi-percent::before {
-  content: "\F3F0";
-}
-.mdi-percent-outline::before {
-  content: "\F02A3";
-}
-.mdi-periodic-table::before {
-  content: "\F8B5";
-}
-.mdi-periodic-table-co::before {
-  content: "\F0329";
-}
-.mdi-periodic-table-co2::before {
-  content: "\F7E3";
-}
-.mdi-periscope::before {
-  content: "\F747";
-}
-.mdi-perspective-less::before {
-  content: "\FCFF";
-}
-.mdi-perspective-more::before {
-  content: "\FD00";
-}
-.mdi-pharmacy::before {
-  content: "\F3F1";
-}
-.mdi-phone::before {
-  content: "\F3F2";
-}
-.mdi-phone-alert::before {
-  content: "\FF37";
-}
-.mdi-phone-alert-outline::before {
-  content: "\F01B9";
-}
-.mdi-phone-bluetooth::before {
-  content: "\F3F3";
-}
-.mdi-phone-bluetooth-outline::before {
-  content: "\F01BA";
-}
-.mdi-phone-cancel::before {
-  content: "\F00E7";
-}
-.mdi-phone-cancel-outline::before {
-  content: "\F01BB";
-}
-.mdi-phone-check::before {
-  content: "\F01D4";
-}
-.mdi-phone-check-outline::before {
-  content: "\F01D5";
-}
-.mdi-phone-classic::before {
-  content: "\F602";
-}
-.mdi-phone-classic-off::before {
-  content: "\F02A4";
-}
-.mdi-phone-forward::before {
-  content: "\F3F4";
-}
-.mdi-phone-forward-outline::before {
-  content: "\F01BC";
-}
-.mdi-phone-hangup::before {
-  content: "\F3F5";
-}
-.mdi-phone-hangup-outline::before {
-  content: "\F01BD";
-}
-.mdi-phone-in-talk::before {
-  content: "\F3F6";
-}
-.mdi-phone-in-talk-outline::before {
-  content: "\F01AD";
-}
-.mdi-phone-incoming::before {
-  content: "\F3F7";
-}
-.mdi-phone-incoming-outline::before {
-  content: "\F01BE";
-}
-.mdi-phone-lock::before {
-  content: "\F3F8";
-}
-.mdi-phone-lock-outline::before {
-  content: "\F01BF";
-}
-.mdi-phone-log::before {
-  content: "\F3F9";
-}
-.mdi-phone-log-outline::before {
-  content: "\F01C0";
-}
-.mdi-phone-message::before {
-  content: "\F01C1";
-}
-.mdi-phone-message-outline::before {
-  content: "\F01C2";
-}
-.mdi-phone-minus::before {
-  content: "\F658";
-}
-.mdi-phone-minus-outline::before {
-  content: "\F01C3";
-}
-.mdi-phone-missed::before {
-  content: "\F3FA";
-}
-.mdi-phone-missed-outline::before {
-  content: "\F01D0";
-}
-.mdi-phone-off::before {
-  content: "\FDCB";
-}
-.mdi-phone-off-outline::before {
-  content: "\F01D1";
-}
-.mdi-phone-outgoing::before {
-  content: "\F3FB";
-}
-.mdi-phone-outgoing-outline::before {
-  content: "\F01C4";
-}
-.mdi-phone-outline::before {
-  content: "\FDCC";
-}
-.mdi-phone-paused::before {
-  content: "\F3FC";
-}
-.mdi-phone-paused-outline::before {
-  content: "\F01C5";
-}
-.mdi-phone-plus::before {
-  content: "\F659";
-}
-.mdi-phone-plus-outline::before {
-  content: "\F01C6";
-}
-.mdi-phone-return::before {
-  content: "\F82E";
-}
-.mdi-phone-return-outline::before {
-  content: "\F01C7";
-}
-.mdi-phone-ring::before {
-  content: "\F01D6";
-}
-.mdi-phone-ring-outline::before {
-  content: "\F01D7";
-}
-.mdi-phone-rotate-landscape::before {
-  content: "\F884";
-}
-.mdi-phone-rotate-portrait::before {
-  content: "\F885";
-}
-.mdi-phone-settings::before {
-  content: "\F3FD";
-}
-.mdi-phone-settings-outline::before {
-  content: "\F01C8";
-}
-.mdi-phone-voip::before {
-  content: "\F3FE";
-}
-.mdi-pi::before {
-  content: "\F3FF";
-}
-.mdi-pi-box::before {
-  content: "\F400";
-}
-.mdi-pi-hole::before {
-  content: "\FDCD";
-}
-.mdi-piano::before {
-  content: "\F67C";
-}
-.mdi-pickaxe::before {
-  content: "\F8B6";
-}
-.mdi-picture-in-picture-bottom-right::before {
-  content: "\FE3A";
-}
-.mdi-picture-in-picture-bottom-right-outline::before {
-  content: "\FE3B";
-}
-.mdi-picture-in-picture-top-right::before {
-  content: "\FE3C";
-}
-.mdi-picture-in-picture-top-right-outline::before {
-  content: "\FE3D";
-}
-.mdi-pier::before {
-  content: "\F886";
-}
-.mdi-pier-crane::before {
-  content: "\F887";
-}
-.mdi-pig::before {
-  content: "\F401";
-}
-.mdi-pig-variant::before {
-  content: "\F0028";
-}
-.mdi-piggy-bank::before {
-  content: "\F0029";
-}
-.mdi-pill::before {
-  content: "\F402";
-}
-.mdi-pillar::before {
-  content: "\F701";
-}
-.mdi-pin::before {
-  content: "\F403";
-}
-.mdi-pin-off::before {
-  content: "\F404";
-}
-.mdi-pin-off-outline::before {
-  content: "\F92F";
-}
-.mdi-pin-outline::before {
-  content: "\F930";
-}
-.mdi-pine-tree::before {
-  content: "\F405";
-}
-.mdi-pine-tree-box::before {
-  content: "\F406";
-}
-.mdi-pinterest::before {
-  content: "\F407";
-}
-.mdi-pinterest-box::before {
-  content: "\F408";
-}
-.mdi-pinwheel::before {
-  content: "\FAD4";
-}
-.mdi-pinwheel-outline::before {
-  content: "\FAD5";
-}
-.mdi-pipe::before {
-  content: "\F7E4";
-}
-.mdi-pipe-disconnected::before {
-  content: "\F7E5";
-}
-.mdi-pipe-leak::before {
-  content: "\F888";
-}
-.mdi-pipe-wrench::before {
-  content: "\F037F";
-}
-.mdi-pirate::before {
-  content: "\FA07";
-}
-.mdi-pistol::before {
-  content: "\F702";
-}
-.mdi-piston::before {
-  content: "\F889";
-}
-.mdi-pizza::before {
-  content: "\F409";
-}
-.mdi-play::before {
-  content: "\F40A";
-}
-.mdi-play-box::before {
-  content: "\F02A5";
-}
-.mdi-play-box-outline::before {
-  content: "\F40B";
-}
-.mdi-play-circle::before {
-  content: "\F40C";
-}
-.mdi-play-circle-outline::before {
-  content: "\F40D";
-}
-.mdi-play-network::before {
-  content: "\F88A";
-}
-.mdi-play-network-outline::before {
-  content: "\FC93";
-}
-.mdi-play-outline::before {
-  content: "\FF38";
-}
-.mdi-play-pause::before {
-  content: "\F40E";
-}
-.mdi-play-protected-content::before {
-  content: "\F40F";
-}
-.mdi-play-speed::before {
-  content: "\F8FE";
-}
-.mdi-playlist-check::before {
-  content: "\F5C7";
-}
-.mdi-playlist-edit::before {
-  content: "\F8FF";
-}
-.mdi-playlist-minus::before {
-  content: "\F410";
-}
-.mdi-playlist-music::before {
-  content: "\FC94";
-}
-.mdi-playlist-music-outline::before {
-  content: "\FC95";
-}
-.mdi-playlist-play::before {
-  content: "\F411";
-}
-.mdi-playlist-plus::before {
-  content: "\F412";
-}
-.mdi-playlist-remove::before {
-  content: "\F413";
-}
-.mdi-playlist-star::before {
-  content: "\FDCE";
-}
-.mdi-playstation::before {
-  content: "\F414";
-}
-.mdi-plex::before {
-  content: "\F6B9";
-}
-.mdi-plus::before {
-  content: "\F415";
-}
-.mdi-plus-box::before {
-  content: "\F416";
-}
-.mdi-plus-box-multiple::before {
-  content: "\F334";
-}
-.mdi-plus-box-multiple-outline::before {
-  content: "\F016E";
-}
-.mdi-plus-box-outline::before {
-  content: "\F703";
-}
-.mdi-plus-circle::before {
-  content: "\F417";
-}
-.mdi-plus-circle-multiple-outline::before {
-  content: "\F418";
-}
-.mdi-plus-circle-outline::before {
-  content: "\F419";
-}
-.mdi-plus-minus::before {
-  content: "\F991";
-}
-.mdi-plus-minus-box::before {
-  content: "\F992";
-}
-.mdi-plus-network::before {
-  content: "\F41A";
-}
-.mdi-plus-network-outline::before {
-  content: "\FC96";
-}
-.mdi-plus-one::before {
-  content: "\F41B";
-}
-.mdi-plus-outline::before {
-  content: "\F704";
-}
-.mdi-plus-thick::before {
-  content: "\F0217";
-}
-.mdi-pocket::before {
-  content: "\F41C";
-}
-.mdi-podcast::before {
-  content: "\F993";
-}
-.mdi-podium::before {
-  content: "\FD01";
-}
-.mdi-podium-bronze::before {
-  content: "\FD02";
-}
-.mdi-podium-gold::before {
-  content: "\FD03";
-}
-.mdi-podium-silver::before {
-  content: "\FD04";
-}
-.mdi-point-of-sale::before {
-  content: "\FD6E";
-}
-.mdi-pokeball::before {
-  content: "\F41D";
-}
-.mdi-pokemon-go::before {
-  content: "\FA08";
-}
-.mdi-poker-chip::before {
-  content: "\F82F";
-}
-.mdi-polaroid::before {
-  content: "\F41E";
-}
-.mdi-police-badge::before {
-  content: "\F0192";
-}
-.mdi-police-badge-outline::before {
-  content: "\F0193";
-}
-.mdi-poll::before {
-  content: "\F41F";
-}
-.mdi-poll-box::before {
-  content: "\F420";
-}
-.mdi-poll-box-outline::before {
-  content: "\F02A6";
-}
-.mdi-polymer::before {
-  content: "\F421";
-}
-.mdi-pool::before {
-  content: "\F606";
-}
-.mdi-popcorn::before {
-  content: "\F422";
-}
-.mdi-post::before {
-  content: "\F002A";
-}
-.mdi-post-outline::before {
-  content: "\F002B";
-}
-.mdi-postage-stamp::before {
-  content: "\FC97";
-}
-.mdi-pot::before {
-  content: "\F65A";
-}
-.mdi-pot-mix::before {
-  content: "\F65B";
-}
-.mdi-pound::before {
-  content: "\F423";
-}
-.mdi-pound-box::before {
-  content: "\F424";
-}
-.mdi-pound-box-outline::before {
-  content: "\F01AA";
-}
-.mdi-power::before {
-  content: "\F425";
-}
-.mdi-power-cycle::before {
-  content: "\F900";
-}
-.mdi-power-off::before {
-  content: "\F901";
-}
-.mdi-power-on::before {
-  content: "\F902";
-}
-.mdi-power-plug::before {
-  content: "\F6A4";
-}
-.mdi-power-plug-off::before {
-  content: "\F6A5";
-}
-.mdi-power-settings::before {
-  content: "\F426";
-}
-.mdi-power-sleep::before {
-  content: "\F903";
-}
-.mdi-power-socket::before {
-  content: "\F427";
-}
-.mdi-power-socket-au::before {
-  content: "\F904";
-}
-.mdi-power-socket-de::before {
-  content: "\F0132";
-}
-.mdi-power-socket-eu::before {
-  content: "\F7E6";
-}
-.mdi-power-socket-fr::before {
-  content: "\F0133";
-}
-.mdi-power-socket-jp::before {
-  content: "\F0134";
-}
-.mdi-power-socket-uk::before {
-  content: "\F7E7";
-}
-.mdi-power-socket-us::before {
-  content: "\F7E8";
-}
-.mdi-power-standby::before {
-  content: "\F905";
-}
-.mdi-powershell::before {
-  content: "\FA09";
-}
-.mdi-prescription::before {
-  content: "\F705";
-}
-.mdi-presentation::before {
-  content: "\F428";
-}
-.mdi-presentation-play::before {
-  content: "\F429";
-}
-.mdi-printer::before {
-  content: "\F42A";
-}
-.mdi-printer-3d::before {
-  content: "\F42B";
-}
-.mdi-printer-3d-nozzle::before {
-  content: "\FE3E";
-}
-.mdi-printer-3d-nozzle-alert::before {
-  content: "\F01EB";
-}
-.mdi-printer-3d-nozzle-alert-outline::before {
-  content: "\F01EC";
-}
-.mdi-printer-3d-nozzle-outline::before {
-  content: "\FE3F";
-}
-.mdi-printer-alert::before {
-  content: "\F42C";
-}
-.mdi-printer-check::before {
-  content: "\F0171";
-}
-.mdi-printer-off::before {
-  content: "\FE40";
-}
-.mdi-printer-pos::before {
-  content: "\F0079";
-}
-.mdi-printer-settings::before {
-  content: "\F706";
-}
-.mdi-printer-wireless::before {
-  content: "\FA0A";
-}
-.mdi-priority-high::before {
-  content: "\F603";
-}
-.mdi-priority-low::before {
-  content: "\F604";
-}
-.mdi-professional-hexagon::before {
-  content: "\F42D";
-}
-.mdi-progress-alert::before {
-  content: "\FC98";
-}
-.mdi-progress-check::before {
-  content: "\F994";
-}
-.mdi-progress-clock::before {
-  content: "\F995";
-}
-.mdi-progress-close::before {
-  content: "\F0135";
-}
-.mdi-progress-download::before {
-  content: "\F996";
-}
-.mdi-progress-upload::before {
-  content: "\F997";
-}
-.mdi-progress-wrench::before {
-  content: "\FC99";
-}
-.mdi-projector::before {
-  content: "\F42E";
-}
-.mdi-projector-screen::before {
-  content: "\F42F";
-}
-.mdi-propane-tank::before {
-  content: "\F0382";
-}
-.mdi-propane-tank-outline::before {
-  content: "\F0383";
-}
-.mdi-protocol::before {
-  content: "\FFF9";
-}
-.mdi-publish::before {
-  content: "\F6A6";
-}
-.mdi-pulse::before {
-  content: "\F430";
-}
-.mdi-pumpkin::before {
-  content: "\FB9B";
-}
-.mdi-purse::before {
-  content: "\FF39";
-}
-.mdi-purse-outline::before {
-  content: "\FF3A";
-}
-.mdi-puzzle::before {
-  content: "\F431";
-}
-.mdi-puzzle-outline::before {
-  content: "\FA65";
-}
-.mdi-qi::before {
-  content: "\F998";
-}
-.mdi-qqchat::before {
-  content: "\F605";
-}
-.mdi-qrcode::before {
-  content: "\F432";
-}
-.mdi-qrcode-edit::before {
-  content: "\F8B7";
-}
-.mdi-qrcode-minus::before {
-  content: "\F01B7";
-}
-.mdi-qrcode-plus::before {
-  content: "\F01B6";
-}
-.mdi-qrcode-remove::before {
-  content: "\F01B8";
-}
-.mdi-qrcode-scan::before {
-  content: "\F433";
-}
-.mdi-quadcopter::before {
-  content: "\F434";
-}
-.mdi-quality-high::before {
-  content: "\F435";
-}
-.mdi-quality-low::before {
-  content: "\FA0B";
-}
-.mdi-quality-medium::before {
-  content: "\FA0C";
-}
-.mdi-quicktime::before {
-  content: "\F436";
-}
-.mdi-quora::before {
-  content: "\FD05";
-}
-.mdi-rabbit::before {
-  content: "\F906";
-}
-.mdi-racing-helmet::before {
-  content: "\FD6F";
-}
-.mdi-racquetball::before {
-  content: "\FD70";
-}
-.mdi-radar::before {
-  content: "\F437";
-}
-.mdi-radiator::before {
-  content: "\F438";
-}
-.mdi-radiator-disabled::before {
-  content: "\FAD6";
-}
-.mdi-radiator-off::before {
-  content: "\FAD7";
-}
-.mdi-radio::before {
-  content: "\F439";
-}
-.mdi-radio-am::before {
-  content: "\FC9A";
-}
-.mdi-radio-fm::before {
-  content: "\FC9B";
-}
-.mdi-radio-handheld::before {
-  content: "\F43A";
-}
-.mdi-radio-off::before {
-  content: "\F0247";
-}
-.mdi-radio-tower::before {
-  content: "\F43B";
-}
-.mdi-radioactive::before {
-  content: "\F43C";
-}
-.mdi-radioactive-off::before {
-  content: "\FEDE";
-}
-.mdi-radiobox-blank::before {
-  content: "\F43D";
-}
-.mdi-radiobox-marked::before {
-  content: "\F43E";
-}
-.mdi-radius::before {
-  content: "\FC9C";
-}
-.mdi-radius-outline::before {
-  content: "\FC9D";
-}
-.mdi-railroad-light::before {
-  content: "\FF3B";
-}
-.mdi-raspberry-pi::before {
-  content: "\F43F";
-}
-.mdi-ray-end::before {
-  content: "\F440";
-}
-.mdi-ray-end-arrow::before {
-  content: "\F441";
-}
-.mdi-ray-start::before {
-  content: "\F442";
-}
-.mdi-ray-start-arrow::before {
-  content: "\F443";
-}
-.mdi-ray-start-end::before {
-  content: "\F444";
-}
-.mdi-ray-vertex::before {
-  content: "\F445";
-}
-.mdi-react::before {
-  content: "\F707";
-}
-.mdi-read::before {
-  content: "\F447";
-}
-.mdi-receipt::before {
-  content: "\F449";
-}
-.mdi-record::before {
-  content: "\F44A";
-}
-.mdi-record-circle::before {
-  content: "\FEDF";
-}
-.mdi-record-circle-outline::before {
-  content: "\FEE0";
-}
-.mdi-record-player::before {
-  content: "\F999";
-}
-.mdi-record-rec::before {
-  content: "\F44B";
-}
-.mdi-rectangle::before {
-  content: "\FE41";
-}
-.mdi-rectangle-outline::before {
-  content: "\FE42";
-}
-.mdi-recycle::before {
-  content: "\F44C";
-}
-.mdi-reddit::before {
-  content: "\F44D";
-}
-.mdi-redhat::before {
-  content: "\F0146";
-}
-.mdi-redo::before {
-  content: "\F44E";
-}
-.mdi-redo-variant::before {
-  content: "\F44F";
-}
-.mdi-reflect-horizontal::before {
-  content: "\FA0D";
-}
-.mdi-reflect-vertical::before {
-  content: "\FA0E";
-}
-.mdi-refresh::before {
-  content: "\F450";
-}
-.mdi-refresh-circle::before {
-  content: "\F03A2";
-}
-.mdi-regex::before {
-  content: "\F451";
-}
-.mdi-registered-trademark::before {
-  content: "\FA66";
-}
-.mdi-relative-scale::before {
-  content: "\F452";
-}
-.mdi-reload::before {
-  content: "\F453";
-}
-.mdi-reload-alert::before {
-  content: "\F0136";
-}
-.mdi-reminder::before {
-  content: "\F88B";
-}
-.mdi-remote::before {
-  content: "\F454";
-}
-.mdi-remote-desktop::before {
-  content: "\F8B8";
-}
-.mdi-remote-off::before {
-  content: "\FEE1";
-}
-.mdi-remote-tv::before {
-  content: "\FEE2";
-}
-.mdi-remote-tv-off::before {
-  content: "\FEE3";
-}
-.mdi-rename-box::before {
-  content: "\F455";
-}
-.mdi-reorder-horizontal::before {
-  content: "\F687";
-}
-.mdi-reorder-vertical::before {
-  content: "\F688";
-}
-.mdi-repeat::before {
-  content: "\F456";
-}
-.mdi-repeat-off::before {
-  content: "\F457";
-}
-.mdi-repeat-once::before {
-  content: "\F458";
-}
-.mdi-replay::before {
-  content: "\F459";
-}
-.mdi-reply::before {
-  content: "\F45A";
-}
-.mdi-reply-all::before {
-  content: "\F45B";
-}
-.mdi-reply-all-outline::before {
-  content: "\FF3C";
-}
-.mdi-reply-circle::before {
-  content: "\F01D9";
-}
-.mdi-reply-outline::before {
-  content: "\FF3D";
-}
-.mdi-reproduction::before {
-  content: "\F45C";
-}
-.mdi-resistor::before {
-  content: "\FB1F";
-}
-.mdi-resistor-nodes::before {
-  content: "\FB20";
-}
-.mdi-resize::before {
-  content: "\FA67";
-}
-.mdi-resize-bottom-right::before {
-  content: "\F45D";
-}
-.mdi-responsive::before {
-  content: "\F45E";
-}
-.mdi-restart::before {
-  content: "\F708";
-}
-.mdi-restart-alert::before {
-  content: "\F0137";
-}
-.mdi-restart-off::before {
-  content: "\FD71";
-}
-.mdi-restore::before {
-  content: "\F99A";
-}
-.mdi-restore-alert::before {
-  content: "\F0138";
-}
-.mdi-rewind::before {
-  content: "\F45F";
-}
-.mdi-rewind-10::before {
-  content: "\FD06";
-}
-.mdi-rewind-30::before {
-  content: "\FD72";
-}
-.mdi-rewind-5::before {
-  content: "\F0224";
-}
-.mdi-rewind-outline::before {
-  content: "\F709";
-}
-.mdi-rhombus::before {
-  content: "\F70A";
-}
-.mdi-rhombus-medium::before {
-  content: "\FA0F";
-}
-.mdi-rhombus-outline::before {
-  content: "\F70B";
-}
-.mdi-rhombus-split::before {
-  content: "\FA10";
-}
-.mdi-ribbon::before {
-  content: "\F460";
-}
-.mdi-rice::before {
-  content: "\F7E9";
-}
-.mdi-ring::before {
-  content: "\F7EA";
-}
-.mdi-rivet::before {
-  content: "\FE43";
-}
-.mdi-road::before {
-  content: "\F461";
-}
-.mdi-road-variant::before {
-  content: "\F462";
-}
-.mdi-robber::before {
-  content: "\F007A";
-}
-.mdi-robot::before {
-  content: "\F6A8";
-}
-.mdi-robot-industrial::before {
-  content: "\FB21";
-}
-.mdi-robot-mower::before {
-  content: "\F0222";
-}
-.mdi-robot-mower-outline::before {
-  content: "\F021E";
-}
-.mdi-robot-vacuum::before {
-  content: "\F70C";
-}
-.mdi-robot-vacuum-variant::before {
-  content: "\F907";
-}
-.mdi-rocket::before {
-  content: "\F463";
-}
-.mdi-rodent::before {
-  content: "\F0352";
-}
-.mdi-roller-skate::before {
-  content: "\FD07";
-}
-.mdi-rollerblade::before {
-  content: "\FD08";
-}
-.mdi-rollupjs::before {
-  content: "\FB9C";
-}
-.mdi-roman-numeral-1::before {
-  content: "\F00B3";
-}
-.mdi-roman-numeral-10::before {
-  content: "\F00BC";
-}
-.mdi-roman-numeral-2::before {
-  content: "\F00B4";
-}
-.mdi-roman-numeral-3::before {
-  content: "\F00B5";
-}
-.mdi-roman-numeral-4::before {
-  content: "\F00B6";
-}
-.mdi-roman-numeral-5::before {
-  content: "\F00B7";
-}
-.mdi-roman-numeral-6::before {
-  content: "\F00B8";
-}
-.mdi-roman-numeral-7::before {
-  content: "\F00B9";
-}
-.mdi-roman-numeral-8::before {
-  content: "\F00BA";
-}
-.mdi-roman-numeral-9::before {
-  content: "\F00BB";
-}
-.mdi-room-service::before {
-  content: "\F88C";
-}
-.mdi-room-service-outline::before {
-  content: "\FD73";
-}
-.mdi-rotate-3d::before {
-  content: "\FEE4";
-}
-.mdi-rotate-3d-variant::before {
-  content: "\F464";
-}
-.mdi-rotate-left::before {
-  content: "\F465";
-}
-.mdi-rotate-left-variant::before {
-  content: "\F466";
-}
-.mdi-rotate-orbit::before {
-  content: "\FD74";
-}
-.mdi-rotate-right::before {
-  content: "\F467";
-}
-.mdi-rotate-right-variant::before {
-  content: "\F468";
-}
-.mdi-rounded-corner::before {
-  content: "\F607";
-}
-.mdi-router::before {
-  content: "\F020D";
-}
-.mdi-router-wireless::before {
-  content: "\F469";
-}
-.mdi-router-wireless-settings::before {
-  content: "\FA68";
-}
-.mdi-routes::before {
-  content: "\F46A";
-}
-.mdi-routes-clock::before {
-  content: "\F007B";
-}
-.mdi-rowing::before {
-  content: "\F608";
-}
-.mdi-rss::before {
-  content: "\F46B";
-}
-.mdi-rss-box::before {
-  content: "\F46C";
-}
-.mdi-rss-off::before {
-  content: "\FF3E";
-}
-.mdi-ruby::before {
-  content: "\FD09";
-}
-.mdi-rugby::before {
-  content: "\FD75";
-}
-.mdi-ruler::before {
-  content: "\F46D";
-}
-.mdi-ruler-square::before {
-  content: "\FC9E";
-}
-.mdi-ruler-square-compass::before {
-  content: "\FEDB";
-}
-.mdi-run::before {
-  content: "\F70D";
-}
-.mdi-run-fast::before {
-  content: "\F46E";
-}
-.mdi-rv-truck::before {
-  content: "\F01FF";
-}
-.mdi-sack::before {
-  content: "\FD0A";
-}
-.mdi-sack-percent::before {
-  content: "\FD0B";
-}
-.mdi-safe::before {
-  content: "\FA69";
-}
-.mdi-safe-square::before {
-  content: "\F02A7";
-}
-.mdi-safe-square-outline::before {
-  content: "\F02A8";
-}
-.mdi-safety-goggles::before {
-  content: "\FD0C";
-}
-.mdi-sailing::before {
-  content: "\FEE5";
-}
-.mdi-sale::before {
-  content: "\F46F";
-}
-.mdi-salesforce::before {
-  content: "\F88D";
-}
-.mdi-sass::before {
-  content: "\F7EB";
-}
-.mdi-satellite::before {
-  content: "\F470";
-}
-.mdi-satellite-uplink::before {
-  content: "\F908";
-}
-.mdi-satellite-variant::before {
-  content: "\F471";
-}
-.mdi-sausage::before {
-  content: "\F8B9";
-}
-.mdi-saw-blade::before {
-  content: "\FE44";
-}
-.mdi-saxophone::before {
-  content: "\F609";
-}
-.mdi-scale::before {
-  content: "\F472";
-}
-.mdi-scale-balance::before {
-  content: "\F5D1";
-}
-.mdi-scale-bathroom::before {
-  content: "\F473";
-}
-.mdi-scale-off::before {
-  content: "\F007C";
-}
-.mdi-scanner::before {
-  content: "\F6AA";
-}
-.mdi-scanner-off::before {
-  content: "\F909";
-}
-.mdi-scatter-plot::before {
-  content: "\FEE6";
-}
-.mdi-scatter-plot-outline::before {
-  content: "\FEE7";
-}
-.mdi-school::before {
-  content: "\F474";
-}
-.mdi-school-outline::before {
-  content: "\F01AB";
-}
-.mdi-scissors-cutting::before {
-  content: "\FA6A";
-}
-.mdi-scooter::before {
-  content: "\F0214";
-}
-.mdi-scoreboard::before {
-  content: "\F02A9";
-}
-.mdi-scoreboard-outline::before {
-  content: "\F02AA";
-}
-.mdi-screen-rotation::before {
-  content: "\F475";
-}
-.mdi-screen-rotation-lock::before {
-  content: "\F476";
-}
-.mdi-screw-flat-top::before {
-  content: "\FDCF";
-}
-.mdi-screw-lag::before {
-  content: "\FE54";
-}
-.mdi-screw-machine-flat-top::before {
-  content: "\FE55";
-}
-.mdi-screw-machine-round-top::before {
-  content: "\FE56";
-}
-.mdi-screw-round-top::before {
-  content: "\FE57";
-}
-.mdi-screwdriver::before {
-  content: "\F477";
-}
-.mdi-script::before {
-  content: "\FB9D";
-}
-.mdi-script-outline::before {
-  content: "\F478";
-}
-.mdi-script-text::before {
-  content: "\FB9E";
-}
-.mdi-script-text-outline::before {
-  content: "\FB9F";
-}
-.mdi-sd::before {
-  content: "\F479";
-}
-.mdi-seal::before {
-  content: "\F47A";
-}
-.mdi-seal-variant::before {
-  content: "\FFFA";
-}
-.mdi-search-web::before {
-  content: "\F70E";
-}
-.mdi-seat::before {
-  content: "\FC9F";
-}
-.mdi-seat-flat::before {
-  content: "\F47B";
-}
-.mdi-seat-flat-angled::before {
-  content: "\F47C";
-}
-.mdi-seat-individual-suite::before {
-  content: "\F47D";
-}
-.mdi-seat-legroom-extra::before {
-  content: "\F47E";
-}
-.mdi-seat-legroom-normal::before {
-  content: "\F47F";
-}
-.mdi-seat-legroom-reduced::before {
-  content: "\F480";
-}
-.mdi-seat-outline::before {
-  content: "\FCA0";
-}
-.mdi-seat-passenger::before {
-  content: "\F0274";
-}
-.mdi-seat-recline-extra::before {
-  content: "\F481";
-}
-.mdi-seat-recline-normal::before {
-  content: "\F482";
-}
-.mdi-seatbelt::before {
-  content: "\FCA1";
-}
-.mdi-security::before {
-  content: "\F483";
-}
-.mdi-security-network::before {
-  content: "\F484";
-}
-.mdi-seed::before {
-  content: "\FE45";
-}
-.mdi-seed-outline::before {
-  content: "\FE46";
-}
-.mdi-segment::before {
-  content: "\FEE8";
-}
-.mdi-select::before {
-  content: "\F485";
-}
-.mdi-select-all::before {
-  content: "\F486";
-}
-.mdi-select-color::before {
-  content: "\FD0D";
-}
-.mdi-select-compare::before {
-  content: "\FAD8";
-}
-.mdi-select-drag::before {
-  content: "\FA6B";
-}
-.mdi-select-group::before {
-  content: "\FF9F";
-}
-.mdi-select-inverse::before {
-  content: "\F487";
-}
-.mdi-select-marker::before {
-  content: "\F02AB";
-}
-.mdi-select-multiple::before {
-  content: "\F02AC";
-}
-.mdi-select-multiple-marker::before {
-  content: "\F02AD";
-}
-.mdi-select-off::before {
-  content: "\F488";
-}
-.mdi-select-place::before {
-  content: "\FFFB";
-}
-.mdi-select-search::before {
-  content: "\F022F";
-}
-.mdi-selection::before {
-  content: "\F489";
-}
-.mdi-selection-drag::before {
-  content: "\FA6C";
-}
-.mdi-selection-ellipse::before {
-  content: "\FD0E";
-}
-.mdi-selection-ellipse-arrow-inside::before {
-  content: "\FF3F";
-}
-.mdi-selection-marker::before {
-  content: "\F02AE";
-}
-.mdi-selection-multiple-marker::before {
-  content: "\F02AF";
-}
-.mdi-selection-mutliple::before {
-  content: "\F02B0";
-}
-.mdi-selection-off::before {
-  content: "\F776";
-}
-.mdi-selection-search::before {
-  content: "\F0230";
-}
-.mdi-semantic-web::before {
-  content: "\F0341";
-}
-.mdi-send::before {
-  content: "\F48A";
-}
-.mdi-send-check::before {
-  content: "\F018C";
-}
-.mdi-send-check-outline::before {
-  content: "\F018D";
-}
-.mdi-send-circle::before {
-  content: "\FE58";
-}
-.mdi-send-circle-outline::before {
-  content: "\FE59";
-}
-.mdi-send-clock::before {
-  content: "\F018E";
-}
-.mdi-send-clock-outline::before {
-  content: "\F018F";
-}
-.mdi-send-lock::before {
-  content: "\F7EC";
-}
-.mdi-send-lock-outline::before {
-  content: "\F0191";
-}
-.mdi-send-outline::before {
-  content: "\F0190";
-}
-.mdi-serial-port::before {
-  content: "\F65C";
-}
-.mdi-server::before {
-  content: "\F48B";
-}
-.mdi-server-minus::before {
-  content: "\F48C";
-}
-.mdi-server-network::before {
-  content: "\F48D";
-}
-.mdi-server-network-off::before {
-  content: "\F48E";
-}
-.mdi-server-off::before {
-  content: "\F48F";
-}
-.mdi-server-plus::before {
-  content: "\F490";
-}
-.mdi-server-remove::before {
-  content: "\F491";
-}
-.mdi-server-security::before {
-  content: "\F492";
-}
-.mdi-set-all::before {
-  content: "\F777";
-}
-.mdi-set-center::before {
-  content: "\F778";
-}
-.mdi-set-center-right::before {
-  content: "\F779";
-}
-.mdi-set-left::before {
-  content: "\F77A";
-}
-.mdi-set-left-center::before {
-  content: "\F77B";
-}
-.mdi-set-left-right::before {
-  content: "\F77C";
-}
-.mdi-set-none::before {
-  content: "\F77D";
-}
-.mdi-set-right::before {
-  content: "\F77E";
-}
-.mdi-set-top-box::before {
-  content: "\F99E";
-}
-.mdi-settings::before {
-  content: "\F493";
-}
-.mdi-settings-box::before {
-  content: "\F494";
-}
-.mdi-settings-helper::before {
-  content: "\FA6D";
-}
-.mdi-settings-outline::before {
-  content: "\F8BA";
-}
-.mdi-settings-transfer::before {
-  content: "\F007D";
-}
-.mdi-settings-transfer-outline::before {
-  content: "\F007E";
-}
-.mdi-shaker::before {
-  content: "\F0139";
-}
-.mdi-shaker-outline::before {
-  content: "\F013A";
-}
-.mdi-shape::before {
-  content: "\F830";
-}
-.mdi-shape-circle-plus::before {
-  content: "\F65D";
-}
-.mdi-shape-outline::before {
-  content: "\F831";
-}
-.mdi-shape-oval-plus::before {
-  content: "\F0225";
-}
-.mdi-shape-plus::before {
-  content: "\F495";
-}
-.mdi-shape-polygon-plus::before {
-  content: "\F65E";
-}
-.mdi-shape-rectangle-plus::before {
-  content: "\F65F";
-}
-.mdi-shape-square-plus::before {
-  content: "\F660";
-}
-.mdi-share::before {
-  content: "\F496";
-}
-.mdi-share-all::before {
-  content: "\F021F";
-}
-.mdi-share-all-outline::before {
-  content: "\F0220";
-}
-.mdi-share-circle::before {
-  content: "\F01D8";
-}
-.mdi-share-off::before {
-  content: "\FF40";
-}
-.mdi-share-off-outline::before {
-  content: "\FF41";
-}
-.mdi-share-outline::before {
-  content: "\F931";
-}
-.mdi-share-variant::before {
-  content: "\F497";
-}
-.mdi-sheep::before {
-  content: "\FCA2";
-}
-.mdi-shield::before {
-  content: "\F498";
-}
-.mdi-shield-account::before {
-  content: "\F88E";
-}
-.mdi-shield-account-outline::before {
-  content: "\FA11";
-}
-.mdi-shield-airplane::before {
-  content: "\F6BA";
-}
-.mdi-shield-airplane-outline::before {
-  content: "\FCA3";
-}
-.mdi-shield-alert::before {
-  content: "\FEE9";
-}
-.mdi-shield-alert-outline::before {
-  content: "\FEEA";
-}
-.mdi-shield-car::before {
-  content: "\FFA0";
-}
-.mdi-shield-check::before {
-  content: "\F565";
-}
-.mdi-shield-check-outline::before {
-  content: "\FCA4";
-}
-.mdi-shield-cross::before {
-  content: "\FCA5";
-}
-.mdi-shield-cross-outline::before {
-  content: "\FCA6";
-}
-.mdi-shield-edit::before {
-  content: "\F01CB";
-}
-.mdi-shield-edit-outline::before {
-  content: "\F01CC";
-}
-.mdi-shield-half::before {
-  content: "\F038B";
-}
-.mdi-shield-half-full::before {
-  content: "\F77F";
-}
-.mdi-shield-home::before {
-  content: "\F689";
-}
-.mdi-shield-home-outline::before {
-  content: "\FCA7";
-}
-.mdi-shield-key::before {
-  content: "\FBA0";
-}
-.mdi-shield-key-outline::before {
-  content: "\FBA1";
-}
-.mdi-shield-link-variant::before {
-  content: "\FD0F";
-}
-.mdi-shield-link-variant-outline::before {
-  content: "\FD10";
-}
-.mdi-shield-lock::before {
-  content: "\F99C";
-}
-.mdi-shield-lock-outline::before {
-  content: "\FCA8";
-}
-.mdi-shield-off::before {
-  content: "\F99D";
-}
-.mdi-shield-off-outline::before {
-  content: "\F99B";
-}
-.mdi-shield-outline::before {
-  content: "\F499";
-}
-.mdi-shield-plus::before {
-  content: "\FAD9";
-}
-.mdi-shield-plus-outline::before {
-  content: "\FADA";
-}
-.mdi-shield-refresh::before {
-  content: "\F01CD";
-}
-.mdi-shield-refresh-outline::before {
-  content: "\F01CE";
-}
-.mdi-shield-remove::before {
-  content: "\FADB";
-}
-.mdi-shield-remove-outline::before {
-  content: "\FADC";
-}
-.mdi-shield-search::before {
-  content: "\FD76";
-}
-.mdi-shield-star::before {
-  content: "\F0166";
-}
-.mdi-shield-star-outline::before {
-  content: "\F0167";
-}
-.mdi-shield-sun::before {
-  content: "\F007F";
-}
-.mdi-shield-sun-outline::before {
-  content: "\F0080";
-}
-.mdi-ship-wheel::before {
-  content: "\F832";
-}
-.mdi-shoe-formal::before {
-  content: "\FB22";
-}
-.mdi-shoe-heel::before {
-  content: "\FB23";
-}
-.mdi-shoe-print::before {
-  content: "\FE5A";
-}
-.mdi-shopify::before {
-  content: "\FADD";
-}
-.mdi-shopping::before {
-  content: "\F49A";
-}
-.mdi-shopping-music::before {
-  content: "\F49B";
-}
-.mdi-shopping-outline::before {
-  content: "\F0200";
-}
-.mdi-shopping-search::before {
-  content: "\FFA1";
-}
-.mdi-shovel::before {
-  content: "\F70F";
-}
-.mdi-shovel-off::before {
-  content: "\F710";
-}
-.mdi-shower::before {
-  content: "\F99F";
-}
-.mdi-shower-head::before {
-  content: "\F9A0";
-}
-.mdi-shredder::before {
-  content: "\F49C";
-}
-.mdi-shuffle::before {
-  content: "\F49D";
-}
-.mdi-shuffle-disabled::before {
-  content: "\F49E";
-}
-.mdi-shuffle-variant::before {
-  content: "\F49F";
-}
-.mdi-shuriken::before {
-  content: "\F03AA";
-}
-.mdi-sigma::before {
-  content: "\F4A0";
-}
-.mdi-sigma-lower::before {
-  content: "\F62B";
-}
-.mdi-sign-caution::before {
-  content: "\F4A1";
-}
-.mdi-sign-direction::before {
-  content: "\F780";
-}
-.mdi-sign-direction-minus::before {
-  content: "\F0022";
-}
-.mdi-sign-direction-plus::before {
-  content: "\FFFD";
-}
-.mdi-sign-direction-remove::before {
-  content: "\FFFE";
-}
-.mdi-sign-real-estate::before {
-  content: "\F0143";
-}
-.mdi-sign-text::before {
-  content: "\F781";
-}
-.mdi-signal::before {
-  content: "\F4A2";
-}
-.mdi-signal-2g::before {
-  content: "\F711";
-}
-.mdi-signal-3g::before {
-  content: "\F712";
-}
-.mdi-signal-4g::before {
-  content: "\F713";
-}
-.mdi-signal-5g::before {
-  content: "\FA6E";
-}
-.mdi-signal-cellular-1::before {
-  content: "\F8BB";
-}
-.mdi-signal-cellular-2::before {
-  content: "\F8BC";
-}
-.mdi-signal-cellular-3::before {
-  content: "\F8BD";
-}
-.mdi-signal-cellular-outline::before {
-  content: "\F8BE";
-}
-.mdi-signal-distance-variant::before {
-  content: "\FE47";
-}
-.mdi-signal-hspa::before {
-  content: "\F714";
-}
-.mdi-signal-hspa-plus::before {
-  content: "\F715";
-}
-.mdi-signal-off::before {
-  content: "\F782";
-}
-.mdi-signal-variant::before {
-  content: "\F60A";
-}
-.mdi-signature::before {
-  content: "\FE5B";
-}
-.mdi-signature-freehand::before {
-  content: "\FE5C";
-}
-.mdi-signature-image::before {
-  content: "\FE5D";
-}
-.mdi-signature-text::before {
-  content: "\FE5E";
-}
-.mdi-silo::before {
-  content: "\FB24";
-}
-.mdi-silverware::before {
-  content: "\F4A3";
-}
-.mdi-silverware-clean::before {
-  content: "\FFFF";
-}
-.mdi-silverware-fork::before {
-  content: "\F4A4";
-}
-.mdi-silverware-fork-knife::before {
-  content: "\FA6F";
-}
-.mdi-silverware-spoon::before {
-  content: "\F4A5";
-}
-.mdi-silverware-variant::before {
-  content: "\F4A6";
-}
-.mdi-sim::before {
-  content: "\F4A7";
-}
-.mdi-sim-alert::before {
-  content: "\F4A8";
-}
-.mdi-sim-off::before {
-  content: "\F4A9";
-}
-.mdi-simple-icons::before {
-  content: "\F0348";
-}
-.mdi-sina-weibo::before {
-  content: "\FADE";
-}
-.mdi-sitemap::before {
-  content: "\F4AA";
-}
-.mdi-skate::before {
-  content: "\FD11";
-}
-.mdi-skew-less::before {
-  content: "\FD12";
-}
-.mdi-skew-more::before {
-  content: "\FD13";
-}
-.mdi-ski::before {
-  content: "\F032F";
-}
-.mdi-ski-cross-country::before {
-  content: "\F0330";
-}
-.mdi-ski-water::before {
-  content: "\F0331";
-}
-.mdi-skip-backward::before {
-  content: "\F4AB";
-}
-.mdi-skip-backward-outline::before {
-  content: "\FF42";
-}
-.mdi-skip-forward::before {
-  content: "\F4AC";
-}
-.mdi-skip-forward-outline::before {
-  content: "\FF43";
-}
-.mdi-skip-next::before {
-  content: "\F4AD";
-}
-.mdi-skip-next-circle::before {
-  content: "\F661";
-}
-.mdi-skip-next-circle-outline::before {
-  content: "\F662";
-}
-.mdi-skip-next-outline::before {
-  content: "\FF44";
-}
-.mdi-skip-previous::before {
-  content: "\F4AE";
-}
-.mdi-skip-previous-circle::before {
-  content: "\F663";
-}
-.mdi-skip-previous-circle-outline::before {
-  content: "\F664";
-}
-.mdi-skip-previous-outline::before {
-  content: "\FF45";
-}
-.mdi-skull::before {
-  content: "\F68B";
-}
-.mdi-skull-crossbones::before {
-  content: "\FBA2";
-}
-.mdi-skull-crossbones-outline::before {
-  content: "\FBA3";
-}
-.mdi-skull-outline::before {
-  content: "\FBA4";
-}
-.mdi-skype::before {
-  content: "\F4AF";
-}
-.mdi-skype-business::before {
-  content: "\F4B0";
-}
-.mdi-slack::before {
-  content: "\F4B1";
-}
-.mdi-slackware::before {
-  content: "\F90A";
-}
-.mdi-slash-forward::before {
-  content: "\F0000";
-}
-.mdi-slash-forward-box::before {
-  content: "\F0001";
-}
-.mdi-sleep::before {
-  content: "\F4B2";
-}
-.mdi-sleep-off::before {
-  content: "\F4B3";
-}
-.mdi-slope-downhill::before {
-  content: "\FE5F";
-}
-.mdi-slope-uphill::before {
-  content: "\FE60";
-}
-.mdi-slot-machine::before {
-  content: "\F013F";
-}
-.mdi-slot-machine-outline::before {
-  content: "\F0140";
-}
-.mdi-smart-card::before {
-  content: "\F00E8";
-}
-.mdi-smart-card-outline::before {
-  content: "\F00E9";
-}
-.mdi-smart-card-reader::before {
-  content: "\F00EA";
-}
-.mdi-smart-card-reader-outline::before {
-  content: "\F00EB";
-}
-.mdi-smog::before {
-  content: "\FA70";
-}
-.mdi-smoke-detector::before {
-  content: "\F392";
-}
-.mdi-smoking::before {
-  content: "\F4B4";
-}
-.mdi-smoking-off::before {
-  content: "\F4B5";
-}
-.mdi-snapchat::before {
-  content: "\F4B6";
-}
-.mdi-snowboard::before {
-  content: "\F0332";
-}
-.mdi-snowflake::before {
-  content: "\F716";
-}
-.mdi-snowflake-alert::before {
-  content: "\FF46";
-}
-.mdi-snowflake-melt::before {
-  content: "\F02F6";
-}
-.mdi-snowflake-variant::before {
-  content: "\FF47";
-}
-.mdi-snowman::before {
-  content: "\F4B7";
-}
-.mdi-soccer::before {
-  content: "\F4B8";
-}
-.mdi-soccer-field::before {
-  content: "\F833";
-}
-.mdi-sofa::before {
-  content: "\F4B9";
-}
-.mdi-solar-panel::before {
-  content: "\FD77";
-}
-.mdi-solar-panel-large::before {
-  content: "\FD78";
-}
-.mdi-solar-power::before {
-  content: "\FA71";
-}
-.mdi-soldering-iron::before {
-  content: "\F00BD";
-}
-.mdi-solid::before {
-  content: "\F68C";
-}
-.mdi-sort::before {
-  content: "\F4BA";
-}
-.mdi-sort-alphabetical::before {
-  content: "\F4BB";
-}
-.mdi-sort-alphabetical-ascending::before {
-  content: "\F0173";
-}
-.mdi-sort-alphabetical-descending::before {
-  content: "\F0174";
-}
-.mdi-sort-ascending::before {
-  content: "\F4BC";
-}
-.mdi-sort-descending::before {
-  content: "\F4BD";
-}
-.mdi-sort-numeric::before {
-  content: "\F4BE";
-}
-.mdi-sort-variant::before {
-  content: "\F4BF";
-}
-.mdi-sort-variant-lock::before {
-  content: "\FCA9";
-}
-.mdi-sort-variant-lock-open::before {
-  content: "\FCAA";
-}
-.mdi-sort-variant-remove::before {
-  content: "\F0172";
-}
-.mdi-soundcloud::before {
-  content: "\F4C0";
-}
-.mdi-source-branch::before {
-  content: "\F62C";
-}
-.mdi-source-commit::before {
-  content: "\F717";
-}
-.mdi-source-commit-end::before {
-  content: "\F718";
-}
-.mdi-source-commit-end-local::before {
-  content: "\F719";
-}
-.mdi-source-commit-local::before {
-  content: "\F71A";
-}
-.mdi-source-commit-next-local::before {
-  content: "\F71B";
-}
-.mdi-source-commit-start::before {
-  content: "\F71C";
-}
-.mdi-source-commit-start-next-local::before {
-  content: "\F71D";
-}
-.mdi-source-fork::before {
-  content: "\F4C1";
-}
-.mdi-source-merge::before {
-  content: "\F62D";
-}
-.mdi-source-pull::before {
-  content: "\F4C2";
-}
-.mdi-source-repository::before {
-  content: "\FCAB";
-}
-.mdi-source-repository-multiple::before {
-  content: "\FCAC";
-}
-.mdi-soy-sauce::before {
-  content: "\F7ED";
-}
-.mdi-spa::before {
-  content: "\FCAD";
-}
-.mdi-spa-outline::before {
-  content: "\FCAE";
-}
-.mdi-space-invaders::before {
-  content: "\FBA5";
-}
-.mdi-space-station::before {
-  content: "\F03AE";
-}
-.mdi-spade::before {
-  content: "\FE48";
-}
-.mdi-speaker::before {
-  content: "\F4C3";
-}
-.mdi-speaker-bluetooth::before {
-  content: "\F9A1";
-}
-.mdi-speaker-multiple::before {
-  content: "\FD14";
-}
-.mdi-speaker-off::before {
-  content: "\F4C4";
-}
-.mdi-speaker-wireless::before {
-  content: "\F71E";
-}
-.mdi-speedometer::before {
-  content: "\F4C5";
-}
-.mdi-speedometer-medium::before {
-  content: "\FFA2";
-}
-.mdi-speedometer-slow::before {
-  content: "\FFA3";
-}
-.mdi-spellcheck::before {
-  content: "\F4C6";
-}
-.mdi-spider::before {
-  content: "\F0215";
-}
-.mdi-spider-thread::before {
-  content: "\F0216";
-}
-.mdi-spider-web::before {
-  content: "\FBA6";
-}
-.mdi-spotify::before {
-  content: "\F4C7";
-}
-.mdi-spotlight::before {
-  content: "\F4C8";
-}
-.mdi-spotlight-beam::before {
-  content: "\F4C9";
-}
-.mdi-spray::before {
-  content: "\F665";
-}
-.mdi-spray-bottle::before {
-  content: "\FADF";
-}
-.mdi-sprinkler::before {
-  content: "\F0081";
-}
-.mdi-sprinkler-variant::before {
-  content: "\F0082";
-}
-.mdi-sprout::before {
-  content: "\FE49";
-}
-.mdi-sprout-outline::before {
-  content: "\FE4A";
-}
-.mdi-square::before {
-  content: "\F763";
-}
-.mdi-square-edit-outline::before {
-  content: "\F90B";
-}
-.mdi-square-inc::before {
-  content: "\F4CA";
-}
-.mdi-square-inc-cash::before {
-  content: "\F4CB";
-}
-.mdi-square-medium::before {
-  content: "\FA12";
-}
-.mdi-square-medium-outline::before {
-  content: "\FA13";
-}
-.mdi-square-off::before {
-  content: "\F0319";
-}
-.mdi-square-off-outline::before {
-  content: "\F031A";
-}
-.mdi-square-outline::before {
-  content: "\F762";
-}
-.mdi-square-root::before {
-  content: "\F783";
-}
-.mdi-square-root-box::before {
-  content: "\F9A2";
-}
-.mdi-square-small::before {
-  content: "\FA14";
-}
-.mdi-squeegee::before {
-  content: "\FAE0";
-}
-.mdi-ssh::before {
-  content: "\F8BF";
-}
-.mdi-stack-exchange::before {
-  content: "\F60B";
-}
-.mdi-stack-overflow::before {
-  content: "\F4CC";
-}
-.mdi-stackpath::before {
-  content: "\F359";
-}
-.mdi-stadium::before {
-  content: "\F001A";
-}
-.mdi-stadium-variant::before {
-  content: "\F71F";
-}
-.mdi-stairs::before {
-  content: "\F4CD";
-}
-.mdi-stairs-down::before {
-  content: "\F02E9";
-}
-.mdi-stairs-up::before {
-  content: "\F02E8";
-}
-.mdi-stamper::before {
-  content: "\FD15";
-}
-.mdi-standard-definition::before {
-  content: "\F7EE";
-}
-.mdi-star::before {
-  content: "\F4CE";
-}
-.mdi-star-box::before {
-  content: "\FA72";
-}
-.mdi-star-box-multiple::before {
-  content: "\F02B1";
-}
-.mdi-star-box-multiple-outline::before {
-  content: "\F02B2";
-}
-.mdi-star-box-outline::before {
-  content: "\FA73";
-}
-.mdi-star-circle::before {
-  content: "\F4CF";
-}
-.mdi-star-circle-outline::before {
-  content: "\F9A3";
-}
-.mdi-star-face::before {
-  content: "\F9A4";
-}
-.mdi-star-four-points::before {
-  content: "\FAE1";
-}
-.mdi-star-four-points-outline::before {
-  content: "\FAE2";
-}
-.mdi-star-half::before {
-  content: "\F4D0";
-}
-.mdi-star-off::before {
-  content: "\F4D1";
-}
-.mdi-star-outline::before {
-  content: "\F4D2";
-}
-.mdi-star-three-points::before {
-  content: "\FAE3";
-}
-.mdi-star-three-points-outline::before {
-  content: "\FAE4";
-}
-.mdi-state-machine::before {
-  content: "\F021A";
-}
-.mdi-steam::before {
-  content: "\F4D3";
-}
-.mdi-steam-box::before {
-  content: "\F90C";
-}
-.mdi-steering::before {
-  content: "\F4D4";
-}
-.mdi-steering-off::before {
-  content: "\F90D";
-}
-.mdi-step-backward::before {
-  content: "\F4D5";
-}
-.mdi-step-backward-2::before {
-  content: "\F4D6";
-}
-.mdi-step-forward::before {
-  content: "\F4D7";
-}
-.mdi-step-forward-2::before {
-  content: "\F4D8";
-}
-.mdi-stethoscope::before {
-  content: "\F4D9";
-}
-.mdi-sticker::before {
-  content: "\F038F";
-}
-.mdi-sticker-alert::before {
-  content: "\F0390";
-}
-.mdi-sticker-alert-outline::before {
-  content: "\F0391";
-}
-.mdi-sticker-check::before {
-  content: "\F0392";
-}
-.mdi-sticker-check-outline::before {
-  content: "\F0393";
-}
-.mdi-sticker-circle-outline::before {
-  content: "\F5D0";
-}
-.mdi-sticker-emoji::before {
-  content: "\F784";
-}
-.mdi-sticker-minus::before {
-  content: "\F0394";
-}
-.mdi-sticker-minus-outline::before {
-  content: "\F0395";
-}
-.mdi-sticker-outline::before {
-  content: "\F0396";
-}
-.mdi-sticker-plus::before {
-  content: "\F0397";
-}
-.mdi-sticker-plus-outline::before {
-  content: "\F0398";
-}
-.mdi-sticker-remove::before {
-  content: "\F0399";
-}
-.mdi-sticker-remove-outline::before {
-  content: "\F039A";
-}
-.mdi-stocking::before {
-  content: "\F4DA";
-}
-.mdi-stomach::before {
-  content: "\F00BE";
-}
-.mdi-stop::before {
-  content: "\F4DB";
-}
-.mdi-stop-circle::before {
-  content: "\F666";
-}
-.mdi-stop-circle-outline::before {
-  content: "\F667";
-}
-.mdi-store::before {
-  content: "\F4DC";
-}
-.mdi-store-24-hour::before {
-  content: "\F4DD";
-}
-.mdi-store-outline::before {
-  content: "\F038C";
-}
-.mdi-storefront::before {
-  content: "\F00EC";
-}
-.mdi-stove::before {
-  content: "\F4DE";
-}
-.mdi-strategy::before {
-  content: "\F0201";
-}
-.mdi-strava::before {
-  content: "\FB25";
-}
-.mdi-stretch-to-page::before {
-  content: "\FF48";
-}
-.mdi-stretch-to-page-outline::before {
-  content: "\FF49";
-}
-.mdi-string-lights::before {
-  content: "\F02E5";
-}
-.mdi-string-lights-off::before {
-  content: "\F02E6";
-}
-.mdi-subdirectory-arrow-left::before {
-  content: "\F60C";
-}
-.mdi-subdirectory-arrow-right::before {
-  content: "\F60D";
-}
-.mdi-subtitles::before {
-  content: "\FA15";
-}
-.mdi-subtitles-outline::before {
-  content: "\FA16";
-}
-.mdi-subway::before {
-  content: "\F6AB";
-}
-.mdi-subway-alert-variant::before {
-  content: "\FD79";
-}
-.mdi-subway-variant::before {
-  content: "\F4DF";
-}
-.mdi-summit::before {
-  content: "\F785";
-}
-.mdi-sunglasses::before {
-  content: "\F4E0";
-}
-.mdi-surround-sound::before {
-  content: "\F5C5";
-}
-.mdi-surround-sound-2-0::before {
-  content: "\F7EF";
-}
-.mdi-surround-sound-3-1::before {
-  content: "\F7F0";
-}
-.mdi-surround-sound-5-1::before {
-  content: "\F7F1";
-}
-.mdi-surround-sound-7-1::before {
-  content: "\F7F2";
-}
-.mdi-svg::before {
-  content: "\F720";
-}
-.mdi-swap-horizontal::before {
-  content: "\F4E1";
-}
-.mdi-swap-horizontal-bold::before {
-  content: "\FBA9";
-}
-.mdi-swap-horizontal-circle::before {
-  content: "\F0002";
-}
-.mdi-swap-horizontal-circle-outline::before {
-  content: "\F0003";
-}
-.mdi-swap-horizontal-variant::before {
-  content: "\F8C0";
-}
-.mdi-swap-vertical::before {
-  content: "\F4E2";
-}
-.mdi-swap-vertical-bold::before {
-  content: "\FBAA";
-}
-.mdi-swap-vertical-circle::before {
-  content: "\F0004";
-}
-.mdi-swap-vertical-circle-outline::before {
-  content: "\F0005";
-}
-.mdi-swap-vertical-variant::before {
-  content: "\F8C1";
-}
-.mdi-swim::before {
-  content: "\F4E3";
-}
-.mdi-switch::before {
-  content: "\F4E4";
-}
-.mdi-sword::before {
-  content: "\F4E5";
-}
-.mdi-sword-cross::before {
-  content: "\F786";
-}
-.mdi-syllabary-hangul::before {
-  content: "\F035E";
-}
-.mdi-syllabary-hiragana::before {
-  content: "\F035F";
-}
-.mdi-syllabary-katakana::before {
-  content: "\F0360";
-}
-.mdi-syllabary-katakana-half-width::before {
-  content: "\F0361";
-}
-.mdi-symfony::before {
-  content: "\FAE5";
-}
-.mdi-sync::before {
-  content: "\F4E6";
-}
-.mdi-sync-alert::before {
-  content: "\F4E7";
-}
-.mdi-sync-circle::before {
-  content: "\F03A3";
-}
-.mdi-sync-off::before {
-  content: "\F4E8";
-}
-.mdi-tab::before {
-  content: "\F4E9";
-}
-.mdi-tab-minus::before {
-  content: "\FB26";
-}
-.mdi-tab-plus::before {
-  content: "\F75B";
-}
-.mdi-tab-remove::before {
-  content: "\FB27";
-}
-.mdi-tab-unselected::before {
-  content: "\F4EA";
-}
-.mdi-table::before {
-  content: "\F4EB";
-}
-.mdi-table-border::before {
-  content: "\FA17";
-}
-.mdi-table-chair::before {
-  content: "\F0083";
-}
-.mdi-table-column::before {
-  content: "\F834";
-}
-.mdi-table-column-plus-after::before {
-  content: "\F4EC";
-}
-.mdi-table-column-plus-before::before {
-  content: "\F4ED";
-}
-.mdi-table-column-remove::before {
-  content: "\F4EE";
-}
-.mdi-table-column-width::before {
-  content: "\F4EF";
-}
-.mdi-table-edit::before {
-  content: "\F4F0";
-}
-.mdi-table-eye::before {
-  content: "\F00BF";
-}
-.mdi-table-headers-eye::before {
-  content: "\F0248";
-}
-.mdi-table-headers-eye-off::before {
-  content: "\F0249";
-}
-.mdi-table-large::before {
-  content: "\F4F1";
-}
-.mdi-table-large-plus::before {
-  content: "\FFA4";
-}
-.mdi-table-large-remove::before {
-  content: "\FFA5";
-}
-.mdi-table-merge-cells::before {
-  content: "\F9A5";
-}
-.mdi-table-of-contents::before {
-  content: "\F835";
-}
-.mdi-table-plus::before {
-  content: "\FA74";
-}
-.mdi-table-remove::before {
-  content: "\FA75";
-}
-.mdi-table-row::before {
-  content: "\F836";
-}
-.mdi-table-row-height::before {
-  content: "\F4F2";
-}
-.mdi-table-row-plus-after::before {
-  content: "\F4F3";
-}
-.mdi-table-row-plus-before::before {
-  content: "\F4F4";
-}
-.mdi-table-row-remove::before {
-  content: "\F4F5";
-}
-.mdi-table-search::before {
-  content: "\F90E";
-}
-.mdi-table-settings::before {
-  content: "\F837";
-}
-.mdi-table-tennis::before {
-  content: "\FE4B";
-}
-.mdi-tablet::before {
-  content: "\F4F6";
-}
-.mdi-tablet-android::before {
-  content: "\F4F7";
-}
-.mdi-tablet-cellphone::before {
-  content: "\F9A6";
-}
-.mdi-tablet-dashboard::before {
-  content: "\FEEB";
-}
-.mdi-tablet-ipad::before {
-  content: "\F4F8";
-}
-.mdi-taco::before {
-  content: "\F761";
-}
-.mdi-tag::before {
-  content: "\F4F9";
-}
-.mdi-tag-faces::before {
-  content: "\F4FA";
-}
-.mdi-tag-heart::before {
-  content: "\F68A";
-}
-.mdi-tag-heart-outline::before {
-  content: "\FBAB";
-}
-.mdi-tag-minus::before {
-  content: "\F90F";
-}
-.mdi-tag-minus-outline::before {
-  content: "\F024A";
-}
-.mdi-tag-multiple::before {
-  content: "\F4FB";
-}
-.mdi-tag-multiple-outline::before {
-  content: "\F0322";
-}
-.mdi-tag-off::before {
-  content: "\F024B";
-}
-.mdi-tag-off-outline::before {
-  content: "\F024C";
-}
-.mdi-tag-outline::before {
-  content: "\F4FC";
-}
-.mdi-tag-plus::before {
-  content: "\F721";
-}
-.mdi-tag-plus-outline::before {
-  content: "\F024D";
-}
-.mdi-tag-remove::before {
-  content: "\F722";
-}
-.mdi-tag-remove-outline::before {
-  content: "\F024E";
-}
-.mdi-tag-text::before {
-  content: "\F024F";
-}
-.mdi-tag-text-outline::before {
-  content: "\F4FD";
-}
-.mdi-tank::before {
-  content: "\FD16";
-}
-.mdi-tanker-truck::before {
-  content: "\F0006";
-}
-.mdi-tape-measure::before {
-  content: "\FB28";
-}
-.mdi-target::before {
-  content: "\F4FE";
-}
-.mdi-target-account::before {
-  content: "\FBAC";
-}
-.mdi-target-variant::before {
-  content: "\FA76";
-}
-.mdi-taxi::before {
-  content: "\F4FF";
-}
-.mdi-tea::before {
-  content: "\FD7A";
-}
-.mdi-tea-outline::before {
-  content: "\FD7B";
-}
-.mdi-teach::before {
-  content: "\F88F";
-}
-.mdi-teamviewer::before {
-  content: "\F500";
-}
-.mdi-telegram::before {
-  content: "\F501";
-}
-.mdi-telescope::before {
-  content: "\FB29";
-}
-.mdi-television::before {
-  content: "\F502";
-}
-.mdi-television-ambient-light::before {
-  content: "\F0381";
-}
-.mdi-television-box::before {
-  content: "\F838";
-}
-.mdi-television-classic::before {
-  content: "\F7F3";
-}
-.mdi-television-classic-off::before {
-  content: "\F839";
-}
-.mdi-television-clean::before {
-  content: "\F013B";
-}
-.mdi-television-guide::before {
-  content: "\F503";
-}
-.mdi-television-off::before {
-  content: "\F83A";
-}
-.mdi-television-pause::before {
-  content: "\FFA6";
-}
-.mdi-television-play::before {
-  content: "\FEEC";
-}
-.mdi-television-stop::before {
-  content: "\FFA7";
-}
-.mdi-temperature-celsius::before {
-  content: "\F504";
-}
-.mdi-temperature-fahrenheit::before {
-  content: "\F505";
-}
-.mdi-temperature-kelvin::before {
-  content: "\F506";
-}
-.mdi-tennis::before {
-  content: "\FD7C";
-}
-.mdi-tennis-ball::before {
-  content: "\F507";
-}
-.mdi-tent::before {
-  content: "\F508";
-}
-.mdi-terraform::before {
-  content: "\F0084";
-}
-.mdi-terrain::before {
-  content: "\F509";
-}
-.mdi-test-tube::before {
-  content: "\F668";
-}
-.mdi-test-tube-empty::before {
-  content: "\F910";
-}
-.mdi-test-tube-off::before {
-  content: "\F911";
-}
-.mdi-text::before {
-  content: "\F9A7";
-}
-.mdi-text-recognition::before {
-  content: "\F0168";
-}
-.mdi-text-shadow::before {
-  content: "\F669";
-}
-.mdi-text-short::before {
-  content: "\F9A8";
-}
-.mdi-text-subject::before {
-  content: "\F9A9";
-}
-.mdi-text-to-speech::before {
-  content: "\F50A";
-}
-.mdi-text-to-speech-off::before {
-  content: "\F50B";
-}
-.mdi-textarea::before {
-  content: "\F00C0";
-}
-.mdi-textbox::before {
-  content: "\F60E";
-}
-.mdi-textbox-lock::before {
-  content: "\F0388";
-}
-.mdi-textbox-password::before {
-  content: "\F7F4";
-}
-.mdi-texture::before {
-  content: "\F50C";
-}
-.mdi-texture-box::before {
-  content: "\F0007";
-}
-.mdi-theater::before {
-  content: "\F50D";
-}
-.mdi-theme-light-dark::before {
-  content: "\F50E";
-}
-.mdi-thermometer::before {
-  content: "\F50F";
-}
-.mdi-thermometer-alert::before {
-  content: "\FE61";
-}
-.mdi-thermometer-chevron-down::before {
-  content: "\FE62";
-}
-.mdi-thermometer-chevron-up::before {
-  content: "\FE63";
-}
-.mdi-thermometer-high::before {
-  content: "\F00ED";
-}
-.mdi-thermometer-lines::before {
-  content: "\F510";
-}
-.mdi-thermometer-low::before {
-  content: "\F00EE";
-}
-.mdi-thermometer-minus::before {
-  content: "\FE64";
-}
-.mdi-thermometer-plus::before {
-  content: "\FE65";
-}
-.mdi-thermostat::before {
-  content: "\F393";
-}
-.mdi-thermostat-box::before {
-  content: "\F890";
-}
-.mdi-thought-bubble::before {
-  content: "\F7F5";
-}
-.mdi-thought-bubble-outline::before {
-  content: "\F7F6";
-}
-.mdi-thumb-down::before {
-  content: "\F511";
-}
-.mdi-thumb-down-outline::before {
-  content: "\F512";
-}
-.mdi-thumb-up::before {
-  content: "\F513";
-}
-.mdi-thumb-up-outline::before {
-  content: "\F514";
-}
-.mdi-thumbs-up-down::before {
-  content: "\F515";
-}
-.mdi-ticket::before {
-  content: "\F516";
-}
-.mdi-ticket-account::before {
-  content: "\F517";
-}
-.mdi-ticket-confirmation::before {
-  content: "\F518";
-}
-.mdi-ticket-outline::before {
-  content: "\F912";
-}
-.mdi-ticket-percent::before {
-  content: "\F723";
-}
-.mdi-tie::before {
-  content: "\F519";
-}
-.mdi-tilde::before {
-  content: "\F724";
-}
-.mdi-timelapse::before {
-  content: "\F51A";
-}
-.mdi-timeline::before {
-  content: "\FBAD";
-}
-.mdi-timeline-alert::before {
-  content: "\FFB2";
-}
-.mdi-timeline-alert-outline::before {
-  content: "\FFB5";
-}
-.mdi-timeline-clock::before {
-  content: "\F0226";
-}
-.mdi-timeline-clock-outline::before {
-  content: "\F0227";
-}
-.mdi-timeline-help::before {
-  content: "\FFB6";
-}
-.mdi-timeline-help-outline::before {
-  content: "\FFB7";
-}
-.mdi-timeline-outline::before {
-  content: "\FBAE";
-}
-.mdi-timeline-plus::before {
-  content: "\FFB3";
-}
-.mdi-timeline-plus-outline::before {
-  content: "\FFB4";
-}
-.mdi-timeline-text::before {
-  content: "\FBAF";
-}
-.mdi-timeline-text-outline::before {
-  content: "\FBB0";
-}
-.mdi-timer::before {
-  content: "\F51B";
-}
-.mdi-timer-10::before {
-  content: "\F51C";
-}
-.mdi-timer-3::before {
-  content: "\F51D";
-}
-.mdi-timer-off::before {
-  content: "\F51E";
-}
-.mdi-timer-sand::before {
-  content: "\F51F";
-}
-.mdi-timer-sand-empty::before {
-  content: "\F6AC";
-}
-.mdi-timer-sand-full::before {
-  content: "\F78B";
-}
-.mdi-timetable::before {
-  content: "\F520";
-}
-.mdi-toaster::before {
-  content: "\F0085";
-}
-.mdi-toaster-off::before {
-  content: "\F01E2";
-}
-.mdi-toaster-oven::before {
-  content: "\FCAF";
-}
-.mdi-toggle-switch::before {
-  content: "\F521";
-}
-.mdi-toggle-switch-off::before {
-  content: "\F522";
-}
-.mdi-toggle-switch-off-outline::before {
-  content: "\FA18";
-}
-.mdi-toggle-switch-outline::before {
-  content: "\FA19";
-}
-.mdi-toilet::before {
-  content: "\F9AA";
-}
-.mdi-toolbox::before {
-  content: "\F9AB";
-}
-.mdi-toolbox-outline::before {
-  content: "\F9AC";
-}
-.mdi-tools::before {
-  content: "\F0086";
-}
-.mdi-tooltip::before {
-  content: "\F523";
-}
-.mdi-tooltip-account::before {
-  content: "\F00C";
-}
-.mdi-tooltip-edit::before {
-  content: "\F524";
-}
-.mdi-tooltip-edit-outline::before {
-  content: "\F02F0";
-}
-.mdi-tooltip-image::before {
-  content: "\F525";
-}
-.mdi-tooltip-image-outline::before {
-  content: "\FBB1";
-}
-.mdi-tooltip-outline::before {
-  content: "\F526";
-}
-.mdi-tooltip-plus::before {
-  content: "\FBB2";
-}
-.mdi-tooltip-plus-outline::before {
-  content: "\F527";
-}
-.mdi-tooltip-text::before {
-  content: "\F528";
-}
-.mdi-tooltip-text-outline::before {
-  content: "\FBB3";
-}
-.mdi-tooth::before {
-  content: "\F8C2";
-}
-.mdi-tooth-outline::before {
-  content: "\F529";
-}
-.mdi-toothbrush::before {
-  content: "\F0154";
-}
-.mdi-toothbrush-electric::before {
-  content: "\F0157";
-}
-.mdi-toothbrush-paste::before {
-  content: "\F0155";
-}
-.mdi-tor::before {
-  content: "\F52A";
-}
-.mdi-tortoise::before {
-  content: "\FD17";
-}
-.mdi-toslink::before {
-  content: "\F02E3";
-}
-.mdi-tournament::before {
-  content: "\F9AD";
-}
-.mdi-tower-beach::before {
-  content: "\F680";
-}
-.mdi-tower-fire::before {
-  content: "\F681";
-}
-.mdi-towing::before {
-  content: "\F83B";
-}
-.mdi-toy-brick::before {
-  content: "\F02B3";
-}
-.mdi-toy-brick-marker::before {
-  content: "\F02B4";
-}
-.mdi-toy-brick-marker-outline::before {
-  content: "\F02B5";
-}
-.mdi-toy-brick-minus::before {
-  content: "\F02B6";
-}
-.mdi-toy-brick-minus-outline::before {
-  content: "\F02B7";
-}
-.mdi-toy-brick-outline::before {
-  content: "\F02B8";
-}
-.mdi-toy-brick-plus::before {
-  content: "\F02B9";
-}
-.mdi-toy-brick-plus-outline::before {
-  content: "\F02BA";
-}
-.mdi-toy-brick-remove::before {
-  content: "\F02BB";
-}
-.mdi-toy-brick-remove-outline::before {
-  content: "\F02BC";
-}
-.mdi-toy-brick-search::before {
-  content: "\F02BD";
-}
-.mdi-toy-brick-search-outline::before {
-  content: "\F02BE";
-}
-.mdi-track-light::before {
-  content: "\F913";
-}
-.mdi-trackpad::before {
-  content: "\F7F7";
-}
-.mdi-trackpad-lock::before {
-  content: "\F932";
-}
-.mdi-tractor::before {
-  content: "\F891";
-}
-.mdi-trademark::before {
-  content: "\FA77";
-}
-.mdi-traffic-cone::before {
-  content: "\F03A7";
-}
-.mdi-traffic-light::before {
-  content: "\F52B";
-}
-.mdi-train::before {
-  content: "\F52C";
-}
-.mdi-train-car::before {
-  content: "\FBB4";
-}
-.mdi-train-variant::before {
-  content: "\F8C3";
-}
-.mdi-tram::before {
-  content: "\F52D";
-}
-.mdi-tram-side::before {
-  content: "\F0008";
-}
-.mdi-transcribe::before {
-  content: "\F52E";
-}
-.mdi-transcribe-close::before {
-  content: "\F52F";
-}
-.mdi-transfer::before {
-  content: "\F0087";
-}
-.mdi-transfer-down::before {
-  content: "\FD7D";
-}
-.mdi-transfer-left::before {
-  content: "\FD7E";
-}
-.mdi-transfer-right::before {
-  content: "\F530";
-}
-.mdi-transfer-up::before {
-  content: "\FD7F";
-}
-.mdi-transit-connection::before {
-  content: "\FD18";
-}
-.mdi-transit-connection-variant::before {
-  content: "\FD19";
-}
-.mdi-transit-detour::before {
-  content: "\FFA8";
-}
-.mdi-transit-transfer::before {
-  content: "\F6AD";
-}
-.mdi-transition::before {
-  content: "\F914";
-}
-.mdi-transition-masked::before {
-  content: "\F915";
-}
-.mdi-translate::before {
-  content: "\F5CA";
-}
-.mdi-translate-off::before {
-  content: "\FE66";
-}
-.mdi-transmission-tower::before {
-  content: "\FD1A";
-}
-.mdi-trash-can::before {
-  content: "\FA78";
-}
-.mdi-trash-can-outline::before {
-  content: "\FA79";
-}
-.mdi-tray::before {
-  content: "\F02BF";
-}
-.mdi-tray-alert::before {
-  content: "\F02C0";
-}
-.mdi-tray-full::before {
-  content: "\F02C1";
-}
-.mdi-tray-minus::before {
-  content: "\F02C2";
-}
-.mdi-tray-plus::before {
-  content: "\F02C3";
-}
-.mdi-tray-remove::before {
-  content: "\F02C4";
-}
-.mdi-treasure-chest::before {
-  content: "\F725";
-}
-.mdi-tree::before {
-  content: "\F531";
-}
-.mdi-tree-outline::before {
-  content: "\FE4C";
-}
-.mdi-trello::before {
-  content: "\F532";
-}
-.mdi-trending-down::before {
-  content: "\F533";
-}
-.mdi-trending-neutral::before {
-  content: "\F534";
-}
-.mdi-trending-up::before {
-  content: "\F535";
-}
-.mdi-triangle::before {
-  content: "\F536";
-}
-.mdi-triangle-outline::before {
-  content: "\F537";
-}
-.mdi-triforce::before {
-  content: "\FBB5";
-}
-.mdi-trophy::before {
-  content: "\F538";
-}
-.mdi-trophy-award::before {
-  content: "\F539";
-}
-.mdi-trophy-broken::before {
-  content: "\FD80";
-}
-.mdi-trophy-outline::before {
-  content: "\F53A";
-}
-.mdi-trophy-variant::before {
-  content: "\F53B";
-}
-.mdi-trophy-variant-outline::before {
-  content: "\F53C";
-}
-.mdi-truck::before {
-  content: "\F53D";
-}
-.mdi-truck-check::before {
-  content: "\FCB0";
-}
-.mdi-truck-check-outline::before {
-  content: "\F02C5";
-}
-.mdi-truck-delivery::before {
-  content: "\F53E";
-}
-.mdi-truck-delivery-outline::before {
-  content: "\F02C6";
-}
-.mdi-truck-fast::before {
-  content: "\F787";
-}
-.mdi-truck-fast-outline::before {
-  content: "\F02C7";
-}
-.mdi-truck-outline::before {
-  content: "\F02C8";
-}
-.mdi-truck-trailer::before {
-  content: "\F726";
-}
-.mdi-trumpet::before {
-  content: "\F00C1";
-}
-.mdi-tshirt-crew::before {
-  content: "\FA7A";
-}
-.mdi-tshirt-crew-outline::before {
-  content: "\F53F";
-}
-.mdi-tshirt-v::before {
-  content: "\FA7B";
-}
-.mdi-tshirt-v-outline::before {
-  content: "\F540";
-}
-.mdi-tumble-dryer::before {
-  content: "\F916";
-}
-.mdi-tumble-dryer-alert::before {
-  content: "\F01E5";
-}
-.mdi-tumble-dryer-off::before {
-  content: "\F01E6";
-}
-.mdi-tumblr::before {
-  content: "\F541";
-}
-.mdi-tumblr-box::before {
-  content: "\F917";
-}
-.mdi-tumblr-reblog::before {
-  content: "\F542";
-}
-.mdi-tune::before {
-  content: "\F62E";
-}
-.mdi-tune-vertical::before {
-  content: "\F66A";
-}
-.mdi-turnstile::before {
-  content: "\FCB1";
-}
-.mdi-turnstile-outline::before {
-  content: "\FCB2";
-}
-.mdi-turtle::before {
-  content: "\FCB3";
-}
-.mdi-twitch::before {
-  content: "\F543";
-}
-.mdi-twitter::before {
-  content: "\F544";
-}
-.mdi-twitter-box::before {
-  content: "\F545";
-}
-.mdi-twitter-circle::before {
-  content: "\F546";
-}
-.mdi-twitter-retweet::before {
-  content: "\F547";
-}
-.mdi-two-factor-authentication::before {
-  content: "\F9AE";
-}
-.mdi-typewriter::before {
-  content: "\FF4A";
-}
-.mdi-uber::before {
-  content: "\F748";
-}
-.mdi-ubisoft::before {
-  content: "\FBB6";
-}
-.mdi-ubuntu::before {
-  content: "\F548";
-}
-.mdi-ufo::before {
-  content: "\F00EF";
-}
-.mdi-ufo-outline::before {
-  content: "\F00F0";
-}
-.mdi-ultra-high-definition::before {
-  content: "\F7F8";
-}
-.mdi-umbraco::before {
-  content: "\F549";
-}
-.mdi-umbrella::before {
-  content: "\F54A";
-}
-.mdi-umbrella-closed::before {
-  content: "\F9AF";
-}
-.mdi-umbrella-outline::before {
-  content: "\F54B";
-}
-.mdi-undo::before {
-  content: "\F54C";
-}
-.mdi-undo-variant::before {
-  content: "\F54D";
-}
-.mdi-unfold-less-horizontal::before {
-  content: "\F54E";
-}
-.mdi-unfold-less-vertical::before {
-  content: "\F75F";
-}
-.mdi-unfold-more-horizontal::before {
-  content: "\F54F";
-}
-.mdi-unfold-more-vertical::before {
-  content: "\F760";
-}
-.mdi-ungroup::before {
-  content: "\F550";
-}
-.mdi-unicode::before {
-  content: "\FEED";
-}
-.mdi-unity::before {
-  content: "\F6AE";
-}
-.mdi-unreal::before {
-  content: "\F9B0";
-}
-.mdi-untappd::before {
-  content: "\F551";
-}
-.mdi-update::before {
-  content: "\F6AF";
-}
-.mdi-upload::before {
-  content: "\F552";
-}
-.mdi-upload-lock::before {
-  content: "\F039E";
-}
-.mdi-upload-lock-outline::before {
-  content: "\F039F";
-}
-.mdi-upload-multiple::before {
-  content: "\F83C";
-}
-.mdi-upload-network::before {
-  content: "\F6F5";
-}
-.mdi-upload-network-outline::before {
-  content: "\FCB4";
-}
-.mdi-upload-off::before {
-  content: "\F00F1";
-}
-.mdi-upload-off-outline::before {
-  content: "\F00F2";
-}
-.mdi-upload-outline::before {
-  content: "\FE67";
-}
-.mdi-usb::before {
-  content: "\F553";
-}
-.mdi-usb-flash-drive::before {
-  content: "\F02C9";
-}
-.mdi-usb-flash-drive-outline::before {
-  content: "\F02CA";
-}
-.mdi-usb-port::before {
-  content: "\F021B";
-}
-.mdi-valve::before {
-  content: "\F0088";
-}
-.mdi-valve-closed::before {
-  content: "\F0089";
-}
-.mdi-valve-open::before {
-  content: "\F008A";
-}
-.mdi-van-passenger::before {
-  content: "\F7F9";
-}
-.mdi-van-utility::before {
-  content: "\F7FA";
-}
-.mdi-vanish::before {
-  content: "\F7FB";
-}
-.mdi-vanity-light::before {
-  content: "\F020C";
-}
-.mdi-variable::before {
-  content: "\FAE6";
-}
-.mdi-variable-box::before {
-  content: "\F013C";
-}
-.mdi-vector-arrange-above::before {
-  content: "\F554";
-}
-.mdi-vector-arrange-below::before {
-  content: "\F555";
-}
-.mdi-vector-bezier::before {
-  content: "\FAE7";
-}
-.mdi-vector-circle::before {
-  content: "\F556";
-}
-.mdi-vector-circle-variant::before {
-  content: "\F557";
-}
-.mdi-vector-combine::before {
-  content: "\F558";
-}
-.mdi-vector-curve::before {
-  content: "\F559";
-}
-.mdi-vector-difference::before {
-  content: "\F55A";
-}
-.mdi-vector-difference-ab::before {
-  content: "\F55B";
-}
-.mdi-vector-difference-ba::before {
-  content: "\F55C";
-}
-.mdi-vector-ellipse::before {
-  content: "\F892";
-}
-.mdi-vector-intersection::before {
-  content: "\F55D";
-}
-.mdi-vector-line::before {
-  content: "\F55E";
-}
-.mdi-vector-link::before {
-  content: "\F0009";
-}
-.mdi-vector-point::before {
-  content: "\F55F";
-}
-.mdi-vector-polygon::before {
-  content: "\F560";
-}
-.mdi-vector-polyline::before {
-  content: "\F561";
-}
-.mdi-vector-polyline-edit::before {
-  content: "\F0250";
-}
-.mdi-vector-polyline-minus::before {
-  content: "\F0251";
-}
-.mdi-vector-polyline-plus::before {
-  content: "\F0252";
-}
-.mdi-vector-polyline-remove::before {
-  content: "\F0253";
-}
-.mdi-vector-radius::before {
-  content: "\F749";
-}
-.mdi-vector-rectangle::before {
-  content: "\F5C6";
-}
-.mdi-vector-selection::before {
-  content: "\F562";
-}
-.mdi-vector-square::before {
-  content: "\F001";
-}
-.mdi-vector-triangle::before {
-  content: "\F563";
-}
-.mdi-vector-union::before {
-  content: "\F564";
-}
-.mdi-venmo::before {
-  content: "\F578";
-}
-.mdi-vhs::before {
-  content: "\FA1A";
-}
-.mdi-vibrate::before {
-  content: "\F566";
-}
-.mdi-vibrate-off::before {
-  content: "\FCB5";
-}
-.mdi-video::before {
-  content: "\F567";
-}
-.mdi-video-3d::before {
-  content: "\F7FC";
-}
-.mdi-video-3d-variant::before {
-  content: "\FEEE";
-}
-.mdi-video-4k-box::before {
-  content: "\F83D";
-}
-.mdi-video-account::before {
-  content: "\F918";
-}
-.mdi-video-check::before {
-  content: "\F008B";
-}
-.mdi-video-check-outline::before {
-  content: "\F008C";
-}
-.mdi-video-image::before {
-  content: "\F919";
-}
-.mdi-video-input-antenna::before {
-  content: "\F83E";
-}
-.mdi-video-input-component::before {
-  content: "\F83F";
-}
-.mdi-video-input-hdmi::before {
-  content: "\F840";
-}
-.mdi-video-input-scart::before {
-  content: "\FFA9";
-}
-.mdi-video-input-svideo::before {
-  content: "\F841";
-}
-.mdi-video-minus::before {
-  content: "\F9B1";
-}
-.mdi-video-off::before {
-  content: "\F568";
-}
-.mdi-video-off-outline::before {
-  content: "\FBB7";
-}
-.mdi-video-outline::before {
-  content: "\FBB8";
-}
-.mdi-video-plus::before {
-  content: "\F9B2";
-}
-.mdi-video-stabilization::before {
-  content: "\F91A";
-}
-.mdi-video-switch::before {
-  content: "\F569";
-}
-.mdi-video-vintage::before {
-  content: "\FA1B";
-}
-.mdi-video-wireless::before {
-  content: "\FEEF";
-}
-.mdi-video-wireless-outline::before {
-  content: "\FEF0";
-}
-.mdi-view-agenda::before {
-  content: "\F56A";
-}
-.mdi-view-agenda-outline::before {
-  content: "\F0203";
-}
-.mdi-view-array::before {
-  content: "\F56B";
-}
-.mdi-view-carousel::before {
-  content: "\F56C";
-}
-.mdi-view-column::before {
-  content: "\F56D";
-}
-.mdi-view-comfy::before {
-  content: "\FE4D";
-}
-.mdi-view-compact::before {
-  content: "\FE4E";
-}
-.mdi-view-compact-outline::before {
-  content: "\FE4F";
-}
-.mdi-view-dashboard::before {
-  content: "\F56E";
-}
-.mdi-view-dashboard-outline::before {
-  content: "\FA1C";
-}
-.mdi-view-dashboard-variant::before {
-  content: "\F842";
-}
-.mdi-view-day::before {
-  content: "\F56F";
-}
-.mdi-view-grid::before {
-  content: "\F570";
-}
-.mdi-view-grid-outline::before {
-  content: "\F0204";
-}
-.mdi-view-grid-plus::before {
-  content: "\FFAA";
-}
-.mdi-view-grid-plus-outline::before {
-  content: "\F0205";
-}
-.mdi-view-headline::before {
-  content: "\F571";
-}
-.mdi-view-list::before {
-  content: "\F572";
-}
-.mdi-view-module::before {
-  content: "\F573";
-}
-.mdi-view-parallel::before {
-  content: "\F727";
-}
-.mdi-view-quilt::before {
-  content: "\F574";
-}
-.mdi-view-sequential::before {
-  content: "\F728";
-}
-.mdi-view-split-horizontal::before {
-  content: "\FBA7";
-}
-.mdi-view-split-vertical::before {
-  content: "\FBA8";
-}
-.mdi-view-stream::before {
-  content: "\F575";
-}
-.mdi-view-week::before {
-  content: "\F576";
-}
-.mdi-vimeo::before {
-  content: "\F577";
-}
-.mdi-violin::before {
-  content: "\F60F";
-}
-.mdi-virtual-reality::before {
-  content: "\F893";
-}
-.mdi-visual-studio::before {
-  content: "\F610";
-}
-.mdi-visual-studio-code::before {
-  content: "\FA1D";
-}
-.mdi-vk::before {
-  content: "\F579";
-}
-.mdi-vk-box::before {
-  content: "\F57A";
-}
-.mdi-vk-circle::before {
-  content: "\F57B";
-}
-.mdi-vlc::before {
-  content: "\F57C";
-}
-.mdi-voice::before {
-  content: "\F5CB";
-}
-.mdi-voice-off::before {
-  content: "\FEF1";
-}
-.mdi-voicemail::before {
-  content: "\F57D";
-}
-.mdi-volleyball::before {
-  content: "\F9B3";
-}
-.mdi-volume-high::before {
-  content: "\F57E";
-}
-.mdi-volume-low::before {
-  content: "\F57F";
-}
-.mdi-volume-medium::before {
-  content: "\F580";
-}
-.mdi-volume-minus::before {
-  content: "\F75D";
-}
-.mdi-volume-mute::before {
-  content: "\F75E";
-}
-.mdi-volume-off::before {
-  content: "\F581";
-}
-.mdi-volume-plus::before {
-  content: "\F75C";
-}
-.mdi-volume-source::before {
-  content: "\F014B";
-}
-.mdi-volume-variant-off::before {
-  content: "\FE68";
-}
-.mdi-volume-vibrate::before {
-  content: "\F014C";
-}
-.mdi-vote::before {
-  content: "\FA1E";
-}
-.mdi-vote-outline::before {
-  content: "\FA1F";
-}
-.mdi-vpn::before {
-  content: "\F582";
-}
-.mdi-vuejs::before {
-  content: "\F843";
-}
-.mdi-vuetify::before {
-  content: "\FE50";
-}
-.mdi-walk::before {
-  content: "\F583";
-}
-.mdi-wall::before {
-  content: "\F7FD";
-}
-.mdi-wall-sconce::before {
-  content: "\F91B";
-}
-.mdi-wall-sconce-flat::before {
-  content: "\F91C";
-}
-.mdi-wall-sconce-variant::before {
-  content: "\F91D";
-}
-.mdi-wallet::before {
-  content: "\F584";
-}
-.mdi-wallet-giftcard::before {
-  content: "\F585";
-}
-.mdi-wallet-membership::before {
-  content: "\F586";
-}
-.mdi-wallet-outline::before {
-  content: "\FBB9";
-}
-.mdi-wallet-plus::before {
-  content: "\FFAB";
-}
-.mdi-wallet-plus-outline::before {
-  content: "\FFAC";
-}
-.mdi-wallet-travel::before {
-  content: "\F587";
-}
-.mdi-wallpaper::before {
-  content: "\FE69";
-}
-.mdi-wan::before {
-  content: "\F588";
-}
-.mdi-wardrobe::before {
-  content: "\FFAD";
-}
-.mdi-wardrobe-outline::before {
-  content: "\FFAE";
-}
-.mdi-warehouse::before {
-  content: "\FFBB";
-}
-.mdi-washing-machine::before {
-  content: "\F729";
-}
-.mdi-washing-machine-alert::before {
-  content: "\F01E7";
-}
-.mdi-washing-machine-off::before {
-  content: "\F01E8";
-}
-.mdi-watch::before {
-  content: "\F589";
-}
-.mdi-watch-export::before {
-  content: "\F58A";
-}
-.mdi-watch-export-variant::before {
-  content: "\F894";
-}
-.mdi-watch-import::before {
-  content: "\F58B";
-}
-.mdi-watch-import-variant::before {
-  content: "\F895";
-}
-.mdi-watch-variant::before {
-  content: "\F896";
-}
-.mdi-watch-vibrate::before {
-  content: "\F6B0";
-}
-.mdi-watch-vibrate-off::before {
-  content: "\FCB6";
-}
-.mdi-water::before {
-  content: "\F58C";
-}
-.mdi-water-boiler::before {
-  content: "\FFAF";
-}
-.mdi-water-boiler-alert::before {
-  content: "\F01DE";
-}
-.mdi-water-boiler-off::before {
-  content: "\F01DF";
-}
-.mdi-water-off::before {
-  content: "\F58D";
-}
-.mdi-water-outline::before {
-  content: "\FE6A";
-}
-.mdi-water-percent::before {
-  content: "\F58E";
-}
-.mdi-water-polo::before {
-  content: "\F02CB";
-}
-.mdi-water-pump::before {
-  content: "\F58F";
-}
-.mdi-water-pump-off::before {
-  content: "\FFB0";
-}
-.mdi-water-well::before {
-  content: "\F008D";
-}
-.mdi-water-well-outline::before {
-  content: "\F008E";
-}
-.mdi-watermark::before {
-  content: "\F612";
-}
-.mdi-wave::before {
-  content: "\FF4B";
-}
-.mdi-waves::before {
-  content: "\F78C";
-}
-.mdi-waze::before {
-  content: "\FBBA";
-}
-.mdi-weather-cloudy::before {
-  content: "\F590";
-}
-.mdi-weather-cloudy-alert::before {
-  content: "\FF4C";
-}
-.mdi-weather-cloudy-arrow-right::before {
-  content: "\FE51";
-}
-.mdi-weather-fog::before {
-  content: "\F591";
-}
-.mdi-weather-hail::before {
-  content: "\F592";
-}
-.mdi-weather-hazy::before {
-  content: "\FF4D";
-}
-.mdi-weather-hurricane::before {
-  content: "\F897";
-}
-.mdi-weather-lightning::before {
-  content: "\F593";
-}
-.mdi-weather-lightning-rainy::before {
-  content: "\F67D";
-}
-.mdi-weather-night::before {
-  content: "\F594";
-}
-.mdi-weather-night-partly-cloudy::before {
-  content: "\FF4E";
-}
-.mdi-weather-partly-cloudy::before {
-  content: "\F595";
-}
-.mdi-weather-partly-lightning::before {
-  content: "\FF4F";
-}
-.mdi-weather-partly-rainy::before {
-  content: "\FF50";
-}
-.mdi-weather-partly-snowy::before {
-  content: "\FF51";
-}
-.mdi-weather-partly-snowy-rainy::before {
-  content: "\FF52";
-}
-.mdi-weather-pouring::before {
-  content: "\F596";
-}
-.mdi-weather-rainy::before {
-  content: "\F597";
-}
-.mdi-weather-snowy::before {
-  content: "\F598";
-}
-.mdi-weather-snowy-heavy::before {
-  content: "\FF53";
-}
-.mdi-weather-snowy-rainy::before {
-  content: "\F67E";
-}
-.mdi-weather-sunny::before {
-  content: "\F599";
-}
-.mdi-weather-sunny-alert::before {
-  content: "\FF54";
-}
-.mdi-weather-sunset::before {
-  content: "\F59A";
-}
-.mdi-weather-sunset-down::before {
-  content: "\F59B";
-}
-.mdi-weather-sunset-up::before {
-  content: "\F59C";
-}
-.mdi-weather-tornado::before {
-  content: "\FF55";
-}
-.mdi-weather-windy::before {
-  content: "\F59D";
-}
-.mdi-weather-windy-variant::before {
-  content: "\F59E";
-}
-.mdi-web::before {
-  content: "\F59F";
-}
-.mdi-web-box::before {
-  content: "\FFB1";
-}
-.mdi-web-clock::before {
-  content: "\F0275";
-}
-.mdi-webcam::before {
-  content: "\F5A0";
-}
-.mdi-webhook::before {
-  content: "\F62F";
-}
-.mdi-webpack::before {
-  content: "\F72A";
-}
-.mdi-webrtc::before {
-  content: "\F0273";
-}
-.mdi-wechat::before {
-  content: "\F611";
-}
-.mdi-weight::before {
-  content: "\F5A1";
-}
-.mdi-weight-gram::before {
-  content: "\FD1B";
-}
-.mdi-weight-kilogram::before {
-  content: "\F5A2";
-}
-.mdi-weight-lifter::before {
-  content: "\F0188";
-}
-.mdi-weight-pound::before {
-  content: "\F9B4";
-}
-.mdi-whatsapp::before {
-  content: "\F5A3";
-}
-.mdi-wheelchair-accessibility::before {
-  content: "\F5A4";
-}
-.mdi-whistle::before {
-  content: "\F9B5";
-}
-.mdi-whistle-outline::before {
-  content: "\F02E7";
-}
-.mdi-white-balance-auto::before {
-  content: "\F5A5";
-}
-.mdi-white-balance-incandescent::before {
-  content: "\F5A6";
-}
-.mdi-white-balance-iridescent::before {
-  content: "\F5A7";
-}
-.mdi-white-balance-sunny::before {
-  content: "\F5A8";
-}
-.mdi-widgets::before {
-  content: "\F72B";
-}
-.mdi-widgets-outline::before {
-  content: "\F0380";
-}
-.mdi-wifi::before {
-  content: "\F5A9";
-}
-.mdi-wifi-off::before {
-  content: "\F5AA";
-}
-.mdi-wifi-star::before {
-  content: "\FE6B";
-}
-.mdi-wifi-strength-1::before {
-  content: "\F91E";
-}
-.mdi-wifi-strength-1-alert::before {
-  content: "\F91F";
-}
-.mdi-wifi-strength-1-lock::before {
-  content: "\F920";
-}
-.mdi-wifi-strength-2::before {
-  content: "\F921";
-}
-.mdi-wifi-strength-2-alert::before {
-  content: "\F922";
-}
-.mdi-wifi-strength-2-lock::before {
-  content: "\F923";
-}
-.mdi-wifi-strength-3::before {
-  content: "\F924";
-}
-.mdi-wifi-strength-3-alert::before {
-  content: "\F925";
-}
-.mdi-wifi-strength-3-lock::before {
-  content: "\F926";
-}
-.mdi-wifi-strength-4::before {
-  content: "\F927";
-}
-.mdi-wifi-strength-4-alert::before {
-  content: "\F928";
-}
-.mdi-wifi-strength-4-lock::before {
-  content: "\F929";
-}
-.mdi-wifi-strength-alert-outline::before {
-  content: "\F92A";
-}
-.mdi-wifi-strength-lock-outline::before {
-  content: "\F92B";
-}
-.mdi-wifi-strength-off::before {
-  content: "\F92C";
-}
-.mdi-wifi-strength-off-outline::before {
-  content: "\F92D";
-}
-.mdi-wifi-strength-outline::before {
-  content: "\F92E";
-}
-.mdi-wii::before {
-  content: "\F5AB";
-}
-.mdi-wiiu::before {
-  content: "\F72C";
-}
-.mdi-wikipedia::before {
-  content: "\F5AC";
-}
-.mdi-wind-turbine::before {
-  content: "\FD81";
-}
-.mdi-window-close::before {
-  content: "\F5AD";
-}
-.mdi-window-closed::before {
-  content: "\F5AE";
-}
-.mdi-window-closed-variant::before {
-  content: "\F0206";
-}
-.mdi-window-maximize::before {
-  content: "\F5AF";
-}
-.mdi-window-minimize::before {
-  content: "\F5B0";
-}
-.mdi-window-open::before {
-  content: "\F5B1";
-}
-.mdi-window-open-variant::before {
-  content: "\F0207";
-}
-.mdi-window-restore::before {
-  content: "\F5B2";
-}
-.mdi-window-shutter::before {
-  content: "\F0147";
-}
-.mdi-window-shutter-alert::before {
-  content: "\F0148";
-}
-.mdi-window-shutter-open::before {
-  content: "\F0149";
-}
-.mdi-windows::before {
-  content: "\F5B3";
-}
-.mdi-windows-classic::before {
-  content: "\FA20";
-}
-.mdi-wiper::before {
-  content: "\FAE8";
-}
-.mdi-wiper-wash::before {
-  content: "\FD82";
-}
-.mdi-wordpress::before {
-  content: "\F5B4";
-}
-.mdi-worker::before {
-  content: "\F5B5";
-}
-.mdi-wrap::before {
-  content: "\F5B6";
-}
-.mdi-wrap-disabled::before {
-  content: "\FBBB";
-}
-.mdi-wrench::before {
-  content: "\F5B7";
-}
-.mdi-wrench-outline::before {
-  content: "\FBBC";
-}
-.mdi-wunderlist::before {
-  content: "\F5B8";
-}
-.mdi-xamarin::before {
-  content: "\F844";
-}
-.mdi-xamarin-outline::before {
-  content: "\F845";
-}
-.mdi-xaml::before {
-  content: "\F673";
-}
-.mdi-xbox::before {
-  content: "\F5B9";
-}
-.mdi-xbox-controller::before {
-  content: "\F5BA";
-}
-.mdi-xbox-controller-battery-alert::before {
-  content: "\F74A";
-}
-.mdi-xbox-controller-battery-charging::before {
-  content: "\FA21";
-}
-.mdi-xbox-controller-battery-empty::before {
-  content: "\F74B";
-}
-.mdi-xbox-controller-battery-full::before {
-  content: "\F74C";
-}
-.mdi-xbox-controller-battery-low::before {
-  content: "\F74D";
-}
-.mdi-xbox-controller-battery-medium::before {
-  content: "\F74E";
-}
-.mdi-xbox-controller-battery-unknown::before {
-  content: "\F74F";
-}
-.mdi-xbox-controller-menu::before {
-  content: "\FE52";
-}
-.mdi-xbox-controller-off::before {
-  content: "\F5BB";
-}
-.mdi-xbox-controller-view::before {
-  content: "\FE53";
-}
-.mdi-xda::before {
-  content: "\F5BC";
-}
-.mdi-xing::before {
-  content: "\F5BD";
-}
-.mdi-xing-box::before {
-  content: "\F5BE";
-}
-.mdi-xing-circle::before {
-  content: "\F5BF";
-}
-.mdi-xml::before {
-  content: "\F5C0";
-}
-.mdi-xmpp::before {
-  content: "\F7FE";
-}
-.mdi-yahoo::before {
-  content: "\FB2A";
-}
-.mdi-yammer::before {
-  content: "\F788";
-}
-.mdi-yeast::before {
-  content: "\F5C1";
-}
-.mdi-yelp::before {
-  content: "\F5C2";
-}
-.mdi-yin-yang::before {
-  content: "\F67F";
-}
-.mdi-yoga::before {
-  content: "\F01A7";
-}
-.mdi-youtube::before {
-  content: "\F5C3";
-}
-.mdi-youtube-creator-studio::before {
-  content: "\F846";
-}
-.mdi-youtube-gaming::before {
-  content: "\F847";
-}
-.mdi-youtube-subscription::before {
-  content: "\FD1C";
-}
-.mdi-youtube-tv::before {
-  content: "\F448";
-}
-.mdi-z-wave::before {
-  content: "\FAE9";
-}
-.mdi-zend::before {
-  content: "\FAEA";
-}
-.mdi-zigbee::before {
-  content: "\FD1D";
-}
-.mdi-zip-box::before {
-  content: "\F5C4";
-}
-.mdi-zip-box-outline::before {
-  content: "\F001B";
-}
-.mdi-zip-disk::before {
-  content: "\FA22";
-}
-.mdi-zodiac-aquarius::before {
-  content: "\FA7C";
-}
-.mdi-zodiac-aries::before {
-  content: "\FA7D";
-}
-.mdi-zodiac-cancer::before {
-  content: "\FA7E";
-}
-.mdi-zodiac-capricorn::before {
-  content: "\FA7F";
-}
-.mdi-zodiac-gemini::before {
-  content: "\FA80";
-}
-.mdi-zodiac-leo::before {
-  content: "\FA81";
-}
-.mdi-zodiac-libra::before {
-  content: "\FA82";
-}
-.mdi-zodiac-pisces::before {
-  content: "\FA83";
-}
-.mdi-zodiac-sagittarius::before {
-  content: "\FA84";
-}
-.mdi-zodiac-scorpio::before {
-  content: "\FA85";
-}
-.mdi-zodiac-taurus::before {
-  content: "\FA86";
-}
-.mdi-zodiac-virgo::before {
-  content: "\FA87";
-}
-.mdi-blank::before {
-  content: "\F68C";
-  visibility: hidden;
-}
-.mdi-18px.mdi-set,
-.mdi-18px.mdi:before {
-  font-size: 18px;
-}
-.mdi-24px.mdi-set,
-.mdi-24px.mdi:before {
-  font-size: 24px;
-}
-.mdi-36px.mdi-set,
-.mdi-36px.mdi:before {
-  font-size: 36px;
-}
-.mdi-48px.mdi-set,
-.mdi-48px.mdi:before {
-  font-size: 48px;
-}
-.mdi-dark:before {
-  color: rgba(0, 0, 0, 0.54);
-}
-.mdi-dark.mdi-inactive:before {
-  color: rgba(0, 0, 0, 0.26);
-}
-.mdi-light:before {
-  color: #fff;
-}
-.mdi-light.mdi-inactive:before {
-  color: rgba(255, 255, 255, 0.3);
-}
-.mdi-rotate-45:before {
-  -webkit-transform: rotate(45deg);
-  -ms-transform: rotate(45deg);
-  transform: rotate(45deg);
-}
-.mdi-rotate-90:before {
-  -webkit-transform: rotate(90deg);
-  -ms-transform: rotate(90deg);
-  transform: rotate(90deg);
-}
-.mdi-rotate-135:before {
-  -webkit-transform: rotate(135deg);
-  -ms-transform: rotate(135deg);
-  transform: rotate(135deg);
-}
-.mdi-rotate-180:before {
-  -webkit-transform: rotate(180deg);
-  -ms-transform: rotate(180deg);
-  transform: rotate(180deg);
-}
-.mdi-rotate-225:before {
-  -webkit-transform: rotate(225deg);
-  -ms-transform: rotate(225deg);
-  transform: rotate(225deg);
-}
-.mdi-rotate-270:before {
-  -webkit-transform: rotate(270deg);
-  -ms-transform: rotate(270deg);
-  transform: rotate(270deg);
-}
-.mdi-rotate-315:before {
-  -webkit-transform: rotate(315deg);
-  -ms-transform: rotate(315deg);
-  transform: rotate(315deg);
-}
-.mdi-flip-h:before {
-  -webkit-transform: scaleX(-1);
-  transform: scaleX(-1);
-  filter: FlipH;
-  -ms-filter: "FlipH";
-}
-.mdi-flip-v:before {
-  -webkit-transform: scaleY(-1);
-  transform: scaleY(-1);
-  filter: FlipV;
-  -ms-filter: "FlipV";
-}
-.mdi-spin:before {
-  -webkit-animation: mdi-spin 2s infinite linear;
-  animation: mdi-spin 2s infinite linear;
-}
-@-webkit-keyframes mdi-spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-  100% {
-    -webkit-transform: rotate(359deg);
-    transform: rotate(359deg);
-  }
-}
-@keyframes mdi-spin {
-  0% {
-    -webkit-transform: rotate(0deg);
-    transform: rotate(0deg);
-  }
-  100% {
-    -webkit-transform: rotate(359deg);
-    transform: rotate(359deg);
-  }
-}
-
-/*# sourceMappingURL=materialdesignicons.css.map */
diff --git a/packages/demobank-ui/src/scss/libs/_all.scss b/packages/demobank-ui/src/scss/libs/_all.scss
deleted file mode 100644
index d33f8acc4..000000000
--- a/packages/demobank-ui/src/scss/libs/_all.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-@import "node_modules/bulma-radio/bulma-radio";
-// @import "node_modules/bulma-responsive-tables/bulma-responsive-tables";
-@import "node_modules/bulma-checkbox/bulma-checkbox";
-// @import "node_modules/bulma-switch-control/bulma-switch-control";
-// @import "node_modules/bulma-upload-control/bulma-upload-control";
-
-/* Bulma */
-@import "node_modules/bulma/bulma";
diff --git a/packages/demobank-ui/src/scss/main.css b/packages/demobank-ui/src/scss/main.css
new file mode 100644
index 000000000..b5c61c956
--- /dev/null
+++ b/packages/demobank-ui/src/scss/main.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/packages/demobank-ui/src/scss/main.scss b/packages/demobank-ui/src/scss/main.scss
deleted file mode 100644
index b9a46718f..000000000
--- a/packages/demobank-ui/src/scss/main.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-@use "pure";
-@use "bank";
-@use "demo";
-@use "toggle";
-@use "colors-bank";
diff --git a/packages/demobank-ui/src/scss/pure.scss b/packages/demobank-ui/src/scss/pure.scss
deleted file mode 100644
index 25a261a5f..000000000
--- a/packages/demobank-ui/src/scss/pure.scss
+++ /dev/null
@@ -1,1397 +0,0 @@
-/*!
-Pure v2.2.0
-Copyright 2013 Yahoo!
-Licensed under the BSD License.
-https://github.com/pure-css/pure/blob/master/LICENSE
-*/
-/*!
-normalize.css v | MIT License | https://necolas.github.io/normalize.css/
-Copyright (c) Nicolas Gallagher and Jonathan Neal
-*/
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
-
-/* Document
-   ========================================================================== */
-
-/**
- * 1. Correct the line height in all browsers.
- * 2. Prevent adjustments of font size after orientation changes in iOS.
- */
-
-html {
-  line-height: 1.15; /* 1 */
-  -webkit-text-size-adjust: 100%; /* 2 */
-}
-
-/* Sections
-   ========================================================================== */
-
-/**
- * Remove the margin in all browsers.
- */
-
-body {
-  margin: 0;
-}
-
-/**
- * Render the `main` element consistently in IE.
- */
-
-main {
-  display: block;
-}
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-
-h1 {
-  font-size: 2em;
-  margin: 0.67em 0;
-}
-
-/* Grouping content
-   ========================================================================== */
-
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-
-hr {
-  -webkit-box-sizing: content-box;
-  box-sizing: content-box; /* 1 */
-  height: 0; /* 1 */
-  overflow: visible; /* 2 */
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-pre {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
-}
-
-/* Text-level semantics
-   ========================================================================== */
-
-/**
- * Remove the gray background on active links in IE 10.
- */
-
-a {
-  background-color: transparent;
-}
-
-/**
- * 1. Remove the bottom border in Chrome 57-
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-
-abbr[title] {
-  border-bottom: none; /* 1 */
-  text-decoration: underline; /* 2 */
-  -webkit-text-decoration: underline dotted;
-  text-decoration: underline dotted; /* 2 */
-}
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-
-b,
-strong {
-  font-weight: bolder;
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-code,
-kbd,
-samp {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
-}
-
-/**
- * Add the correct font size in all browsers.
- */
-
-small {
-  font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-
-sub,
-sup {
-  font-size: 75%;
-  line-height: 0;
-  position: relative;
-  vertical-align: baseline;
-}
-
-sub {
-  bottom: -0.25em;
-}
-
-sup {
-  top: -0.5em;
-}
-
-/* Embedded content
-   ========================================================================== */
-
-/**
- * Remove the border on images inside links in IE 10.
- */
-
-img {
-  border-style: none;
-}
-
-/* Forms
-   ========================================================================== */
-
-/**
- * 1. Change the font styles in all browsers.
- * 2. Remove the margin in Firefox and Safari.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
-  font-family: inherit; /* 1 */
-  font-size: 100%; /* 1 */
-  line-height: 1.15; /* 1 */
-  margin: 0; /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-
-button,
-input {
-  /* 1 */
-  overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-
-button,
-select {
-  /* 1 */
-  text-transform: none;
-}
-
-/**
- * Correct the inability to style clickable types in iOS and Safari.
- */
-
-button,
-[type="button"],
-[type="reset"],
-[type="submit"] {
-  -webkit-appearance: button;
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-
-button::-moz-focus-inner,
-[type="button"]::-moz-focus-inner,
-[type="reset"]::-moz-focus-inner,
-[type="submit"]::-moz-focus-inner {
-  border-style: none;
-  padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-
-button:-moz-focusring,
-[type="button"]:-moz-focusring,
-[type="reset"]:-moz-focusring,
-[type="submit"]:-moz-focusring {
-  outline: 1px dotted ButtonText;
-}
-
-/**
- * Correct the padding in Firefox.
- */
-
-fieldset {
-  padding: 0.35em 0.75em 0.625em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- *    `fieldset` elements in all browsers.
- */
-
-legend {
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box; /* 1 */
-  color: inherit; /* 2 */
-  display: table; /* 1 */
-  max-width: 100%; /* 1 */
-  padding: 0; /* 3 */
-  white-space: normal; /* 1 */
-}
-
-/**
- * Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-
-progress {
-  vertical-align: baseline;
-}
-
-/**
- * Remove the default vertical scrollbar in IE 10+.
- */
-
-textarea {
-  overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10.
- * 2. Remove the padding in IE 10.
- */
-
-[type="checkbox"],
-[type="radio"] {
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box; /* 1 */
-  padding: 0; /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-
-[type="number"]::-webkit-inner-spin-button,
-[type="number"]::-webkit-outer-spin-button {
-  height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-
-[type="search"] {
-  -webkit-appearance: textfield; /* 1 */
-  outline-offset: -2px; /* 2 */
-}
-
-/**
- * Remove the inner padding in Chrome and Safari on macOS.
- */
-
-[type="search"]::-webkit-search-decoration {
-  -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-
-::-webkit-file-upload-button {
-  -webkit-appearance: button; /* 1 */
-  font: inherit; /* 2 */
-}
-
-/* Interactive
-   ========================================================================== */
-
-/*
- * Add the correct display in Edge, IE 10+, and Firefox.
- */
-
-details {
-  display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-
-summary {
-  display: list-item;
-}
-
-/* Misc
-   ========================================================================== */
-
-/**
- * Add the correct display in IE 10+.
- */
-
-template {
-  display: none;
-}
-
-/**
- * Add the correct display in IE 10.
- */
-
-[hidden] {
-  display: none;
-}
-
-/*csslint important:false*/
-
-/* ==========================================================================
-   Pure Base Extras
-   ========================================================================== */
-
-/**
- * Extra rules that Pure adds on top of Normalize.css
- */
-
-html {
-  font-family: sans-serif;
-}
-
-/**
- * Always hide an element when it has the `hidden` HTML attribute.
- */
-
-.hidden,
-[hidden] {
-  display: none !important;
-}
-
-/**
- * Add this class to an image to make it fit within it's fluid parent wrapper while maintaining
- * aspect ratio.
- */
-.pure-img {
-  max-width: 100%;
-  height: auto;
-  display: block;
-}
-
-/*csslint regex-selectors:false, known-properties:false, duplicate-properties:false*/
-
-.pure-g {
-  letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
-  text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */
-
-  /*
-    Sets the font stack to fonts known to work properly with the above letter
-    and word spacings. See: https://github.com/pure-css/pure/issues/41/
-
-    The following font stack makes Pure Grids work on all known environments.
-
-    * FreeSans: Ships with many Linux distros, including Ubuntu
-
-    * Arimo: Ships with Chrome OS. Arimo has to be defined before Helvetica and
-      Arial to get picked up by the browser, even though neither is available
-      in Chrome OS.
-
-    * Droid Sans: Ships with all versions of Android.
-
-    * Helvetica, Arial, sans-serif: Common font stack on OS X and Windows.
-    */
-  font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;
-
-  /* Use flexbox when possible to avoid `letter-spacing` side-effects. */
-  display: -webkit-box;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-box-orient: horizontal;
-  -webkit-box-direction: normal;
-  -ms-flex-flow: row wrap;
-  flex-flow: row wrap;
-
-  /* Prevents distributing space between rows */
-  -ms-flex-line-pack: start;
-  align-content: flex-start;
-}
-
-/* IE10 display: -ms-flexbox (and display: flex in IE 11) does not work inside a table; fall back to block and rely on font hack */
-@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
-  table .pure-g {
-    display: block;
-  }
-}
-
-/* Opera as of 12 on Windows needs word-spacing.
-   The ".opera-only" selector is used to prevent actual prefocus styling
-   and is not required in markup.
-*/
-.opera-only :-o-prefocus,
-.pure-g {
-  word-spacing: -0.43em;
-}
-
-.pure-u {
-  display: inline-block;
-  letter-spacing: normal;
-  word-spacing: normal;
-  vertical-align: top;
-  text-rendering: auto;
-}
-
-/*
-Resets the font family back to the OS/browser's default sans-serif font,
-this the same font stack that Normalize.css sets for the `body`.
-*/
-.pure-g [class*="pure-u"] {
-  font-family: sans-serif;
-}
-
-.pure-u-1,
-.pure-u-1-1,
-.pure-u-1-2,
-.pure-u-1-3,
-.pure-u-2-3,
-.pure-u-1-4,
-.pure-u-3-4,
-.pure-u-1-5,
-.pure-u-2-5,
-.pure-u-3-5,
-.pure-u-4-5,
-.pure-u-5-5,
-.pure-u-1-6,
-.pure-u-5-6,
-.pure-u-1-8,
-.pure-u-3-8,
-.pure-u-5-8,
-.pure-u-7-8,
-.pure-u-1-12,
-.pure-u-5-12,
-.pure-u-7-12,
-.pure-u-11-12,
-.pure-u-1-24,
-.pure-u-2-24,
-.pure-u-3-24,
-.pure-u-4-24,
-.pure-u-5-24,
-.pure-u-6-24,
-.pure-u-7-24,
-.pure-u-8-24,
-.pure-u-9-24,
-.pure-u-10-24,
-.pure-u-11-24,
-.pure-u-12-24,
-.pure-u-13-24,
-.pure-u-14-24,
-.pure-u-15-24,
-.pure-u-16-24,
-.pure-u-17-24,
-.pure-u-18-24,
-.pure-u-19-24,
-.pure-u-20-24,
-.pure-u-21-24,
-.pure-u-22-24,
-.pure-u-23-24,
-.pure-u-24-24 {
-  display: inline-block;
-  letter-spacing: normal;
-  word-spacing: normal;
-  vertical-align: top;
-  text-rendering: auto;
-}
-
-.pure-u-1-24 {
-  width: 4.1667%;
-}
-
-.pure-u-1-12,
-.pure-u-2-24 {
-  width: 8.3333%;
-}
-
-.pure-u-1-8,
-.pure-u-3-24 {
-  width: 12.5%;
-}
-
-.pure-u-1-6,
-.pure-u-4-24 {
-  width: 16.6667%;
-}
-
-.pure-u-1-5 {
-  width: 20%;
-}
-
-.pure-u-5-24 {
-  width: 20.8333%;
-}
-
-.pure-u-1-4,
-.pure-u-6-24 {
-  width: 25%;
-}
-
-.pure-u-7-24 {
-  width: 29.1667%;
-}
-
-.pure-u-1-3,
-.pure-u-8-24 {
-  width: 33.3333%;
-}
-
-.pure-u-3-8,
-.pure-u-9-24 {
-  width: 37.5%;
-}
-
-.pure-u-2-5 {
-  width: 40%;
-}
-
-.pure-u-5-12,
-.pure-u-10-24 {
-  width: 41.6667%;
-}
-
-.pure-u-11-24 {
-  width: 45.8333%;
-}
-
-.pure-u-1-2,
-.pure-u-12-24 {
-  width: 50%;
-}
-
-.pure-u-13-24 {
-  width: 54.1667%;
-}
-
-.pure-u-7-12,
-.pure-u-14-24 {
-  width: 58.3333%;
-}
-
-.pure-u-3-5 {
-  width: 60%;
-}
-
-.pure-u-5-8,
-.pure-u-15-24 {
-  width: 62.5%;
-}
-
-.pure-u-2-3,
-.pure-u-16-24 {
-  width: 66.6667%;
-}
-
-.pure-u-17-24 {
-  width: 70.8333%;
-}
-
-.pure-u-3-4,
-.pure-u-18-24 {
-  width: 75%;
-}
-
-.pure-u-19-24 {
-  width: 79.1667%;
-}
-
-.pure-u-4-5 {
-  width: 80%;
-}
-
-.pure-u-5-6,
-.pure-u-20-24 {
-  width: 83.3333%;
-}
-
-.pure-u-7-8,
-.pure-u-21-24 {
-  width: 87.5%;
-}
-
-.pure-u-11-12,
-.pure-u-22-24 {
-  width: 91.6667%;
-}
-
-.pure-u-23-24 {
-  width: 95.8333%;
-}
-
-.pure-u-1,
-.pure-u-1-1,
-.pure-u-5-5,
-.pure-u-24-24 {
-  width: 100%;
-}
-.pure-button {
-  /* Structure */
-  display: inline-block;
-  line-height: normal;
-  white-space: nowrap;
-  vertical-align: middle;
-  text-align: center;
-  cursor: pointer;
-  -webkit-user-drag: none;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box;
-}
-
-/* Firefox: Get rid of the inner focus border */
-.pure-button::-moz-focus-inner {
-  padding: 0;
-  border: 0;
-}
-
-/* Inherit .pure-g styles */
-.pure-button-group {
-  letter-spacing: -0.31em; /* Webkit: collapse white-space between units */
-  text-rendering: optimizespeed; /* Webkit: fixes text-rendering: optimizeLegibility */
-}
-
-.opera-only :-o-prefocus,
-.pure-button-group {
-  word-spacing: -0.43em;
-}
-
-.pure-button-group .pure-button {
-  letter-spacing: normal;
-  word-spacing: normal;
-  vertical-align: top;
-  text-rendering: auto;
-}
-
-/*csslint outline-none:false*/
-
-.pure-button {
-  font-family: inherit;
-  font-size: 100%;
-  padding: 0.5em 1em;
-  color: rgba(0, 0, 0, 0.8);
-  border: none rgba(0, 0, 0, 0);
-  background-color: #e6e6e6;
-  text-decoration: none;
-  border-radius: 2px;
-}
-
-.pure-button-hover,
-.pure-button:hover,
-.pure-button:focus {
-  background-image: -webkit-gradient(
-    linear,
-    left top,
-    left bottom,
-    from(transparent),
-    color-stop(40%, rgba(0, 0, 0, 0.05)),
-    to(rgba(0, 0, 0, 0.1))
-  );
-  background-image: linear-gradient(
-    transparent,
-    rgba(0, 0, 0, 0.05) 40%,
-    rgba(0, 0, 0, 0.1)
-  );
-}
-.pure-button:focus {
-  outline: 0;
-}
-.pure-button-active,
-.pure-button:active {
-  -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
-    0 0 6px rgba(0, 0, 0, 0.2) inset;
-  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset,
-    0 0 6px rgba(0, 0, 0, 0.2) inset;
-  border-color: #000;
-}
-
-.pure-button[disabled],
-.pure-button-disabled,
-.pure-button-disabled:hover,
-.pure-button-disabled:focus,
-.pure-button-disabled:active {
-  border: none;
-  background-image: none;
-  opacity: 0.4;
-  cursor: not-allowed;
-  -webkit-box-shadow: none;
-  box-shadow: none;
-  pointer-events: none;
-}
-
-.pure-button-hidden {
-  display: none;
-}
-
-.pure-button-primary,
-.pure-button-selected,
-a.pure-button-primary,
-a.pure-button-selected {
-  background-color: rgb(0, 120, 231);
-  color: #fff;
-}
-
-/* Button Groups */
-.pure-button-group .pure-button {
-  margin: 0;
-  border-radius: 0;
-  border-right: 1px solid rgba(0, 0, 0, 0.2);
-}
-
-.pure-button-group .pure-button:first-child {
-  border-top-left-radius: 2px;
-  border-bottom-left-radius: 2px;
-}
-.pure-button-group .pure-button:last-child {
-  border-top-right-radius: 2px;
-  border-bottom-right-radius: 2px;
-  border-right: none;
-}
-
-/*csslint box-model:false*/
-/*
-Box-model set to false because we're setting a height on select elements, which
-also have border and padding. This is done because some browsers don't render
-the padding. We explicitly set the box-model for select elements to border-box,
-so we can ignore the csslint warning.
-*/
-
-.pure-form input[type="text"],
-.pure-form input[type="password"],
-.pure-form input[type="email"],
-.pure-form input[type="url"],
-.pure-form input[type="date"],
-.pure-form input[type="month"],
-.pure-form input[type="time"],
-.pure-form input[type="datetime"],
-.pure-form input[type="datetime-local"],
-.pure-form input[type="week"],
-.pure-form input[type="number"],
-.pure-form input[type="search"],
-.pure-form input[type="tel"],
-.pure-form input[type="color"],
-.pure-form select,
-.pure-form textarea {
-  padding: 0.5em 0.6em;
-  display: inline-block;
-  border: 1px solid #ccc;
-  -webkit-box-shadow: inset 0 1px 3px #ddd;
-  box-shadow: inset 0 1px 3px #ddd;
-  border-radius: 4px;
-  vertical-align: middle;
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box;
-}
-
-/*
-Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
-since IE8 won't execute CSS that contains a CSS3 selector.
-*/
-.pure-form input:not([type]) {
-  padding: 0.5em 0.6em;
-  display: inline-block;
-  border: 1px solid #ccc;
-  -webkit-box-shadow: inset 0 1px 3px #ddd;
-  box-shadow: inset 0 1px 3px #ddd;
-  border-radius: 4px;
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box;
-}
-
-/* Chrome (as of v.32/34 on OS X) needs additional room for color to display. */
-/* May be able to remove this tweak as color inputs become more standardized across browsers. */
-.pure-form input[type="color"] {
-  padding: 0.2em 0.5em;
-}
-
-.pure-form input[type="text"]:focus,
-.pure-form input[type="password"]:focus,
-.pure-form input[type="email"]:focus,
-.pure-form input[type="url"]:focus,
-.pure-form input[type="date"]:focus,
-.pure-form input[type="month"]:focus,
-.pure-form input[type="time"]:focus,
-.pure-form input[type="datetime"]:focus,
-.pure-form input[type="datetime-local"]:focus,
-.pure-form input[type="week"]:focus,
-.pure-form input[type="number"]:focus,
-.pure-form input[type="search"]:focus,
-.pure-form input[type="tel"]:focus,
-.pure-form input[type="color"]:focus,
-.pure-form select:focus,
-.pure-form textarea:focus {
-  outline: 0;
-  border-color: #129fea;
-}
-
-/*
-Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
-since IE8 won't execute CSS that contains a CSS3 selector.
-*/
-.pure-form input:not([type]):focus {
-  outline: 0;
-  border-color: #129fea;
-}
-
-.pure-form input[type="file"]:focus,
-.pure-form input[type="radio"]:focus,
-.pure-form input[type="checkbox"]:focus {
-  outline: thin solid #129fea;
-  outline: 1px auto #129fea;
-}
-.pure-form .pure-checkbox,
-.pure-form .pure-radio {
-  margin: 0.5em 0;
-  display: block;
-}
-
-.pure-form input[type="text"][disabled],
-.pure-form input[type="password"][disabled],
-.pure-form input[type="email"][disabled],
-.pure-form input[type="url"][disabled],
-.pure-form input[type="date"][disabled],
-.pure-form input[type="month"][disabled],
-.pure-form input[type="time"][disabled],
-.pure-form input[type="datetime"][disabled],
-.pure-form input[type="datetime-local"][disabled],
-.pure-form input[type="week"][disabled],
-.pure-form input[type="number"][disabled],
-.pure-form input[type="search"][disabled],
-.pure-form input[type="tel"][disabled],
-.pure-form input[type="color"][disabled],
-.pure-form select[disabled],
-.pure-form textarea[disabled] {
-  cursor: not-allowed;
-  background-color: #eaeded;
-  color: #cad2d3;
-}
-
-/*
-Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
-since IE8 won't execute CSS that contains a CSS3 selector.
-*/
-.pure-form input:not([type])[disabled] {
-  cursor: not-allowed;
-  background-color: #eaeded;
-  color: #cad2d3;
-}
-.pure-form input[readonly],
-.pure-form select[readonly],
-.pure-form textarea[readonly] {
-  background-color: #eee; /* menu hover bg color */
-  color: #777; /* menu text color */
-  border-color: #ccc;
-}
-
-/**
- * Even if we add novalidate property
- * in the form, the styles are applied for
- * invalid elements so we need to remove this styles
- *
- */
-// .pure-form input:focus:invalid,
-// .pure-form textarea:focus:invalid,
-// .pure-form select:focus:invalid {
-//   color: #b94a48;
-//   border-color: #e9322d;
-// }
-// .pure-form input[type="file"]:focus:invalid:focus,
-// .pure-form input[type="radio"]:focus:invalid:focus,
-// .pure-form input[type="checkbox"]:focus:invalid:focus {
-//   outline-color: #e9322d;
-// }
-.pure-form select {
-  /* Normalizes the height; padding is not sufficient. */
-  height: 2.25em;
-  border: 1px solid #ccc;
-  background-color: white;
-}
-.pure-form select[multiple] {
-  height: auto;
-}
-.pure-form label {
-  margin: 0.5em 0 0.2em;
-}
-.pure-form fieldset {
-  margin: 0;
-  padding: 0.35em 0 0.75em;
-  border: 0;
-}
-.pure-form legend {
-  display: block;
-  width: 100%;
-  padding: 0.3em 0;
-  margin-bottom: 0.3em;
-  color: #333;
-  border-bottom: 1px solid #e5e5e5;
-}
-
-.pure-form-stacked input[type="text"],
-.pure-form-stacked input[type="password"],
-.pure-form-stacked input[type="email"],
-.pure-form-stacked input[type="url"],
-.pure-form-stacked input[type="date"],
-.pure-form-stacked input[type="month"],
-.pure-form-stacked input[type="time"],
-.pure-form-stacked input[type="datetime"],
-.pure-form-stacked input[type="datetime-local"],
-.pure-form-stacked input[type="week"],
-.pure-form-stacked input[type="number"],
-.pure-form-stacked input[type="search"],
-.pure-form-stacked input[type="tel"],
-.pure-form-stacked input[type="color"],
-.pure-form-stacked input[type="file"],
-.pure-form-stacked select,
-.pure-form-stacked label,
-.pure-form-stacked textarea {
-  display: block;
-  margin: 0.25em 0;
-}
-
-/*
-Need to separate out the :not() selector from the rest of the CSS 2.1 selectors
-since IE8 won't execute CSS that contains a CSS3 selector.
-*/
-.pure-form-stacked input:not([type]) {
-  display: block;
-  margin: 0.25em 0;
-}
-.pure-form-aligned input,
-.pure-form-aligned textarea,
-.pure-form-aligned select,
-.pure-form-message-inline {
-  display: inline-block;
-  vertical-align: middle;
-}
-.pure-form-aligned textarea {
-  vertical-align: top;
-}
-
-/* Aligned Forms */
-.pure-form-aligned .pure-control-group {
-  margin-bottom: 0.5em;
-}
-.pure-form-aligned .pure-control-group label {
-  text-align: right;
-  display: inline-block;
-  vertical-align: middle;
-  width: 10em;
-  margin: 0 1em 0 0;
-}
-.pure-form-aligned .pure-controls {
-  margin: 1.5em 0 0 11em;
-}
-
-/* Rounded Inputs */
-.pure-form input.pure-input-rounded,
-.pure-form .pure-input-rounded {
-  border-radius: 2em;
-  padding: 0.5em 1em;
-}
-
-/* Grouped Inputs */
-.pure-form .pure-group fieldset {
-  margin-bottom: 10px;
-}
-.pure-form .pure-group input,
-.pure-form .pure-group textarea {
-  display: block;
-  padding: 10px;
-  margin: 0 0 -1px;
-  border-radius: 0;
-  position: relative;
-  top: -1px;
-}
-.pure-form .pure-group input:focus,
-.pure-form .pure-group textarea:focus {
-  z-index: 3;
-}
-.pure-form .pure-group input:first-child,
-.pure-form .pure-group textarea:first-child {
-  top: 1px;
-  border-radius: 4px 4px 0 0;
-  margin: 0;
-}
-.pure-form .pure-group input:first-child:last-child,
-.pure-form .pure-group textarea:first-child:last-child {
-  top: 1px;
-  border-radius: 4px;
-  margin: 0;
-}
-.pure-form .pure-group input:last-child,
-.pure-form .pure-group textarea:last-child {
-  top: -2px;
-  border-radius: 0 0 4px 4px;
-  margin: 0;
-}
-.pure-form .pure-group button {
-  margin: 0.35em 0;
-}
-
-.pure-form .pure-input-1 {
-  width: 100%;
-}
-.pure-form .pure-input-3-4 {
-  width: 75%;
-}
-.pure-form .pure-input-2-3 {
-  width: 66%;
-}
-.pure-form .pure-input-1-2 {
-  width: 50%;
-}
-.pure-form .pure-input-1-3 {
-  width: 33%;
-}
-.pure-form .pure-input-1-4 {
-  width: 25%;
-}
-
-/* Inline help for forms */
-.pure-form-message-inline {
-  display: inline-block;
-  padding-left: 0.3em;
-  color: #666;
-  vertical-align: middle;
-  font-size: 0.875em;
-}
-
-/* Block help for forms */
-.pure-form-message {
-  display: block;
-  color: #666;
-  font-size: 0.875em;
-}
-
-@media only screen and (max-width: 480px) {
-  // .pure-form button[type="submit"] {
-  //   margin: 0.7em 0 0;
-  // }
-
-  // .pure-form input:not([type]),
-  // .pure-form input[type="text"],
-  // .pure-form input[type="password"],
-  // .pure-form input[type="email"],
-  // .pure-form input[type="url"],
-  // .pure-form input[type="date"],
-  // .pure-form input[type="month"],
-  // .pure-form input[type="time"],
-  // .pure-form input[type="datetime"],
-  // .pure-form input[type="datetime-local"],
-  // .pure-form input[type="week"],
-  // .pure-form input[type="number"],
-  // .pure-form input[type="search"],
-  // .pure-form input[type="tel"],
-  // .pure-form input[type="color"],
-  // .pure-form label {
-  //   margin-bottom: 0.3em;
-  //   display: block;
-  // }
-
-  .pure-group input:not([type]),
-  .pure-group input[type="text"],
-  .pure-group input[type="password"],
-  .pure-group input[type="email"],
-  .pure-group input[type="url"],
-  .pure-group input[type="date"],
-  .pure-group input[type="month"],
-  .pure-group input[type="time"],
-  .pure-group input[type="datetime"],
-  .pure-group input[type="datetime-local"],
-  .pure-group input[type="week"],
-  .pure-group input[type="number"],
-  .pure-group input[type="search"],
-  .pure-group input[type="tel"],
-  .pure-group input[type="color"] {
-    margin-bottom: 0;
-  }
-
-  .pure-form-aligned .pure-control-group label {
-    margin-bottom: 0.3em;
-    text-align: left;
-    display: block;
-    width: 100%;
-  }
-
-  .pure-form-aligned .pure-controls {
-    margin: 1.5em 0 0 0;
-  }
-
-  .pure-form-message-inline,
-  .pure-form-message {
-    display: block;
-    font-size: 0.75em;
-    /* Increased bottom padding to make it group with its related input element. */
-    padding: 0.2em 0 0.8em;
-  }
-}
-
-/*csslint adjoining-classes: false, box-model:false*/
-.pure-menu {
-  -webkit-box-sizing: border-box;
-  box-sizing: border-box;
-}
-
-.pure-menu-fixed {
-  position: fixed;
-  left: 0;
-  top: 0;
-  z-index: 3;
-}
-
-.pure-menu-list,
-.pure-menu-item {
-  position: relative;
-}
-
-.pure-menu-list {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-}
-
-.pure-menu-item {
-  padding: 0;
-  margin: 0;
-  height: 100%;
-}
-
-.pure-menu-link,
-.pure-menu-heading {
-  display: block;
-  text-decoration: none;
-  white-space: nowrap;
-}
-
-/* HORIZONTAL MENU */
-.pure-menu-horizontal {
-  width: 100%;
-  white-space: nowrap;
-}
-
-.pure-menu-horizontal .pure-menu-list {
-  display: inline-block;
-}
-
-/* Initial menus should be inline-block so that they are horizontal */
-.pure-menu-horizontal .pure-menu-item,
-.pure-menu-horizontal .pure-menu-heading,
-.pure-menu-horizontal .pure-menu-separator {
-  display: inline-block;
-  vertical-align: middle;
-}
-
-/* Submenus should still be display: block; */
-.pure-menu-item .pure-menu-item {
-  display: block;
-}
-
-.pure-menu-children {
-  display: none;
-  position: absolute;
-  left: 100%;
-  top: 0;
-  margin: 0;
-  padding: 0;
-  z-index: 3;
-}
-
-.pure-menu-horizontal .pure-menu-children {
-  left: 0;
-  top: auto;
-  width: inherit;
-}
-
-.pure-menu-allow-hover:hover > .pure-menu-children,
-.pure-menu-active > .pure-menu-children {
-  display: block;
-  position: absolute;
-}
-
-/* Vertical Menus - show the dropdown arrow */
-.pure-menu-has-children > .pure-menu-link:after {
-  padding-left: 0.5em;
-  content: "\25B8";
-  font-size: small;
-}
-
-/* Horizontal Menus - show the dropdown arrow */
-.pure-menu-horizontal .pure-menu-has-children > .pure-menu-link:after {
-  content: "\25BE";
-}
-
-/* scrollable menus */
-.pure-menu-scrollable {
-  overflow-y: scroll;
-  overflow-x: hidden;
-}
-
-.pure-menu-scrollable .pure-menu-list {
-  display: block;
-}
-
-.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list {
-  display: inline-block;
-}
-
-.pure-menu-horizontal.pure-menu-scrollable {
-  white-space: nowrap;
-  overflow-y: hidden;
-  overflow-x: auto;
-  /* a little extra padding for this style to allow for scrollbars */
-  padding: 0.5em 0;
-}
-
-/* misc default styling */
-
-.pure-menu-separator,
-.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
-  background-color: #ccc;
-  height: 1px;
-  margin: 0.3em 0;
-}
-
-.pure-menu-horizontal .pure-menu-separator {
-  width: 1px;
-  height: 1.3em;
-  margin: 0 0.3em;
-}
-
-/* Need to reset the separator since submenu is vertical */
-.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
-  display: block;
-  width: auto;
-}
-
-.pure-menu-heading {
-  text-transform: uppercase;
-  color: #565d64;
-}
-
-.pure-menu-link {
-  color: #777;
-}
-
-.pure-menu-children {
-  background-color: #fff;
-}
-
-.pure-menu-link,
-.pure-menu-heading {
-  padding: 0.5em 1em;
-}
-
-.pure-menu-disabled {
-  opacity: 0.5;
-}
-
-.pure-menu-disabled .pure-menu-link:hover {
-  background-color: transparent;
-  cursor: default;
-}
-
-.pure-menu-active > .pure-menu-link,
-.pure-menu-link:hover,
-.pure-menu-link:focus {
-  background-color: #eee;
-}
-
-.pure-menu-selected > .pure-menu-link,
-.pure-menu-selected > .pure-menu-link:visited {
-  color: #000;
-}
-
-.pure-table {
-  /* Remove spacing between table cells (from Normalize.css) */
-  border-collapse: collapse;
-  border-spacing: 0;
-  empty-cells: show;
-  border: 1px solid #cbcbcb;
-}
-
-.pure-table caption {
-  color: #000;
-  font: italic 85%/1 arial, sans-serif;
-  padding: 1em 0;
-  text-align: center;
-}
-
-.pure-table td,
-.pure-table th {
-  border-left: 1px solid #cbcbcb; /*  inner column border */
-  border-width: 0 0 0 1px;
-  font-size: inherit;
-  margin: 0;
-  overflow: visible; /*to make ths where the title is really long work*/
-  padding: 0.5em 1em; /* cell padding */
-}
-
-.pure-table thead {
-  background-color: #e0e0e0;
-  color: #000;
-  text-align: left;
-  vertical-align: bottom;
-}
-
-/*
-striping:
-   even - #fff (white)
-   odd  - #f2f2f2 (light gray)
-*/
-.pure-table td {
-  background-color: transparent;
-}
-.pure-table-odd td {
-  background-color: #f2f2f2;
-}
-
-/* nth-child selector for modern browsers */
-.pure-table-striped tr:nth-child(2n-1) td {
-  background-color: #f2f2f2;
-}
-
-/* BORDERED TABLES */
-.pure-table-bordered td {
-  border-bottom: 1px solid #cbcbcb;
-}
-.pure-table-bordered tbody > tr:last-child > td {
-  border-bottom-width: 0;
-}
-
-/* HORIZONTAL BORDERED TABLES */
-
-.pure-table-horizontal td,
-.pure-table-horizontal th {
-  border-width: 0 0 1px 0;
-  border-bottom: 1px solid #cbcbcb;
-}
-.pure-table-horizontal tbody > tr:last-child > td {
-  border-bottom-width: 0;
-}
diff --git a/packages/demobank-ui/src/scss/toggle.scss b/packages/demobank-ui/src/scss/toggle.scss
deleted file mode 100644
index 24636da2f..000000000
--- a/packages/demobank-ui/src/scss/toggle.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-$green: #56c080;
-
-.toggle {
-  cursor: pointer;
-  display: inline-block;
-}
-.toggle-switch {
-  display: inline-block;
-  background: #ccc;
-  border-radius: 16px;
-  width: 58px;
-  height: 32px;
-  position: relative;
-  vertical-align: middle;
-  transition: background 0.25s;
-  &:before,
-  &:after {
-    content: "";
-  }
-  &:before {
-    display: block;
-    background: linear-gradient(to bottom, #fff 0%, #eee 100%);
-    border-radius: 50%;
-    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
-    width: 24px;
-    height: 24px;
-    position: absolute;
-    top: 4px;
-    left: 4px;
-    transition: left 0.25s;
-  }
-  .toggle:hover &:before {
-    background: linear-gradient(to bottom, #fff 0%, #fff 100%);
-    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
-  }
-  .toggle-checkbox:checked + & {
-    background: $green;
-    &:before {
-      left: 30px;
-    }
-  }
-}
-.toggle-checkbox {
-  position: absolute;
-  visibility: hidden;
-}
-.toggle-label {
-  margin-left: 5px;
-  position: relative;
-  top: 2px;
-}
diff --git a/packages/demobank-ui/src/stories.tsx b/packages/demobank-ui/src/stories.tsx
index c6e8eb9ba..87848cb09 100644
--- a/packages/demobank-ui/src/stories.tsx
+++ b/packages/demobank-ui/src/stories.tsx
@@ -25,8 +25,6 @@ import * as components from "./components/index.examples.js";
 
 import { renderStories } from "@gnu-taler/web-util/browser";
 
-import "./scss/main.scss";
-
 function main(): void {
   renderStories(
     { pages, components },
diff --git a/packages/demobank-ui/tailwind.config.js b/packages/demobank-ui/tailwind.config.js
new file mode 100644
index 000000000..01f058b2e
--- /dev/null
+++ b/packages/demobank-ui/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+  content: ["./src/**/*.{html,tsx}"],
+  theme: {
+    extend: {},
+  },
+  plugins: [require("@tailwindcss/typography"), require("@tailwindcss/forms")],
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6c5946a38..295074f61 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,4 +1,4 @@
-lockfileVersion: '6.0'
+lockfileVersion: '6.1'
 
 settings:
   autoInstallPeers: true
@@ -259,6 +259,12 @@ importers:
       '@gnu-taler/pogen':
         specifier: ^0.0.5
         version: link:../pogen
+      '@tailwindcss/forms':
+        specifier: ^0.5.3
+        version: 0.5.3(tailwindcss@3.3.2)
+      '@tailwindcss/typography':
+        specifier: ^0.5.9
+        version: 0.5.9(tailwindcss@3.3.2)
       '@types/chai':
         specifier: ^4.3.0
         version: 4.3.3
@@ -277,6 +283,9 @@ importers:
       '@typescript-eslint/parser':
         specifier: ^5.41.0
         version: 5.41.0(eslint@8.48.0)(typescript@5.2.2)
+      autoprefixer:
+        specifier: ^10.4.14
+        version: 10.4.14(postcss@8.4.23)
       bulma:
         specifier: ^0.9.4
         version: 0.9.4
@@ -307,6 +316,9 @@ importers:
       sass:
         specifier: 1.56.1
         version: 1.56.1
+      tailwindcss:
+        specifier: ^3.3.2
+        version: 3.3.2
       typescript:
         specifier: 5.2.2
         version: 5.2.2
@@ -319,7 +331,7 @@ importers:
     optionalDependencies:
       better-sqlite3:
         specifier: ^8.4.0
-        version: 8.6.0
+        version: 8.4.0
     devDependencies:
       '@types/better-sqlite3':
         specifier: ^7.6.4
@@ -1122,7 +1134,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/types': 7.22.4
-      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/gen-mapping': 0.3.3
       '@jridgewell/trace-mapping': 0.3.17
       jsesc: 2.5.2
 
@@ -4925,15 +4937,15 @@ packages:
     engines: {node: '>=6.0.0'}
     dependencies:
       '@jridgewell/set-array': 1.1.2
-      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/sourcemap-codec': 1.4.15
 
   /@jridgewell/gen-mapping@0.3.2:
     resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
     engines: {node: '>=6.0.0'}
     dependencies:
       '@jridgewell/set-array': 1.1.2
-      '@jridgewell/sourcemap-codec': 1.4.14
-      '@jridgewell/trace-mapping': 0.3.17
+      '@jridgewell/sourcemap-codec': 1.4.15
+      '@jridgewell/trace-mapping': 0.3.19
 
   /@jridgewell/gen-mapping@0.3.3:
     resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
@@ -4942,7 +4954,6 @@ packages:
       '@jridgewell/set-array': 1.1.2
       '@jridgewell/sourcemap-codec': 1.4.15
       '@jridgewell/trace-mapping': 0.3.19
-    dev: true
 
   /@jridgewell/resolve-uri@3.1.0:
     resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
@@ -4960,8 +4971,8 @@ packages:
   /@jridgewell/source-map@0.3.2:
     resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
     dependencies:
-      '@jridgewell/gen-mapping': 0.3.2
-      '@jridgewell/trace-mapping': 0.3.17
+      '@jridgewell/gen-mapping': 0.3.3
+      '@jridgewell/trace-mapping': 0.3.19
     dev: true
 
   /@jridgewell/source-map@0.3.5:
@@ -4976,7 +4987,6 @@ packages:
 
   /@jridgewell/sourcemap-codec@1.4.15:
     resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-    dev: true
 
   /@jridgewell/trace-mapping@0.3.17:
     resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
@@ -4987,15 +4997,14 @@ packages:
   /@jridgewell/trace-mapping@0.3.19:
     resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==}
     dependencies:
-      '@jridgewell/resolve-uri': 3.1.1
+      '@jridgewell/resolve-uri': 3.1.0
       '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
 
   /@jridgewell/trace-mapping@0.3.9:
     resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
     dependencies:
-      '@jridgewell/resolve-uri': 3.1.0
-      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/resolve-uri': 3.1.1
+      '@jridgewell/sourcemap-codec': 1.4.15
     dev: true
 
   /@leichtgewicht/ip-codec@2.0.4:
@@ -5466,7 +5475,7 @@ packages:
       builtin-modules: 3.3.0
       deepmerge: 4.2.2
       is-module: 1.0.0
-      resolve: 1.22.1
+      resolve: 1.22.2
       rollup: 2.79.1
     dev: true
 
@@ -7003,7 +7012,7 @@ packages:
     dependencies:
       '@babel/runtime': 7.19.4
       cosmiconfig: 7.0.1
-      resolve: 1.22.1
+      resolve: 1.22.2
     dev: true
 
   /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.18.9):
@@ -7159,8 +7168,8 @@ packages:
       tweetnacl: 0.14.5
     dev: true
 
-  /better-sqlite3@8.6.0:
-    resolution: {integrity: sha512-jwAudeiTMTSyby+/SfbHDebShbmC2MCH8mU2+DXi0WJfv13ypEJm47cd3kljmy/H130CazEvkf2Li//ewcMJ1g==}
+  /better-sqlite3@8.4.0:
+    resolution: {integrity: sha512-NmsNW1CQvqMszu/CFAJ3pLct6NEFlNfuGM6vw72KHkjOD1UDnL96XNN1BMQc1hiHo8vE2GbOWQYIpZ+YM5wrZw==}
     requiresBuild: true
     dependencies:
       bindings: 1.5.0
@@ -8210,7 +8219,7 @@ packages:
       webpack: ^4.37.0 || ^5.0.0
     dependencies:
       cacache: 15.3.0
-      fast-glob: 3.2.12
+      fast-glob: 3.3.1
       find-cache-dir: 3.3.2
       glob-parent: 5.1.2
       globby: 11.1.0
@@ -8487,6 +8496,7 @@ packages:
   /cssesc@3.0.0:
     resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
     engines: {node: '>=4'}
+    hasBin: true
 
   /cssnano-preset-default@4.0.8:
     resolution: {integrity: sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==}
@@ -8610,7 +8620,7 @@ packages:
       postcss: ^8.2.15
     dependencies:
       cssnano-preset-default: 5.2.12(postcss@8.4.23)
-      lilconfig: 2.0.6
+      lilconfig: 2.1.0
       postcss: 8.4.23
       yaml: 1.10.2
     dev: true
@@ -9449,7 +9459,7 @@ packages:
     resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==}
     dependencies:
       debug: 3.2.7
-      resolve: 1.22.1
+      resolve: 1.22.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -10885,7 +10895,7 @@ packages:
     dependencies:
       array-union: 2.1.0
       dir-glob: 3.0.1
-      fast-glob: 3.2.12
+      fast-glob: 3.3.1
       ignore: 5.2.0
       merge2: 1.4.1
       slash: 3.0.0
@@ -11969,6 +11979,7 @@ packages:
 
   /jiti@1.18.2:
     resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==}
+    hasBin: true
 
   /js-sdsl@4.1.5:
     resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
@@ -12232,11 +12243,6 @@ packages:
       type-check: 0.4.0
     dev: true
 
-  /lilconfig@2.0.6:
-    resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==}
-    engines: {node: '>=10'}
-    dev: true
-
   /lilconfig@2.1.0:
     resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
     engines: {node: '>=10'}
@@ -13966,7 +13972,7 @@ packages:
       ts-node:
         optional: true
     dependencies:
-      lilconfig: 2.0.6
+      lilconfig: 2.1.0
       postcss: 8.4.23
       yaml: 1.10.2
     dev: true
@@ -15309,6 +15315,7 @@ packages:
 
   /resolve@1.22.2:
     resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==}
+    hasBin: true
     dependencies:
       is-core-module: 2.11.0
       path-parse: 1.0.7
@@ -16256,6 +16263,7 @@ packages:
   /sucrase@3.32.0:
     resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==}
     engines: {node: '>=8'}
+    hasBin: true
     dependencies:
       '@jridgewell/gen-mapping': 0.3.2
       commander: 4.1.1
@@ -16368,6 +16376,7 @@ packages:
   /tailwindcss@3.3.2:
     resolution: {integrity: sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==}
     engines: {node: '>=14.0.0'}
+    hasBin: true
     dependencies:
       '@alloc/quick-lru': 5.2.0
       arg: 5.0.2
-- 
cgit v1.2.3


From b3c747151bb3f50d28bf6205cafa4b7dd6ae2b1c Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Thu, 21 Sep 2023 09:41:07 -0300
Subject: more ui

---
 packages/demobank-ui/src/components/CopyButton.tsx |   4 +-
 packages/demobank-ui/src/components/Routing.tsx    |  66 +++++-
 .../src/components/Transactions/state.ts           |   2 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       | 227 +++++++++++----------
 .../src/pages/PaytoWireTransferForm.tsx            | 111 +---------
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  70 +------
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |   8 +-
 7 files changed, 193 insertions(+), 295 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/CopyButton.tsx b/packages/demobank-ui/src/components/CopyButton.tsx
index 97ccbf2bf..b36de770e 100644
--- a/packages/demobank-ui/src/components/CopyButton.tsx
+++ b/packages/demobank-ui/src/components/CopyButton.tsx
@@ -45,14 +45,14 @@ export function CopyButton({ getContent }: { getContent: () => string }): VNode
 
   if (!copied) {
     return (
-      <button class="text-white" onClick={copyText} style={{ width: 32, height: 32, fontSize: "initial" }}>
+      <button class="text-white" onClick={copyText} style={{ width: 16, height: 16, fontSize: "initial" }}>
         <CopyIcon />
       </button>
     );
   }
   return (
     <div class="text-white" content="Copied" style={{ display: "inline-block" }}>
-      <button disabled style={{ width: 32, height: 32, fontSize: "initial" }}>
+      <button disabled style={{ width: 16, height: 16, fontSize: "initial" }}>
         <CopiedIcon />
       </button>
     </div>
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 2c532e863..890058a9b 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -24,12 +24,50 @@ import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
 import { Test } from "../pages/Test.js";
+import { useBackendContext } from "../context/backend.js";
+import { LoginForm } from "../pages/LoginForm.js";
+import { AdminPage } from "../pages/AdminPage.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
+  const backend = useBackendContext();
+
+  if (backend.state.status === "loggedOut") {
+    return <BankFrame
+      goToBusinessAccount={() => {
+        route("/business");
+      }}
+    >
+      <Router history={history}>
+        <Route
+          path="/login"
+          component={() => (
+            <LoginForm
+              onRegister={() => {
+                route("/register");
+              }}
+            />
+          )}
+        />
+        <Route
+          path="/register"
+          component={() => (
+            <RegistrationPage
+              onComplete={() => {
+                route("/account");
+              }}
+            />
+          )}
+        />
+        <Route default component={Redirect} to="/login" />
+      </Router>
+    </BankFrame>
+  }
+  const isAdmin = backend.state.isUserAdministrator
 
   return (
     <BankFrame
+      account={backend.state.username}
       goToBusinessAccount={() => {
         route("/business");
       }}
@@ -69,16 +107,24 @@ export function Routing(): VNode {
         />
         <Route
           path="/account"
-          component={() => (
-            <HomePage
-              onPendingOperationFound={(wopid) => {
-                route(`/operation/${wopid}`);
-              }}
-              onRegister={() => {
-                route("/register");
-              }}
-            />
-          )}
+          component={() => {
+            if (isAdmin) {
+              return <AdminPage
+                onRegister={() => {
+                  route("/register");
+                }}
+              />;
+            } else {
+              return <HomePage
+                onPendingOperationFound={(wopid) => {
+                  route(`/operation/${wopid}`);
+                }}
+                onRegister={() => {
+                  route("/register");
+                }}
+              />
+            }
+          }}
         />
         <Route
           path="/business"
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
index 09c039055..d2a512b08 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -61,7 +61,7 @@ export function useComponentState({ account }: Props): State {
         return;
       }
 
-      const negative = anyItem.direction === "DBIT";
+      const negative = anyItem.direction === "DEBIT";
       const counterpart = negative ? anyItem.creditorIban : anyItem.debtorIban;
 
       let date = anyItem.date ? parseInt(anyItem.date, 10) : 0;
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index e682085ae..80a8224d4 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
 import { useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { StateUpdater, useEffect, useState } from "preact/hooks";
@@ -25,6 +25,7 @@ import { bankUiSettings } from "../settings.js";
 import { useSettings } from "../hooks/settings.js";
 import { CopyButton, CopyIcon } from "../components/CopyButton.js";
 import logo from "../assets/logo-2021.svg";
+import { useAccountDetails } from "../hooks/access.js";
 
 const IS_PUBLIC_ACCOUNT_ENABLED = false;
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
@@ -63,7 +64,9 @@ function MaybeBusinessButton({
 export function BankFrame({
   children,
   goToBusinessAccount,
+  account,
 }: {
+  account: string | undefined,
   children: ComponentChildren;
   goToBusinessAccount?: () => void;
 }): VNode {
@@ -122,123 +125,118 @@ export function BankFrame({
         </div>
 
         {open &&
-          <Fragment>
-            <div class="relative z-10" aria-labelledby="slide-over-title" role="dialog" aria-modal="true"
-              onClick={() => {
-                setOpen(false)
-              }}>
-              <div class="fixed inset-0"></div>
-
-              <div class="fixed inset-0 overflow-hidden">
-                <div class="absolute inset-0 overflow-hidden">
-                  <div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
-                    <div class="pointer-events-auto w-screen max-w-md" >
-                      <div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl" onClick={(e) => {
-                        //do not trigger close if clicking inside the sidebar
-                        e.stopPropagation();
-                      }}>
-                        <div class="px-4 sm:px-6" >
-                          <div class="flex items-start justify-between" >
-                            <h2 class="text-base font-semibold leading-6 text-gray-900" id="slide-over-title">
-                              <i18n.Translate>Settings</i18n.Translate>
-                            </h2>
-                            <div class="ml-3 flex h-7 items-center">
-                              <button type="button" class="relative rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
-                                onClick={(e) => {
-                                  setOpen(false)
-                                }}
-
-                              >
-                                <span class="absolute -inset-2.5"></span>
-                                <span class="sr-only">
-                                  <i18n.Translate>Close panel</i18n.Translate>
-                                </span>
-                                <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-                                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
-                                </svg>
-                              </button>
-                            </div>
+          <div class="relative z-10" aria-labelledby="slide-over-title" role="dialog" aria-modal="true"
+            onClick={() => {
+              setOpen(false)
+            }}>
+            <div class="fixed inset-0"></div>
+
+            <div class="fixed inset-0 overflow-hidden">
+              <div class="absolute inset-0 overflow-hidden">
+                <div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
+                  <div class="pointer-events-auto w-screen max-w-md" >
+                    <div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl" onClick={(e) => {
+                      //do not trigger close if clicking inside the sidebar
+                      e.stopPropagation();
+                    }}>
+                      <div class="px-4 sm:px-6" >
+                        <div class="flex items-start justify-between" >
+                          <h2 class="text-base font-semibold leading-6 text-gray-900" id="slide-over-title">
+                            <i18n.Translate>Preferences</i18n.Translate>
+                          </h2>
+                          <div class="ml-3 flex h-7 items-center">
+                            <button type="button" class="relative rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
+                              onClick={(e) => {
+                                setOpen(false)
+                              }}
+
+                            >
+                              <span class="absolute -inset-2.5"></span>
+                              <span class="sr-only">
+                                <i18n.Translate>Close panel</i18n.Translate>
+                              </span>
+                              <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                                <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
+                              </svg>
+                            </button>
                           </div>
                         </div>
-                        <div class="relative mt-6 flex-1 px-4 sm:px-6">
-                          {/* <!-- Your content --> */}
-
-                          <nav class="flex flex-1 flex-col" aria-label="Sidebar">
-                            <ul role="list" class="flex flex-1 flex-col gap-y-7">
-                              <li>
-                                <ul role="list" class="-mx-2 space-y-1">
-                                  <li>
-                                    <a href="#"
-                                      class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
-                                      onClick={() => {
-                                        backend.logOut();
-                                        setOpen(false)
-                                        updateSettings("currentWithdrawalOperationId", undefined);
-                                      }}
-                                    >
-                                      <svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-                                        <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
-                                      </svg>
-                                      Log out
-                                      {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
-                                    </a>
-                                  </li>
-                                  <li>
-                                    <div class="flex items-center justify-between">
-                                      <span class="flex flex-grow flex-col">
-                                        <span class="text-sm text-black font-medium leading-6 " id="availability-label">
-                                          <i18n.Translate>Show withdrawal confirmation</i18n.Translate>
-                                        </span>
+                      </div>
+                      <div class="relative mt-6 flex-1 px-4 sm:px-6">
+                        <nav class="flex flex-1 flex-col" aria-label="Sidebar">
+                          <ul role="list" class="flex flex-1 flex-col gap-y-7">
+                            <li>
+                              <ul role="list" class="-mx-2 space-y-1">
+                                <li>
+                                  <a href="#"
+                                    class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
+                                    onClick={() => {
+                                      backend.logOut();
+                                      setOpen(false)
+                                      updateSettings("currentWithdrawalOperationId", undefined);
+                                    }}
+                                  >
+                                    <svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                                      <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
+                                    </svg>
+                                    Log out
+                                    {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
+                                  </a>
+                                </li>
+                                <li>
+                                  <div class="flex items-center justify-between">
+                                    <span class="flex flex-grow flex-col">
+                                      <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+                                        <i18n.Translate>Show withdrawal confirmation</i18n.Translate>
                                       </span>
-                                      <button type="button" data-enabled={settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
-                                        onClick={() => {
-                                          console.log(settings.showWithdrawalSuccess)
-                                          updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
-                                        }}>
-                                        <span aria-hidden="true" data-enabled={settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
-                                      </button>
-                                    </div>
-                                  </li>
-                                </ul>
-                              </li>
-
-
-
-                              <li class="sm:hidden">
-                                <div class="text-xs font-semibold leading-6 text-gray-400">
-                                  <i18n.Translate>Sites</i18n.Translate>
-                                </div>
-                                <ul role="list" class="-mx-2 mt-2 space-y-1">
-                                  {bankUiSettings.demoSites.map(([name, url]) => {
-                                    return <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
-                                      <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
-                                      <span class="truncate">{name}</span>
-                                    </a>
-                                  })}
-                                </ul>
-                              </li>
-                            </ul>
-                          </nav>
-                        </div>
+                                    </span>
+                                    <button type="button" data-enabled={settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
+
+                                      onClick={() => {
+                                        console.log(settings.showWithdrawalSuccess)
+                                        updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
+                                      }}>
+                                      <span aria-hidden="true" data-enabled={settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+                                    </button>
+                                  </div>
+                                </li>
+                              </ul>
+                            </li>
+
+
+
+                            <li class="sm:hidden">
+                              <div class="text-xs font-semibold leading-6 text-gray-400">
+                                <i18n.Translate>Sites</i18n.Translate>
+                              </div>
+                              <ul role="list" class="-mx-2 mt-2 space-y-1">
+                                {bankUiSettings.demoSites.map(([name, url]) => {
+                                  return <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
+                                    <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
+                                    <span class="truncate">{name}</span>
+                                  </a>
+                                })}
+                              </ul>
+                            </li>
+                          </ul>
+                        </nav>
                       </div>
                     </div>
                   </div>
                 </div>
               </div>
             </div>
-
-          </Fragment>
+          </div>
         }
       </nav >
 
-      {true &&
+      {account &&
         <header class="py-5 border-t border-indigo-300 border-opacity-25 bg-indigo-600 lg:border-t lg:border-indigo-400 lg:border-opacity-25">
           <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
             <div class=" flex flex-wrap items-center justify-between sm:flex-nowrap">
-              <h3 class="text-2xl font-bold tracking-tight text-white"><WelcomeAccount /></h3>
+              <h3 class="text-2xl font-bold tracking-tight text-white"><WelcomeAccount account={account} /></h3>
               <div>
-                <h3 class="text-2xl font-bold tracking-tight text-white"><AccountBalance /></h3>
+                <h3 class="text-2xl font-bold tracking-tight text-white"><AccountBalance account={account} /></h3>
               </div>
             </div>
           </div>
@@ -466,17 +464,30 @@ function Footer() {
   );
 }
 
-function WelcomeAccount(): VNode {
+function WelcomeAccount({ account }: { account: string }): VNode {
   const { i18n } = useTranslationContext();
-  const account = "Sebastian"
-  const payto: PaytoUriIBAN = parsePaytoUri("payto://iban/bank.localhost/DE955922") as PaytoUriIBAN
+
+  const result = useAccountDetails(account);
+  if (!result.ok) return <div />
+
+  // const account = "Sebastian"
+  const payto = parsePaytoUri(result.data.paytoUri)
+  if (!payto) return <div />
+
+  const accountNumber = !payto.isKnown ? undefined : payto.targetType === "iban" ? payto.iban : payto.targetType === "x-taler-bank" ? payto.account : undefined;
   return <i18n.Translate>
-    Welcome,  {account} (<a href={stringifyPaytoUri(payto)}>{payto.iban}</a>)! <CopyButton getContent={() => stringifyPaytoUri(payto)} />
+    Welcome,  {account} {accountNumber !== undefined ?
+      <span>
+        (<a href={result.data.paytoUri}>{accountNumber}</a> <CopyButton getContent={() => result.data.paytoUri} />)
+      </span>
+      : <Fragment />}!
   </i18n.Translate>
 
 }
 
-function AccountBalance(): VNode {
+function AccountBalance({ account }: { account: string }): VNode {
+  const result = useAccountDetails(account);
+  if (!result.ok) return <div />
 
-  return <div>KUDOS 100.00</div>
+  return <div>{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""} {Amounts.currencyOf(result.data.balance.amount)} {Amounts.stringifyValue(result.data.balance.amount)}</div>
 }
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 5e0624cbf..cdaf363e3 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -151,7 +151,7 @@ export function PaytoWireTransferForm({
             ? error.message
             : JSON.stringify(error)) as TranslatedString
         )
-}
+      }
     }
 
   }
@@ -160,8 +160,7 @@ export function PaytoWireTransferForm({
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Transfer details</i18n.Translate></h2>
       <div>
-        <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
-          {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+        <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-1 sm:gap-x-4">
           <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
             <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onChange={() => {
               setIsRawPayto(false)
@@ -169,14 +168,12 @@ export function PaytoWireTransferForm({
             <span class="flex flex-1">
               <span class="flex flex-col">
                 <span class="block text-sm  font-medium text-gray-900">
-                  <i18n.Translate>form</i18n.Translate>
+                  <i18n.Translate>using a form</i18n.Translate>
                 </span>
               </span>
             </span>
           </label>
 
-
-          {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
           <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
             <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onChange={() => {
               setIsRawPayto(true)
@@ -184,7 +181,7 @@ export function PaytoWireTransferForm({
             <span class="flex flex-1">
               <span class="flex flex-col">
                 <span class="block text-sm  font-medium text-gray-900">
-                  <i18n.Translate>payto://</i18n.Translate>
+                  <i18n.Translate>using the payto:// format</i18n.Translate>
                 </span>
               </span>
             </span>
@@ -325,107 +322,7 @@ export function PaytoWireTransferForm({
     </form>
   </div >
   )
-  // }
-  // return (
-  //   <div>
-  //     <form
-  //       class="pure-form"
-  //       name="wire-transfer-form"
-  //       onSubmit={(e) => {
-  //         e.preventDefault();
-  //       }}
-  //     >
-  //       <label for="iban">{i18n.str`Receiver IBAN:`}</label>&nbsp;
-
-  //       <label for="subject">{i18n.str`Transfer subject:`}</label>&nbsp;
-
-  //       <label for="amount">{i18n.str`Amount:`}</label>&nbsp;
-  //       <div style={{ width: "max-content", display: "flex" }}>
-  //         <input
-  //           type="text"
-  //           readonly
-  //           class="currency-indicator"
-  //           size={limit.currency.length}
-  //           maxLength={limit.currency.length}
-  //           tabIndex={-1}
-  //           style={{
-  //             borderTopRightRadius: 0,
-  //             borderBottomRightRadius: 0,
-  //             borderRight: 0,
-  //           }}
-  //           value={limit.currency}
-  //         />
-  //         <input
-  //           type="number"
-  //           name="amount"
-  //           id="amount"
-  //           placeholder="amount"
-  //           required
-  //           style={{
-  //             borderTopLeftRadius: 0,
-  //             borderBottomLeftRadius: 0,
-  //             borderLeft: 0,
-  //             width: 150,
-  //           }}
-  //           value={amount ?? ""}
-  //           onInput={(e): void => {
-  //             setAmount(e.currentTarget.value);
-  //           }}
-  //         />
-  //       </div>
-  //       <ShowInputErrorLabel
-  //         message={errorsWire?.amount}
-  //         isDirty={amount !== undefined}
-  //       />
-  //       <p style={{ display: "flex", justifyContent: "space-between" }}>
-  //         <input
-  //           type="submit"
-  //           class="pure-button pure-button-primary"
-  //           disabled={!!errorsWire}
-  //           value="Send"
-  //           onClick={async (e) => {
-  //             e.preventDefault();
-  //             if (!(iban && subject && amount)) {
-  //               return;
-  //             }
-
-  //           }}
-  //         />
-  //         <input
-  //           type="button"
-  //           class="pure-button"
-  //           value="Clear"
-  //           onClick={async (e) => {
-  //             e.preventDefault();
-  //             setAmount(undefined);
-  //             setIban(undefined);
-  //             setSubject(undefined);
-  //           }}
-  //         />
-  //       </p>
-  //     </form>
-  //     <p>
-  //       <a
-  //         href="#"
-  //         onClick={(e) => {
-  //           setIsRawPayto(true);
-  //           e.preventDefault();
-  //         }}
-  //       >
-  //         {i18n.str`Want to try the raw payto://-format?`}
-  //       </a>
-  //     </p>
-  //   </div>
-  // );
-
-
-
-  // return (
-  //   <div>
-  //     <p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p>
 
-  //   </div>
-  // );
 }
 export function Amount(
   {
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 6574ec934..8c41f7576 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -78,6 +78,7 @@ export function WalletWithdrawForm({
   async function doStart() {
     if (!parsedAmount) return;
     try {
+      console.log("ASDASD")
       const result = await createWithdrawal({
         amount: Amounts.stringify(parsedAmount),
       });
@@ -106,7 +107,7 @@ export function WalletWithdrawForm({
             ? error.message
             : JSON.stringify(error)) as TranslatedString
         )
-}
+      }
     }
 
   }
@@ -115,7 +116,7 @@ export function WalletWithdrawForm({
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Prepare your wallet</i18n.Translate></h2>
       <p class="mt-1 text-sm text-gray-500">
-        <i18n.Translate>Upon starting you will receive the money in your digital wallet, if you don't have one please <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">install one from here</a></i18n.Translate>.
+        <i18n.Translate>Upon starting you will receive the money in your digital wallet, if you don't have one please <a target="_blank" rel="noreferrer noopener" class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">install one from here</a></i18n.Translate>.
       </p>
       <p class="mt-1 text-sm text-gray-500">
         <i18n.Translate>After using your wallet you will be redirected here to confirm or cancel the operation.</i18n.Translate>
@@ -147,7 +148,7 @@ export function WalletWithdrawForm({
           <div class="sm:col-span-5">
             <span class="isolate inline-flex rounded-md shadow-sm">
               <button type="button"
-                class="relative               inline-flex px-3 py-2 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                 onClick={(e) => {
                   e.preventDefault();
                   setAmountStr("50.00")
@@ -156,7 +157,7 @@ export function WalletWithdrawForm({
                 50.00
               </button>
               <button type="button"
-                class="relative -ml-px -mr-px inline-flex px-3 py-2 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                 onClick={(e) => {
                   e.preventDefault();
                   setAmountStr("25.00")
@@ -166,7 +167,7 @@ export function WalletWithdrawForm({
                 25.00
               </button>
               <button type="button"
-                class="relative -ml-px -mr-px inline-flex px-3 py-2 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                 onClick={(e) => {
                   e.preventDefault();
                   setAmountStr("10.00")
@@ -175,7 +176,7 @@ export function WalletWithdrawForm({
                 10.00
               </button>
               <button type="button"
-                class="relative               inline-flex px-3 py-2 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                 onClick={(e) => {
                   e.preventDefault();
                   setAmountStr("5.00")
@@ -210,60 +211,3 @@ export function WalletWithdrawForm({
   );
 }
 
-// export function Amount(
-//   {
-//     currency,
-//     value,
-//     error,
-//     onChange,
-//   }: {
-//     error?: string;
-//     currency: string;
-//     value: string | undefined;
-//     onChange?: (s: string) => void;
-//   },
-//   ref: Ref<HTMLInputElement>,
-// ): VNode {
-//   return (
-//     <div style={{ width: "max-content" }}>
-//       <div>
-//         <input
-//           type="text"
-//           readonly
-//           class="currency-indicator"
-//           size={currency.length}
-//           maxLength={currency.length}
-//           tabIndex={-1}
-//           style={{
-//             borderTopRightRadius: 0,
-//             borderBottomRightRadius: 0,
-//             borderRight: 0,
-//           }}
-//           value={currency}
-//         />
-//         <input
-//           type="number"
-//           ref={ref}
-//           name="amount"
-//           id="amount"
-//           placeholder="0"
-//           style={{
-//             borderTopLeftRadius: 0,
-//             borderBottomLeftRadius: 0,
-//             borderLeft: 0,
-//             width: 150,
-//             color: "black",
-//           }}
-//           value={value ?? ""}
-//           disabled={!onChange}
-//           onInput={(e): void => {
-//             if (onChange) {
-//               onChange(e.currentTarget.value);
-//             }
-//           }}
-//         />
-//       </div>
-//       <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
-//     </div>
-//   );
-// }
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 80e7a78ac..ced152feb 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -143,7 +143,7 @@ export function WithdrawalConfirmationQuestion({
     <Fragment>
       <div class="bg-white shadow sm:rounded-lg">
         <div class="px-4 py-5 sm:p-6">
-          <h3 class="text-base font-semibold leading-6 text-gray-900">
+          <h3 class="text-base font-semibold text-gray-900">
             <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
           </h3>
           <div class="mt-2 max-w-xl text-sm text-gray-500">
@@ -203,7 +203,7 @@ export function WithdrawalConfirmationQuestion({
 
             <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
               <div class="px-4 sm:px-0">
-                <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Answer the next question to authorize the wire transfer</i18n.Translate></h2>
+                <h2 class="text-base font-semibold text-gray-900"><i18n.Translate>Answer the next question to authorize the wire transfer</i18n.Translate></h2>
               </div>
               <form
                 class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
@@ -264,7 +264,7 @@ export function WithdrawalConfirmationQuestion({
           </div>
           <div class="px-4 mt-4 ">
             <div class="w-full">
-              <div class="px-4 sm:px-0">
+              <div class="px-4 sm:px-0 text-sm">
                 <p><i18n.Translate>Wire transfer details</i18n.Translate></p>
               </div>
               <div class="mt-6 border-t border-gray-100">
@@ -313,7 +313,7 @@ export function WithdrawalConfirmationQuestion({
                   })()}
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                     <dt class="text-sm font-medium leading-6 text-gray-900">Withdrawal identification</dt>
-                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{details.reserve}</dd>
+                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0 break-words">{details.reserve}</dd>
                   </div>
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                     <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
-- 
cgit v1.2.3


From 062939d9cc016a186a282f7a48492c3e01cd740c Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Thu, 21 Sep 2023 10:31:10 -0300
Subject: admin refactor

---
 packages/demobank-ui/src/components/Routing.tsx    |   13 +-
 packages/demobank-ui/src/pages/AdminPage.tsx       | 1042 --------------------
 packages/demobank-ui/src/pages/BusinessAccount.tsx |  758 --------------
 packages/demobank-ui/src/pages/HomePage.tsx        |   19 +-
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   |  143 +++
 .../src/pages/UpdateAccountPassword.tsx            |  131 +++
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |    3 +-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |    4 -
 packages/demobank-ui/src/pages/admin/Account.tsx   |   56 ++
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |  219 ++++
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  120 +++
 .../src/pages/admin/CreateNewAccount.tsx           |  107 ++
 packages/demobank-ui/src/pages/admin/Home.tsx      |  162 +++
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |  112 +++
 packages/demobank-ui/src/pages/business/Home.tsx   |  757 ++++++++++++++
 15 files changed, 1823 insertions(+), 1823 deletions(-)
 delete mode 100644 packages/demobank-ui/src/pages/AdminPage.tsx
 delete mode 100644 packages/demobank-ui/src/pages/BusinessAccount.tsx
 create mode 100644 packages/demobank-ui/src/pages/ShowAccountDetails.tsx
 create mode 100644 packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/Account.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/AccountForm.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/AccountList.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/Home.tsx
 create mode 100644 packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
 create mode 100644 packages/demobank-ui/src/pages/business/Home.tsx

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 890058a9b..ef11af76e 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -19,14 +19,14 @@ import { VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
 import { useEffect } from "preact/hooks";
 import { BankFrame } from "../pages/BankFrame.js";
-import { BusinessAccount } from "../pages/BusinessAccount.js";
+import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
 import { Test } from "../pages/Test.js";
 import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
-import { AdminPage } from "../pages/AdminPage.js";
+import { AdminHome } from "../pages/admin/Home.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
@@ -34,6 +34,7 @@ export function Routing(): VNode {
 
   if (backend.state.status === "loggedOut") {
     return <BankFrame
+      account={undefined}
       goToBusinessAccount={() => {
         route("/business");
       }}
@@ -63,7 +64,7 @@ export function Routing(): VNode {
       </Router>
     </BankFrame>
   }
-  const isAdmin = backend.state.isUserAdministrator
+  const { isUserAdministrator, username } = backend.state
 
   return (
     <BankFrame
@@ -108,14 +109,15 @@ export function Routing(): VNode {
         <Route
           path="/account"
           component={() => {
-            if (isAdmin) {
-              return <AdminPage
+            if (isUserAdministrator) {
+              return <AdminHome
                 onRegister={() => {
                   route("/register");
                 }}
               />;
             } else {
               return <HomePage
+                account={username}
                 onPendingOperationFound={(wopid) => {
                   route(`/operation/${wopid}`);
                 }}
@@ -130,6 +132,7 @@ export function Routing(): VNode {
           path="/business"
           component={() => (
             <BusinessAccount
+              account={username}
               onClose={() => {
                 route("/account");
               }}
diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx
deleted file mode 100644
index 18462bdc3..000000000
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ /dev/null
@@ -1,1042 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri } from "@gnu-taler/taler-util";
-import {
-  ErrorType,
-  HttpResponsePaginated,
-  RequestError,
-  notify,
-  notifyError,
-  notifyInfo,
-  useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Cashouts } from "../components/Cashouts/index.js";
-import { useBackendContext } from "../context/backend.js";
-import { useAccountDetails } from "../hooks/access.js";
-import {
-  useAdminAccountAPI,
-  useBusinessAccountDetails,
-  useBusinessAccounts,
-} from "../hooks/circuit.js";
-import {
-  buildRequestErrorMessage,
-  PartialButDefined,
-  RecursivePartial,
-  undefinedIfEmpty,
-  validateIBAN,
-  WithIntermediate,
-} from "../utils.js";
-import { ShowCashoutDetails } from "./BusinessAccount.js";
-import { handleNotOkResult } from "./HomePage.js";
-import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-
-const charset =
-  "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-const upperIdx = charset.indexOf("A");
-
-function randomPassword(): string {
-  const random = Array.from({ length: 16 }).map(() => {
-    return charset.charCodeAt(Math.random() * charset.length);
-  });
-  // first char can't be upper
-  const charIdx = charset.indexOf(String.fromCharCode(random[0]));
-  random[0] =
-    charIdx > upperIdx ? charset.charCodeAt(charIdx - upperIdx) : random[0];
-  return String.fromCharCode(...random);
-}
-
-interface Props {
-  onRegister: () => void;
-}
-/**
- * Query account information and show QR code if there is pending withdrawal
- */
-export function AdminPage({ onRegister }: Props): VNode {
-  const [account, setAccount] = useState<string | undefined>();
-  const [showDetails, setShowDetails] = useState<string | undefined>();
-  const [showCashouts, setShowCashouts] = useState<string | undefined>();
-  const [updatePassword, setUpdatePassword] = useState<string | undefined>();
-  const [removeAccount, setRemoveAccount] = useState<string | undefined>();
-  const [showCashoutDetails, setShowCashoutDetails] = useState<
-    string | undefined
-  >();
-
-  const [createAccount, setCreateAccount] = useState(false);
-
-  const result = useBusinessAccounts({ account });
-  const { i18n } = useTranslationContext();
-
-  if (result.loading) return <div />;
-  if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
-  }
-
-  const { customers } = result.data;
-
-  if (showCashoutDetails) {
-    return (
-      <ShowCashoutDetails
-        id={showCashoutDetails}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onCancel={() => {
-          setShowCashoutDetails(undefined);
-        }}
-      />
-    );
-  }
-
-  if (showCashouts) {
-    return (
-      <div>
-        <div>
-          <h1 class="nav welcome-text">
-            <i18n.Translate>Cashout for account {showCashouts}</i18n.Translate>
-          </h1>
-        </div>
-        <Cashouts
-          account={showCashouts}
-          onSelected={(id) => {
-            setShowCashouts(id);
-            setShowCashouts(undefined);
-          }}
-        />
-        <p>
-          <input
-            class="pure-button"
-            type="submit"
-            value={i18n.str`Close`}
-            onClick={async (e) => {
-              e.preventDefault();
-              setShowCashouts(undefined);
-            }}
-          />
-        </p>
-      </div>
-    );
-  }
-
-  if (showDetails) {
-    return (
-      <ShowAccountDetails
-        account={showDetails}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onChangePassword={() => {
-          setUpdatePassword(showDetails);
-          setShowDetails(undefined);
-        }}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account updated`);
-          setShowDetails(undefined);
-        }}
-        onClear={() => {
-          setShowDetails(undefined);
-        }}
-      />
-    );
-  }
-  if (removeAccount) {
-    return (
-      <RemoveAccount
-        account={removeAccount}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account removed`);
-          setRemoveAccount(undefined);
-        }}
-        onClear={() => {
-          setRemoveAccount(undefined);
-        }}
-      />
-    );
-  }
-  if (updatePassword) {
-    return (
-      <UpdateAccountPassword
-        account={updatePassword}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Password changed`);
-          setUpdatePassword(undefined);
-        }}
-        onClear={() => {
-          setUpdatePassword(undefined);
-        }}
-      />
-    );
-  }
-  if (createAccount) {
-    return (
-      <CreateNewAccount
-        onClose={() => setCreateAccount(false)}
-        onCreateSuccess={(password) => {
-          notifyInfo(
-            i18n.str`Account created with password "${password}". The user must change the password on the next login.`,
-          );
-          setCreateAccount(false);
-        }}
-      />
-    );
-  }
-
-  return (
-    <Fragment>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>Admin panel</i18n.Translate>
-        </h1>
-      </div>
-
-      <p>
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <div></div>
-          <div>
-            <input
-              class="pure-button pure-button-primary content"
-              type="submit"
-              value={i18n.str`Create account`}
-              onClick={async (e) => {
-                e.preventDefault();
-
-                setCreateAccount(true);
-              }}
-            />
-          </div>
-        </div>
-      </p>
-
-      <AdminAccount onRegister={onRegister} />
-      <section
-        id="main"
-        style={{ width: 600, marginLeft: "auto", marginRight: "auto" }}
-      >
-        {!customers.length ? (
-          <div></div>
-        ) : (
-          <article>
-            <h2>{i18n.str`Accounts:`}</h2>
-            <div class="results">
-              <table class="pure-table pure-table-striped">
-                <thead>
-                  <tr>
-                    <th>{i18n.str`Username`}</th>
-                    <th>{i18n.str`Name`}</th>
-                    <th>{i18n.str`Balance`}</th>
-                    <th>{i18n.str`Actions`}</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  {customers.map((item, idx) => {
-                    const balance = !item.balance
-                      ? undefined
-                      : Amounts.parse(item.balance.amount);
-                    const balanceIsDebit =
-                      item.balance &&
-                      item.balance.credit_debit_indicator == "debit";
-                    return (
-                      <tr key={idx}>
-                        <td>
-                          <a
-                            href="#"
-                            onClick={(e) => {
-                              e.preventDefault();
-                              setShowDetails(item.username);
-                            }}
-                          >
-                            {item.username}
-                          </a>
-                        </td>
-                        <td>{item.name}</td>
-                        <td>
-                          {!balance ? (
-                            i18n.str`unknown`
-                          ) : (
-                            <span class="amount">
-                              {balanceIsDebit ? <b>-</b> : null}
-                              <span class="value">{`${Amounts.stringifyValue(
-                                balance,
-                              )}`}</span>
-                              &nbsp;
-                              <span class="currency">{`${balance.currency}`}</span>
-                            </span>
-                          )}
-                        </td>
-                        <td>
-                          <a
-                            href="#"
-                            onClick={(e) => {
-                              e.preventDefault();
-                              setUpdatePassword(item.username);
-                            }}
-                          >
-                            change password
-                          </a>
-                          &nbsp;
-                          <a
-                            href="#"
-                            onClick={(e) => {
-                              e.preventDefault();
-                              setShowCashouts(item.username);
-                            }}
-                          >
-                            cashouts
-                          </a>
-                          &nbsp;
-                          <a
-                            href="#"
-                            onClick={(e) => {
-                              e.preventDefault();
-                              setRemoveAccount(item.username);
-                            }}
-                          >
-                            remove
-                          </a>
-                        </td>
-                      </tr>
-                    );
-                  })}
-                </tbody>
-              </table>
-            </div>
-          </article>
-        )}
-      </section>
-    </Fragment>
-  );
-}
-
-function AdminAccount({ onRegister }: { onRegister: () => void }): VNode {
-  const { i18n } = useTranslationContext();
-  const r = useBackendContext();
-  const account = r.state.status === "loggedIn" ? r.state.username : "admin";
-  const result = useAccountDetails(account);
-
-  if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
-  }
-  const { data } = result;
-  const balance = Amounts.parseOrThrow(data.balance.amount);
-  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
-  const limit = balanceIsDebit
-    ? Amounts.sub(debitThreshold, balance).amount
-    : Amounts.add(balance, debitThreshold).amount;
-  if (!balance) return <Fragment />;
-  return (
-    <Fragment>
-      <section id="assets">
-        <div class="asset-summary">
-          <h2>{i18n.str`Bank account balance`}</h2>
-          {!balance ? (
-            <div class="large-amount" style={{ color: "gray" }}>
-              Waiting server response...
-            </div>
-          ) : (
-            <div class="large-amount amount">
-              {balanceIsDebit ? <b>-</b> : null}
-              <span class="value">{`${Amounts.stringifyValue(balance)}`}</span>
-              &nbsp;
-              <span class="currency">{`${balance.currency}`}</span>
-            </div>
-          )}
-        </div>
-      </section>
-      <PaytoWireTransferForm
-        focus
-        limit={limit}
-        onSuccess={() => {
-          notifyInfo(i18n.str`Wire transfer created!`);
-        }}
-        onCancel={undefined}
-      />
-    </Fragment>
-  );
-}
-
-const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
-const EMAIL_REGEX =
-  /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
-
-function initializeFromTemplate(
-  account: SandboxBackend.Circuit.CircuitAccountData | undefined,
-): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
-  const emptyAccount = {
-    cashout_address: undefined,
-    iban: undefined,
-    name: undefined,
-    username: undefined,
-    contact_data: undefined,
-  };
-  const emptyContact = {
-    email: undefined,
-    phone: undefined,
-  };
-
-  const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
-    structuredClone(account) ?? emptyAccount;
-  if (typeof initial.contact_data === "undefined") {
-    initial.contact_data = emptyContact;
-  }
-  initial.contact_data.email;
-  return initial as any;
-}
-
-export function UpdateAccountPassword({
-  account,
-  onClear,
-  onUpdateSuccess,
-  onLoadNotOk,
-}: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
-  onClear: () => void;
-  onUpdateSuccess: () => void;
-  account: string;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  const { changePassword } = useAdminAccountAPI();
-  const [password, setPassword] = useState<string | undefined>();
-  const [repeat, setRepeat] = useState<string | undefined>();
-
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
-    }
-    return onLoadNotOk(result);
-  }
-
-  const errors = undefinedIfEmpty({
-    password: !password ? i18n.str`required` : undefined,
-    repeat: !repeat
-      ? i18n.str`required`
-      : password !== repeat
-        ? i18n.str`password doesn't match`
-        : undefined,
-  });
-
-  return (
-    <div>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>Update password for {account}</i18n.Translate>
-        </h1>
-      </div>
-
-      <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-        <form class="pure-form">
-          <fieldset>
-            <label>{i18n.str`Password`}</label>
-            <input
-              type="password"
-              value={password ?? ""}
-              onChange={(e) => {
-                setPassword(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.password}
-              isDirty={password !== undefined}
-            />
-          </fieldset>
-          <fieldset>
-            <label>{i18n.str`Repeat password`}</label>
-            <input
-              type="password"
-              value={repeat ?? ""}
-              onChange={(e) => {
-                setRepeat(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.repeat}
-              isDirty={repeat !== undefined}
-            />
-          </fieldset>
-        </form>
-        <p>
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <div>
-              <input
-                class="pure-button"
-                type="submit"
-                value={i18n.str`Close`}
-                onClick={async (e) => {
-                  e.preventDefault();
-                  onClear();
-                }}
-              />
-            </div>
-            <div>
-              <input
-                id="select-exchange"
-                class="pure-button pure-button-primary content"
-                disabled={!!errors}
-                type="submit"
-                value={i18n.str`Confirm`}
-                onClick={async (e) => {
-                  e.preventDefault();
-                  if (!!errors || !password) return;
-                  try {
-                    const r = await changePassword(account, {
-                      new_password: password,
-                    });
-                    onUpdateSuccess();
-                  } catch (error) {
-                    if (error instanceof RequestError) {
-                      notify(buildRequestErrorMessage(i18n, error.cause));
-                    } else {
-                      notifyError(i18n.str`Operation failed, please report`, (error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error)) as TranslatedString)
-                    }
-                  }
-                }}
-              />
-            </div>
-          </div>
-        </p>
-      </div>
-    </div>
-  );
-}
-
-function CreateNewAccount({
-  onClose,
-  onCreateSuccess,
-}: {
-  onClose: () => void;
-  onCreateSuccess: (password: string) => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  const { createAccount } = useAdminAccountAPI();
-  const [submitAccount, setSubmitAccount] = useState<
-    SandboxBackend.Circuit.CircuitAccountData | undefined
-  >();
-  return (
-    <div>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>New account</i18n.Translate>
-        </h1>
-      </div>
-
-      <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-        <AccountForm
-          template={undefined}
-          purpose="create"
-          onChange={(a) => {
-            setSubmitAccount(a);
-          }}
-        />
-
-        <p>
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <div>
-              <input
-                class="pure-button"
-                type="submit"
-                value={i18n.str`Close`}
-                onClick={async (e) => {
-                  e.preventDefault();
-                  onClose();
-                }}
-              />
-            </div>
-            <div>
-              <input
-                id="select-exchange"
-                class="pure-button pure-button-primary content"
-                disabled={!submitAccount}
-                type="submit"
-                value={i18n.str`Confirm`}
-                onClick={async (e) => {
-                  e.preventDefault();
-
-                  if (!submitAccount) return;
-                  try {
-                    const account: SandboxBackend.Circuit.CircuitAccountRequest =
-                    {
-                      cashout_address: submitAccount.cashout_address,
-                      contact_data: submitAccount.contact_data,
-                      internal_iban: submitAccount.iban,
-                      name: submitAccount.name,
-                      username: submitAccount.username,
-                      password: randomPassword(),
-                    };
-
-                    await createAccount(account);
-                    onCreateSuccess(account.password);
-                  } catch (error) {
-                    if (error instanceof RequestError) {
-                      notify(
-                        buildRequestErrorMessage(i18n, error.cause, {
-                          onClientError: (status) =>
-                            status === HttpStatusCode.Forbidden
-                              ? i18n.str`The rights to perform the operation are not sufficient`
-                              : status === HttpStatusCode.BadRequest
-                                ? i18n.str`Input data was invalid`
-                                : status === HttpStatusCode.Conflict
-                                  ? i18n.str`At least one registration detail was not available`
-                                  : undefined,
-                        }),
-                      );
-                    } else {
-                      notifyError(
-                        i18n.str`Operation failed, please report`,
-                        (error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error)) as TranslatedString
-                      )
-                    }
-                  }
-                }}
-              />
-            </div>
-          </div>
-        </p>
-      </div>
-    </div>
-  );
-}
-
-export function ShowAccountDetails({
-  account,
-  onClear,
-  onUpdateSuccess,
-  onLoadNotOk,
-  onChangePassword,
-}: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
-  onClear?: () => void;
-  onChangePassword: () => void;
-  onUpdateSuccess: () => void;
-  account: string;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  const { updateAccount } = useAdminAccountAPI();
-  const [update, setUpdate] = useState(false);
-  const [submitAccount, setSubmitAccount] = useState<
-    SandboxBackend.Circuit.CircuitAccountData | undefined
-  >();
-
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
-    }
-    return onLoadNotOk(result);
-  }
-
-  return (
-    <div>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>Business account details</i18n.Translate>
-        </h1>
-      </div>
-      <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-        <AccountForm
-          template={result.data}
-          purpose={update ? "update" : "show"}
-          onChange={(a) => setSubmitAccount(a)}
-        />
-
-        <p class="buttons-account">
-          <div
-            style={{
-              display: "flex",
-              justifyContent: "space-between",
-              flexFlow: "wrap-reverse",
-            }}
-          >
-            <div>
-              {onClear ? (
-                <input
-                  class="pure-button"
-                  type="submit"
-                  value={i18n.str`Close`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    onClear();
-                  }}
-                />
-              ) : undefined}
-            </div>
-            <div style={{ display: "flex" }}>
-              <div>
-                <input
-                  id="select-exchange"
-                  class="pure-button pure-button-primary content"
-                  disabled={update && !submitAccount}
-                  type="submit"
-                  value={i18n.str`Change password`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    onChangePassword();
-                  }}
-                />
-              </div>
-              <div>
-                <input
-                  id="select-exchange"
-                  class="pure-button pure-button-primary content"
-                  disabled={update && !submitAccount}
-                  type="submit"
-                  value={update ? i18n.str`Confirm` : i18n.str`Update`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-
-                    if (!update) {
-                      setUpdate(true);
-                    } else {
-                      if (!submitAccount) return;
-                      try {
-                        await updateAccount(account, {
-                          cashout_address: submitAccount.cashout_address,
-                          contact_data: submitAccount.contact_data,
-                        });
-                        onUpdateSuccess();
-                      } catch (error) {
-                        if (error instanceof RequestError) {
-                          notify(
-                            buildRequestErrorMessage(i18n, error.cause, {
-                              onClientError: (status) =>
-                                status === HttpStatusCode.Forbidden
-                                  ? i18n.str`The rights to change the account are not sufficient`
-                                  : status === HttpStatusCode.NotFound
-                                    ? i18n.str`The username was not found`
-                                    : undefined,
-                            }),
-                          );
-                        } else {
-                          notifyError(
-                            i18n.str`Operation failed, please report`,
-                            (error instanceof Error
-                              ? error.message
-                              : JSON.stringify(error)) as TranslatedString
-                          )
-                        }
-                      }
-                    }
-                  }}
-                />
-              </div>
-            </div>
-          </div>
-        </p>
-      </div>
-    </div>
-  );
-}
-
-function RemoveAccount({
-  account,
-  onClear,
-  onUpdateSuccess,
-  onLoadNotOk,
-}: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
-  onClear: () => void;
-  onUpdateSuccess: () => void;
-  account: string;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  const result = useAccountDetails(account);
-  const { deleteAccount } = useAdminAccountAPI();
-
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
-    }
-    return onLoadNotOk(result);
-  }
-
-  const balance = Amounts.parse(result.data.balance.amount);
-  if (!balance) {
-    return <div>there was an error reading the balance</div>;
-  }
-  const isBalanceEmpty = Amounts.isZero(balance);
-  return (
-    <div>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>Remove account: {account}</i18n.Translate>
-        </h1>
-      </div>
-      {/* {FXME: SHOW WARNING} */}
-      {/* {!isBalanceEmpty && (
-        <ErrorBannerFloat
-          error={{
-            title: i18n.str`Can't delete the account`,
-            description: i18n.str`Balance is not empty`,
-          }}
-          onClear={() => saveError(undefined)}
-        />
-      )} */}
-
-      <p>
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <div>
-            <input
-              class="pure-button"
-              type="submit"
-              value={i18n.str`Cancel`}
-              onClick={async (e) => {
-                e.preventDefault();
-                onClear();
-              }}
-            />
-          </div>
-          <div>
-            <input
-              id="select-exchange"
-              class="pure-button pure-button-primary content"
-              disabled={!isBalanceEmpty}
-              type="submit"
-              value={i18n.str`Confirm`}
-              onClick={async (e) => {
-                e.preventDefault();
-                try {
-                  const r = await deleteAccount(account);
-                  onUpdateSuccess();
-                } catch (error) {
-                  if (error instanceof RequestError) {
-                    notify(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.Forbidden
-                            ? i18n.str`The administrator specified a institutional username`
-                            : status === HttpStatusCode.NotFound
-                              ? i18n.str`The username was not found`
-                              : status === HttpStatusCode.PreconditionFailed
-                                ? i18n.str`Balance was not zero`
-                                : undefined,
-                      }),
-                    );
-                  } else {
-                    notifyError(i18n.str`Operation failed, please report`,
-                      (error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error)) as TranslatedString);
-                  }
-                }
-              }}
-            />
-          </div>
-        </div>
-      </p>
-    </div>
-  );
-}
-/**
- * Create valid account object to update or create
- * Take template as initial values for the form
- * Purpose indicate if all field al read only (show), part of them (update)
- * or none (create)
- * @param param0
- * @returns
- */
-function AccountForm({
-  template,
-  purpose,
-  onChange,
-}: {
-  template: SandboxBackend.Circuit.CircuitAccountData | undefined;
-  onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
-  purpose: "create" | "update" | "show";
-}): VNode {
-  const initial = initializeFromTemplate(template);
-  const [form, setForm] = useState(initial);
-  const [errors, setErrors] = useState<
-    RecursivePartial<typeof initial> | undefined
-  >(undefined);
-  const { i18n } = useTranslationContext();
-
-  function updateForm(newForm: typeof initial): void {
-    const parsed = !newForm.cashout_address
-      ? undefined
-      : parsePaytoUri(newForm.cashout_address);
-
-    const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
-      cashout_address: !newForm.cashout_address
-        ? i18n.str`required`
-        : !parsed
-          ? i18n.str`does not follow the pattern`
-          : !parsed.isKnown || parsed.targetType !== "iban"
-            ? i18n.str`only "IBAN" target are supported`
-            : !IBAN_REGEX.test(parsed.iban)
-              ? i18n.str`IBAN should have just uppercased letters and numbers`
-              : validateIBAN(parsed.iban, i18n),
-      contact_data: undefinedIfEmpty({
-        email: !newForm.contact_data?.email
-          ? i18n.str`required`
-          : !EMAIL_REGEX.test(newForm.contact_data.email)
-            ? i18n.str`it should be an email`
-            : undefined,
-        phone: !newForm.contact_data?.phone
-          ? i18n.str`required`
-          : !newForm.contact_data.phone.startsWith("+")
-            ? i18n.str`should start with +`
-            : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
-              ? i18n.str`phone number can't have other than numbers`
-              : undefined,
-      }),
-      iban: !newForm.iban
-        ? undefined //optional field
-        : !IBAN_REGEX.test(newForm.iban)
-          ? i18n.str`IBAN should have just uppercased letters and numbers`
-          : validateIBAN(newForm.iban, i18n),
-      name: !newForm.name ? i18n.str`required` : undefined,
-      username: !newForm.username ? i18n.str`required` : undefined,
-    });
-    setErrors(errors);
-    setForm(newForm);
-    onChange(errors === undefined ? (newForm as any) : undefined);
-  }
-
-  return (
-    <form class="pure-form">
-      <fieldset>
-        <label for="username">
-          {i18n.str`Username`}
-          {purpose === "create" && <b style={{ color: "red" }}>*</b>}
-        </label>
-        <input
-          name="username"
-          type="text"
-          disabled={purpose !== "create"}
-          value={form.username}
-          onChange={(e) => {
-            form.username = e.currentTarget.value;
-            updateForm(structuredClone(form));
-          }}
-        />{" "}
-        <ShowInputErrorLabel
-          message={errors?.username}
-          isDirty={form.username !== undefined}
-        />
-      </fieldset>
-      <fieldset>
-        <label>
-          {i18n.str`Name`}
-          {purpose === "create" && <b style={{ color: "red" }}>*</b>}
-        </label>
-        <input
-          disabled={purpose !== "create"}
-          value={form.name ?? ""}
-          onChange={(e) => {
-            form.name = e.currentTarget.value;
-            updateForm(structuredClone(form));
-          }}
-        />
-        <ShowInputErrorLabel
-          message={errors?.name}
-          isDirty={form.name !== undefined}
-        />
-      </fieldset>
-      {purpose !== "create" && (
-        <fieldset>
-          <label>{i18n.str`Internal IBAN`}</label>
-          <input
-            disabled={true}
-            value={form.iban ?? ""}
-            onChange={(e) => {
-              form.iban = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.iban}
-            isDirty={form.iban !== undefined}
-          />
-        </fieldset>
-      )}
-      <fieldset>
-        <label>
-          {i18n.str`Email`}
-          {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-        </label>
-        <input
-          disabled={purpose === "show"}
-          value={form.contact_data.email ?? ""}
-          onChange={(e) => {
-            form.contact_data.email = e.currentTarget.value;
-            updateForm(structuredClone(form));
-          }}
-        />
-        <ShowInputErrorLabel
-          message={errors?.contact_data?.email}
-          isDirty={form.contact_data.email !== undefined}
-        />
-      </fieldset>
-      <fieldset>
-        <label>
-          {i18n.str`Phone`}
-          {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-        </label>
-        <input
-          disabled={purpose === "show"}
-          value={form.contact_data.phone ?? ""}
-          onChange={(e) => {
-            form.contact_data.phone = e.currentTarget.value;
-            updateForm(structuredClone(form));
-          }}
-        />
-        <ShowInputErrorLabel
-          message={errors?.contact_data?.phone}
-          isDirty={form.contact_data?.phone !== undefined}
-        />
-      </fieldset>
-      <fieldset>
-        <label>
-          {i18n.str`Cashout address`}
-          {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-        </label>
-        <input
-          disabled={purpose === "show"}
-          value={(form.cashout_address ?? "").substring("payto://iban/".length)}
-          onChange={(e) => {
-            form.cashout_address = "payto://iban/" + e.currentTarget.value;
-            updateForm(structuredClone(form));
-          }}
-        />
-        <ShowInputErrorLabel
-          message={errors?.cashout_address}
-          isDirty={form.cashout_address !== undefined}
-        />
-      </fieldset>
-    </form>
-  );
-}
diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx
deleted file mode 100644
index ec71ceca6..000000000
--- a/packages/demobank-ui/src/pages/BusinessAccount.tsx
+++ /dev/null
@@ -1,758 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-import {
-  AmountJson,
-  Amounts,
-  HttpStatusCode,
-  TranslatedString
-} from "@gnu-taler/taler-util";
-import {
-  HttpResponse,
-  HttpResponsePaginated,
-  RequestError,
-  notify,
-  notifyError,
-  notifyInfo,
-  useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { Cashouts } from "../components/Cashouts/index.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { useBackendContext } from "../context/backend.js";
-import { useAccountDetails } from "../hooks/access.js";
-import {
-  useCashoutDetails,
-  useCircuitAccountAPI,
-  useEstimator,
-  useRatiosAndFeeConfig,
-} from "../hooks/circuit.js";
-import {
-  TanChannel,
-  buildRequestErrorMessage,
-  undefinedIfEmpty,
-} from "../utils.js";
-import { ShowAccountDetails, UpdateAccountPassword } from "./AdminPage.js";
-import { handleNotOkResult } from "./HomePage.js";
-import { LoginForm } from "./LoginForm.js";
-import { Amount } from "./PaytoWireTransferForm.js";
-
-interface Props {
-  onClose: () => void;
-  onRegister: () => void;
-  onLoadNotOk: () => void;
-}
-export function BusinessAccount({
-  onClose,
-  onLoadNotOk,
-  onRegister,
-}: Props): VNode {
-  const { i18n } = useTranslationContext();
-  const backend = useBackendContext();
-  const [updatePassword, setUpdatePassword] = useState(false);
-  const [newCashout, setNewcashout] = useState(false);
-  const [showCashoutDetails, setShowCashoutDetails] = useState<
-    string | undefined
-  >();
-
-  if (backend.state.status === "loggedOut") {
-    return <LoginForm onRegister={onRegister} />;
-  }
-
-  if (newCashout) {
-    return (
-      <CreateCashout
-        account={backend.state.username}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onCancel={() => {
-          setNewcashout(false);
-        }}
-        onComplete={(id) => {
-          notifyInfo(
-            i18n.str`Cashout created. You need to confirm the operation to complete the transaction.`,
-          );
-          setNewcashout(false);
-          setShowCashoutDetails(id);
-        }}
-      />
-    );
-  }
-  if (showCashoutDetails) {
-    return (
-      <ShowCashoutDetails
-        id={showCashoutDetails}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onCancel={() => {
-          setShowCashoutDetails(undefined);
-        }}
-      />
-    );
-  }
-  if (updatePassword) {
-    return (
-      <UpdateAccountPassword
-        account={backend.state.username}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Password changed`);
-          setUpdatePassword(false);
-        }}
-        onClear={() => {
-          setUpdatePassword(false);
-        }}
-      />
-    );
-  }
-  return (
-    <div>
-      <ShowAccountDetails
-        account={backend.state.username}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
-        onUpdateSuccess={() => {
-          notifyInfo(i18n.str`Account updated`);
-        }}
-        onChangePassword={() => {
-          setUpdatePassword(true);
-        }}
-        onClear={onClose}
-      />
-      <section style={{ marginTop: "2em" }}>
-        <div class="active">
-          <h3>{i18n.str`Latest cashouts`}</h3>
-          <Cashouts
-            account={backend.state.username}
-            onSelected={(id) => {
-              setShowCashoutDetails(id);
-            }}
-          />
-        </div>
-        <br />
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <div />
-          <input
-            class="pure-button pure-button-primary content"
-            type="submit"
-            value={i18n.str`New cashout`}
-            onClick={async (e) => {
-              e.preventDefault();
-              setNewcashout(true);
-            }}
-          />
-        </div>
-      </section>
-    </div>
-  );
-}
-
-interface PropsCashout {
-  account: string;
-  onComplete: (id: string) => void;
-  onCancel: () => void;
-  onLoadNotOk: <T>(
-    error:
-      | HttpResponsePaginated<T, SandboxBackend.SandboxError>
-      | HttpResponse<T, SandboxBackend.SandboxError>,
-  ) => VNode;
-}
-
-type FormType = {
-  isDebit: boolean;
-  amount: string;
-  subject: string;
-  channel: TanChannel;
-};
-type ErrorFrom<T> = {
-  [P in keyof T]+?: string;
-};
-
-// check #7719
-function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
-  SandboxBackend.Circuit.Config & { hasChanged?: boolean },
-  SandboxBackend.SandboxError
-> {
-  const result = useRatiosAndFeeConfig();
-  const [oldResult, setOldResult] = useState<
-    SandboxBackend.Circuit.Config | undefined
-  >(undefined);
-  const dataFromBackend = result.ok ? result.data : undefined;
-  useEffect(() => {
-    // save only the first result of /config to the backend
-    if (!dataFromBackend || oldResult !== undefined) return;
-    setOldResult(dataFromBackend);
-  }, [dataFromBackend]);
-
-  if (!result.ok) return result;
-
-  const data = !oldResult ? result.data : oldResult;
-  const hasChanged =
-    oldResult &&
-    (result.data.name !== oldResult.name ||
-      result.data.version !== oldResult.version ||
-      result.data.ratios_and_fees.buy_at_ratio !==
-        oldResult.ratios_and_fees.buy_at_ratio ||
-      result.data.ratios_and_fees.buy_in_fee !==
-        oldResult.ratios_and_fees.buy_in_fee ||
-      result.data.ratios_and_fees.sell_at_ratio !==
-        oldResult.ratios_and_fees.sell_at_ratio ||
-      result.data.ratios_and_fees.sell_out_fee !==
-        oldResult.ratios_and_fees.sell_out_fee ||
-      result.data.fiat_currency !== oldResult.fiat_currency);
-
-  return {
-    ...result,
-    data: { ...data, hasChanged },
-  };
-}
-
-function CreateCashout({
-  account,
-  onComplete,
-  onCancel,
-  onLoadNotOk,
-}: PropsCashout): VNode {
-  const { i18n } = useTranslationContext();
-  const ratiosResult = useRatiosAndFeeConfig();
-  const result = useAccountDetails(account);
-  const {
-    estimateByCredit: calculateFromCredit,
-    estimateByDebit: calculateFromDebit,
-  } = useEstimator();
-  const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
-
-  const { createCashout } = useCircuitAccountAPI();
-  if (!result.ok) return onLoadNotOk(result);
-  if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
-  const config = ratiosResult.data;
-
-  const balance = Amounts.parseOrThrow(result.data.balance.amount);
-  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
-  const zero = Amounts.zeroOfCurrency(balance.currency);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
-  const limit = balanceIsDebit
-    ? Amounts.sub(debitThreshold, balance).amount
-    : Amounts.add(balance, debitThreshold).amount;
-
-  const zeroCalc = { debit: zero, credit: zero, beforeFee: zero };
-  const [calc, setCalc] = useState(zeroCalc);
-  const sellRate = config.ratios_and_fees.sell_at_ratio;
-  const sellFee = !config.ratios_and_fees.sell_out_fee
-    ? zero
-    : Amounts.parseOrThrow(
-        `${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
-      );
-  const fiatCurrency = config.fiat_currency;
-
-  if (!sellRate || sellRate < 0) return <div>error rate</div>;
-
-  const amount = Amounts.parseOrThrow(
-    `${!form.isDebit ? fiatCurrency : balance.currency}:${
-      !form.amount ? "0" : form.amount
-    }`,
-  );
-
-  useEffect(() => {
-    if (form.isDebit) {
-      calculateFromDebit(amount, sellFee, sellRate)
-        .then((r) => {
-          setCalc(r);
-        })
-        .catch((error) => {
-          notify(
-            error instanceof RequestError
-              ? buildRequestErrorMessage(i18n, error.cause)
-              : {
-                  type: "error",
-                  title: i18n.str`Could not estimate the cashout`,
-                  description: error.message as TranslatedString
-                },
-          );
-        });
-    } else {
-      calculateFromCredit(amount, sellFee, sellRate)
-        .then((r) => {
-          setCalc(r);
-        })
-        .catch((error) => {
-          notify(
-            error instanceof RequestError
-              ? buildRequestErrorMessage(i18n, error.cause)
-              : {
-                  type: "error",
-                  title: i18n.str`Could not estimate the cashout`,
-                  description: error.message,
-                },
-          );
-        });
-    }
-  }, [form.amount, form.isDebit]);
-
-  const balanceAfter = Amounts.sub(balance, calc.debit).amount;
-
-  function updateForm(newForm: typeof form): void {
-    setForm(newForm);
-  }
-  const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
-    amount: !form.amount
-      ? i18n.str`required`
-      : !amount
-      ? i18n.str`could not be parsed`
-      : Amounts.cmp(limit, calc.debit) === -1
-      ? i18n.str`balance is not enough`
-      : Amounts.cmp(calc.beforeFee, sellFee) === -1
-      ? i18n.str`the total amount to transfer does not cover the fees`
-      : Amounts.isZero(calc.credit)
-      ? i18n.str`the total transfer at destination will be zero`
-      : undefined,
-    channel: !form.channel ? i18n.str`required` : undefined,
-  });
-
-  return (
-    <div>
-      <h1>New cashout</h1>
-      <form class="pure-form">
-        <fieldset>
-          <label>{i18n.str`Subject`}</label>
-          <input
-            value={form.subject ?? ""}
-            onChange={(e) => {
-              form.subject = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.subject}
-            isDirty={form.subject !== undefined}
-          />
-        </fieldset>
-        <fieldset>
-          <label for="amount">
-            {form.isDebit
-              ? i18n.str`Amount to send`
-              : i18n.str`Amount to receive`}
-            
-          </label>
-          <div style={{ display: "flex" }}>
-            <Amount
-              name="amount"
-              currency={amount.currency}
-              value={form.amount}
-              onChange={(v) => {
-                form.amount = v;
-                updateForm(structuredClone(form));
-              }}
-              error={errors?.amount}
-            />
-            <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
-              <input
-                class="toggle-checkbox"
-                type="checkbox"
-                name="asd"
-                onChange={(e): void => {
-                  console.log("asdasd", form.isDebit);
-                  form.isDebit = !form.isDebit;
-                  updateForm(structuredClone(form));
-                }}
-              />
-              <div class="toggle-switch"></div>
-            </label>
-          </div>
-        </fieldset>
-        <fieldset>
-          <label>{i18n.str`Conversion rate`}</label>
-          <input value={sellRate} disabled />
-        </fieldset>
-        <fieldset>
-          <label for="balance-now">{i18n.str`Balance now`}</label>
-          <Amount
-            name="banace-now"
-            currency={balance.currency}
-            value={Amounts.stringifyValue(balance)}
-          />
-        </fieldset>
-        <fieldset>
-          <label for="total-cost"
-            style={{ fontWeight: "bold", color: "red" }}
-          >{i18n.str`Total cost`}</label>
-          <Amount
-            name="total-cost"
-            currency={balance.currency}
-            value={Amounts.stringifyValue(calc.debit)}
-          />
-        </fieldset>
-        <fieldset>
-          <label for="balance-after">{i18n.str`Balance after`}</label>
-          <Amount
-            name="balance-after"
-            currency={balance.currency}
-            value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
-          />
-        </fieldset>{" "}
-        {Amounts.isZero(sellFee) ? undefined : (
-          <Fragment>
-            <fieldset>
-              <label for="amount-conversiojn">{i18n.str`Amount after conversion`}</label>
-              <Amount
-                name="amount-conversion"
-                currency={fiatCurrency}
-                value={Amounts.stringifyValue(calc.beforeFee)}
-              />
-            </fieldset>
-
-            <fieldset>
-              <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
-              <Amount
-                name="cashout-fee"
-                currency={fiatCurrency}
-                value={Amounts.stringifyValue(sellFee)}
-              />
-            </fieldset>
-          </Fragment>
-        )}
-        <fieldset>
-          <label for="total"
-            style={{ fontWeight: "bold", color: "green" }}
-          >{i18n.str`Total cashout transfer`}</label>
-          <Amount
-            name="total"
-            currency={fiatCurrency}
-            value={Amounts.stringifyValue(calc.credit)}
-          />
-        </fieldset>
-        <fieldset>
-          <label>{i18n.str`Confirmation channel`}</label>
-
-          <div class="channel">
-            <input
-              class={
-                "pure-button content " +
-                (form.channel === TanChannel.EMAIL
-                  ? "pure-button-primary"
-                  : "pure-button-secondary")
-              }
-              type="submit"
-              value={i18n.str`Email`}
-              onClick={async (e) => {
-                e.preventDefault();
-                form.channel = TanChannel.EMAIL;
-                updateForm(structuredClone(form));
-              }}
-            />
-            <input
-              class={
-                "pure-button content " +
-                (form.channel === TanChannel.SMS
-                  ? "pure-button-primary"
-                  : "pure-button-secondary")
-              }
-              type="submit"
-              value={i18n.str`SMS`}
-              onClick={async (e) => {
-                e.preventDefault();
-                form.channel = TanChannel.SMS;
-                updateForm(structuredClone(form));
-              }}
-            />
-            <input
-              class={
-                "pure-button content " +
-                (form.channel === TanChannel.FILE
-                  ? "pure-button-primary"
-                  : "pure-button-secondary")
-              }
-              type="submit"
-              value={i18n.str`FILE`}
-              onClick={async (e) => {
-                e.preventDefault();
-                form.channel = TanChannel.FILE;
-                updateForm(structuredClone(form));
-              }}
-            />
-          </div>
-          <ShowInputErrorLabel
-            message={errors?.channel}
-            isDirty={form.channel !== undefined}
-          />
-        </fieldset>
-        <br />
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <button
-            class="pure-button pure-button-secondary btn-cancel"
-            onClick={(e) => {
-              e.preventDefault();
-              onCancel();
-            }}
-          >
-            {i18n.str`Cancel`}
-          </button>
-
-          <button
-            class="pure-button pure-button-primary btn-register"
-            type="submit"
-            disabled={!!errors}
-            onClick={async (e) => {
-              e.preventDefault();
-
-              if (errors) return;
-              try {
-                const res = await createCashout({
-                  amount_credit: Amounts.stringify(calc.credit),
-                  amount_debit: Amounts.stringify(calc.debit),
-                  subject: form.subject,
-                  tan_channel: form.channel,
-                });
-                onComplete(res.data.uuid);
-              } catch (error) {
-                if (error instanceof RequestError) {
-                  notify(
-                    buildRequestErrorMessage(i18n, error.cause, {
-                      onClientError: (status) =>
-                        status === HttpStatusCode.BadRequest
-                          ? i18n.str`The exchange rate was incorrectly applied`
-                          : status === HttpStatusCode.Forbidden
-                          ? i18n.str`A institutional user tried the operation`
-                          : status === HttpStatusCode.Conflict
-                          ? i18n.str`Need a contact data where to send the TAN`
-                          : status === HttpStatusCode.PreconditionFailed
-                          ? i18n.str`The account does not have sufficient funds`
-                          : undefined,
-                      onServerError: (status) =>
-                        status === HttpStatusCode.ServiceUnavailable
-                          ? i18n.str`The bank does not support the TAN channel for this operation`
-                          : undefined,
-                    }),
-                  );
-                } else {
-                  notifyError(
-                    i18n.str`Operation failed, please report`,
-                    (error instanceof Error
-                      ? error.message
-                      : JSON.stringify(error)) as TranslatedString
-                  )
-            }
-              }
-            }}
-          >
-            {i18n.str`Create`}
-          </button>
-        </div>
-      </form>
-    </div>
-  );
-}
-
-interface ShowCashoutProps {
-  id: string;
-  onCancel: () => void;
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
-}
-export function ShowCashoutDetails({
-  id,
-  onCancel,
-  onLoadNotOk,
-}: ShowCashoutProps): VNode {
-  const { i18n } = useTranslationContext();
-  const result = useCashoutDetails(id);
-  const { abortCashout, confirmCashout } = useCircuitAccountAPI();
-  const [code, setCode] = useState<string | undefined>(undefined);
-  if (!result.ok) return onLoadNotOk(result);
-  const errors = undefinedIfEmpty({
-    code: !code ? i18n.str`required` : undefined,
-  });
-  const isPending = String(result.data.status).toUpperCase() === "PENDING";
-  return (
-    <div>
-      <h1>Cashout details {id}</h1>
-      <form class="pure-form">
-        <fieldset>
-          <label>
-            <i18n.Translate>Subject</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.subject} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Created</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.creation_time ?? ""} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Confirmed</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.confirmation_time ?? ""} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Debited</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.amount_debit} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Credit</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.amount_credit} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Status</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.status} />
-        </fieldset>
-        <fieldset>
-          <label>
-            <i18n.Translate>Destination</i18n.Translate>
-          </label>
-          <input readOnly value={result.data.cashout_address} />
-        </fieldset>
-        {isPending ? (
-          <fieldset>
-            <label>
-              <i18n.Translate>Code</i18n.Translate>
-            </label>
-            <input
-              value={code ?? ""}
-              onChange={(e) => {
-                setCode(e.currentTarget.value);
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.code}
-              isDirty={code !== undefined}
-            />
-          </fieldset>
-        ) : undefined}
-      </form>
-      <br />
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <button
-          class="pure-button pure-button-secondary btn-cancel"
-          onClick={(e) => {
-            e.preventDefault();
-            onCancel();
-          }}
-        >
-          {i18n.str`Back`}
-        </button>
-        {isPending ? (
-          <div>
-            <button
-              type="submit"
-              class="pure-button pure-button-primary button-error"
-              onClick={async (e) => {
-                e.preventDefault();
-                try {
-                  await abortCashout(id);
-                  onCancel();
-                } catch (error) {
-                  if (error instanceof RequestError) {
-                    notify(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.NotFound
-                            ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
-                            : status === HttpStatusCode.PreconditionFailed
-                            ? i18n.str`Cashout was already confimed`
-                            : undefined,
-                      }),
-                    );
-                  } else {
-                    notifyError(
-                      i18n.str`Operation failed, please report`,
-                      (error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error)) as TranslatedString
-                    )
-                }
-                }
-              }}
-            >
-              {i18n.str`Abort`}
-            </button>
-            &nbsp;
-            <button
-              type="submit"
-              disabled={!code}
-              class="pure-button pure-button-primary "
-              onClick={async (e) => {
-                e.preventDefault();
-                try {
-                  if (!code) return;
-                  const rest = await confirmCashout(id, {
-                    tan: code,
-                  });
-                } catch (error) {
-                  if (error instanceof RequestError) {
-                    notify(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.NotFound
-                            ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
-                            : status === HttpStatusCode.PreconditionFailed
-                            ? i18n.str`Cashout was already confimed`
-                            : status === HttpStatusCode.Conflict
-                            ? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
-                            : status === HttpStatusCode.Forbidden
-                            ? i18n.str`Invalid code`
-                            : undefined,
-                      }),
-                    );
-                  } else {
-                    notifyError(
-                      i18n.str`Operation failed, please report`,
-                      (error instanceof Error
-                        ? error.message
-                        : JSON.stringify(error)) as TranslatedString
-                    )
-                }
-                }
-              }}
-            >
-              {i18n.str`Confirm`}
-            </button>
-          </div>
-        ) : (
-          <div />
-        )}
-      </div>
-    </div>
-  );
-}
-
-const MAX_AMOUNT_DIGIT = 2;
-/**
- * Truncate the amount of digits to display
- * in the form based on the fee calculations
- *
- * Backend must have the same truncation
- * @param a
- * @returns
- */
-function truncate(a: AmountJson): AmountJson {
-  const str = Amounts.stringify(a);
-  const idx = str.indexOf(".");
-  if (idx === -1) {
-    return a;
-  }
-  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT);
-  return Amounts.parseOrThrow(truncated);
-}
-
-export function assertUnreachable(x: never): never {
-  throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index e82e46eb2..a911f347c 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -35,7 +35,7 @@ import { useBackendContext } from "../context/backend.js";
 import { getInitialBackendBaseURL } from "../hooks/backend.js";
 import { useSettings } from "../hooks/settings.js";
 import { AccountPage } from "./AccountPage/index.js";
-import { AdminPage } from "./AdminPage.js";
+import { AdminHome } from "./admin/Home.js";
 import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
 import { error } from "console";
@@ -54,31 +54,24 @@ const logger = new Logger("AccountPage");
  */
 export function HomePage({
   onRegister,
+  account,
   onPendingOperationFound,
 }: {
+  account: string,
   onPendingOperationFound: (id: string) => void;
   onRegister: () => void;
 }): VNode {
-  const backend = useBackendContext();
   const [settings] = useSettings();
   const { i18n } = useTranslationContext();
 
-  if (backend.state.status === "loggedOut") {
-    return <LoginForm onRegister={onRegister} />;
-  }
-
   if (settings.currentWithdrawalOperationId) {
     onPendingOperationFound(settings.currentWithdrawalOperationId);
     return <Loading />;
   }
 
-  if (backend.state.isUserAdministrator) {
-    return <AdminPage onRegister={onRegister} />;
-  }
-
   return (
     <AccountPage
-      account={backend.state.username}
+      account={account}
       onLoadNotOk={handleNotOkResult(i18n, onRegister)}
     />
   );
@@ -105,8 +98,8 @@ export function WithdrawalOperationPage({
 
   if (!parsedUri) {
     notifyError(
-      i18n.str`The Withdrawal URI is not valid: "${uri}"`,
-      undefined
+      i18n.str`The Withdrawal URI is not valid`,
+      uri as TranslatedString
     );
     return <Loading />;
   }
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
new file mode 100644
index 000000000..91b50b84c
--- /dev/null
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -0,0 +1,143 @@
+import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode,h } from "preact";
+import { useAdminAccountAPI, useBusinessAccountDetails } from "../hooks/circuit.js";
+import { useState } from "preact/hooks";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { buildRequestErrorMessage } from "../utils.js";
+import { AccountForm } from "./admin/AccountForm.js";
+
+export function ShowAccountDetails({
+    account,
+    onClear,
+    onUpdateSuccess,
+    onLoadNotOk,
+    onChangePassword,
+  }: {
+    onLoadNotOk: <T>(
+      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+    ) => VNode;
+    onClear?: () => void;
+    onChangePassword: () => void;
+    onUpdateSuccess: () => void;
+    account: string;
+  }): VNode {
+    const { i18n } = useTranslationContext();
+    const result = useBusinessAccountDetails(account);
+    const { updateAccount } = useAdminAccountAPI();
+    const [update, setUpdate] = useState(false);
+    const [submitAccount, setSubmitAccount] = useState<
+      SandboxBackend.Circuit.CircuitAccountData | undefined
+    >();
+  
+    if (!result.ok) {
+      if (result.loading || result.type === ErrorType.TIMEOUT) {
+        return onLoadNotOk(result);
+      }
+      if (result.status === HttpStatusCode.NotFound) {
+        return <div>account not found</div>;
+      }
+      return onLoadNotOk(result);
+    }
+  
+    return (
+      <div>
+        <div>
+          <h1 class="nav welcome-text">
+            <i18n.Translate>Business account details</i18n.Translate>
+          </h1>
+        </div>
+        <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
+          <AccountForm
+            template={result.data}
+            purpose={update ? "update" : "show"}
+            onChange={(a) => setSubmitAccount(a)}
+          />
+  
+          <p class="buttons-account">
+            <div
+              style={{
+                display: "flex",
+                justifyContent: "space-between",
+                flexFlow: "wrap-reverse",
+              }}
+            >
+              <div>
+                {onClear ? (
+                  <input
+                    class="pure-button"
+                    type="submit"
+                    value={i18n.str`Close`}
+                    onClick={async (e) => {
+                      e.preventDefault();
+                      onClear();
+                    }}
+                  />
+                ) : undefined}
+              </div>
+              <div style={{ display: "flex" }}>
+                <div>
+                  <input
+                    id="select-exchange"
+                    class="pure-button pure-button-primary content"
+                    disabled={update && !submitAccount}
+                    type="submit"
+                    value={i18n.str`Change password`}
+                    onClick={async (e) => {
+                      e.preventDefault();
+                      onChangePassword();
+                    }}
+                  />
+                </div>
+                <div>
+                  <input
+                    id="select-exchange"
+                    class="pure-button pure-button-primary content"
+                    disabled={update && !submitAccount}
+                    type="submit"
+                    value={update ? i18n.str`Confirm` : i18n.str`Update`}
+                    onClick={async (e) => {
+                      e.preventDefault();
+  
+                      if (!update) {
+                        setUpdate(true);
+                      } else {
+                        if (!submitAccount) return;
+                        try {
+                          await updateAccount(account, {
+                            cashout_address: submitAccount.cashout_address,
+                            contact_data: submitAccount.contact_data,
+                          });
+                          onUpdateSuccess();
+                        } catch (error) {
+                          if (error instanceof RequestError) {
+                            notify(
+                              buildRequestErrorMessage(i18n, error.cause, {
+                                onClientError: (status) =>
+                                  status === HttpStatusCode.Forbidden
+                                    ? i18n.str`The rights to change the account are not sufficient`
+                                    : status === HttpStatusCode.NotFound
+                                      ? i18n.str`The username was not found`
+                                      : undefined,
+                              }),
+                            );
+                          } else {
+                            notifyError(
+                              i18n.str`Operation failed, please report`,
+                              (error instanceof Error
+                                ? error.message
+                                : JSON.stringify(error)) as TranslatedString
+                            )
+                          }
+                        }
+                      }
+                    }}
+                  />
+                </div>
+              </div>
+            </div>
+          </p>
+        </div>
+      </div>
+    );
+  }
+  
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
new file mode 100644
index 000000000..084a5b643
--- /dev/null
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -0,0 +1,131 @@
+import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useAdminAccountAPI, useBusinessAccountDetails } from "../hooks/circuit.js";
+import { useState } from "preact/hooks";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { VNode,h ,Fragment} from "preact";
+import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+
+export function UpdateAccountPassword({
+    account,
+    onClear,
+    onUpdateSuccess,
+    onLoadNotOk,
+  }: {
+    onLoadNotOk: <T>(
+      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+    ) => VNode;
+    onClear: () => void;
+    onUpdateSuccess: () => void;
+    account: string;
+  }): VNode {
+    const { i18n } = useTranslationContext();
+    const result = useBusinessAccountDetails(account);
+    const { changePassword } = useAdminAccountAPI();
+    const [password, setPassword] = useState<string | undefined>();
+    const [repeat, setRepeat] = useState<string | undefined>();
+  
+    if (!result.ok) {
+      if (result.loading || result.type === ErrorType.TIMEOUT) {
+        return onLoadNotOk(result);
+      }
+      if (result.status === HttpStatusCode.NotFound) {
+        return <div>account not found</div>;
+      }
+      return onLoadNotOk(result);
+    }
+  
+    const errors = undefinedIfEmpty({
+      password: !password ? i18n.str`required` : undefined,
+      repeat: !repeat
+        ? i18n.str`required`
+        : password !== repeat
+          ? i18n.str`password doesn't match`
+          : undefined,
+    });
+  
+    return (
+      <div>
+        <div>
+          <h1 class="nav welcome-text">
+            <i18n.Translate>Update password for {account}</i18n.Translate>
+          </h1>
+        </div>
+  
+        <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
+          <form class="pure-form">
+            <fieldset>
+              <label>{i18n.str`Password`}</label>
+              <input
+                type="password"
+                value={password ?? ""}
+                onChange={(e) => {
+                  setPassword(e.currentTarget.value);
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errors?.password}
+                isDirty={password !== undefined}
+              />
+            </fieldset>
+            <fieldset>
+              <label>{i18n.str`Repeat password`}</label>
+              <input
+                type="password"
+                value={repeat ?? ""}
+                onChange={(e) => {
+                  setRepeat(e.currentTarget.value);
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errors?.repeat}
+                isDirty={repeat !== undefined}
+              />
+            </fieldset>
+          </form>
+          <p>
+            <div style={{ display: "flex", justifyContent: "space-between" }}>
+              <div>
+                <input
+                  class="pure-button"
+                  type="submit"
+                  value={i18n.str`Close`}
+                  onClick={async (e) => {
+                    e.preventDefault();
+                    onClear();
+                  }}
+                />
+              </div>
+              <div>
+                <input
+                  id="select-exchange"
+                  class="pure-button pure-button-primary content"
+                  disabled={!!errors}
+                  type="submit"
+                  value={i18n.str`Confirm`}
+                  onClick={async (e) => {
+                    e.preventDefault();
+                    if (!!errors || !password) return;
+                    try {
+                      const r = await changePassword(account, {
+                        new_password: password,
+                      });
+                      onUpdateSuccess();
+                    } catch (error) {
+                      if (error instanceof RequestError) {
+                        notify(buildRequestErrorMessage(i18n, error.cause));
+                      } else {
+                        notifyError(i18n.str`Operation failed, please report`, (error instanceof Error
+                          ? error.message
+                          : JSON.stringify(error)) as TranslatedString)
+                      }
+                    }
+                  }}
+                />
+              </div>
+            </div>
+          </p>
+        </div>
+      </div>
+    );
+  }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index ced152feb..30fcbdff7 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -317,7 +317,8 @@ export function WithdrawalConfirmationQuestion({
                   </div>
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                     <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
-                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{Amounts.stringifyValue(details.amount)}</dd>
+                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">To be added</dd>
+                    {/* Amounts.stringifyValue(details.amount) */}
                   </div>
                 </dl>
               </div>
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index b48e3b1dc..2a3a1ec2c 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -100,10 +100,6 @@ export function WithdrawalQRCode({
   }
 
   if (data.confirmation_done) {
-    if (!settings.showWithdrawalSuccess) {
-      clearCurrentWithdrawal()
-      onContinue()
-    }
     return <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
       <div>
         <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
new file mode 100644
index 000000000..8ab3e1323
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -0,0 +1,56 @@
+import { Amounts } from "@gnu-taler/taler-util";
+import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js";
+import { handleNotOkResult } from "../HomePage.js";
+import { useAccountDetails } from "../../hooks/access.js";
+import { useBackendContext } from "../../context/backend.js";
+import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+
+export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode {
+    const { i18n } = useTranslationContext();
+    const r = useBackendContext();
+    const account = r.state.status === "loggedIn" ? r.state.username : "admin";
+    const result = useAccountDetails(account);
+  
+    if (!result.ok) {
+      return handleNotOkResult(i18n, onRegister)(result);
+    }
+    const { data } = result;
+    const balance = Amounts.parseOrThrow(data.balance.amount);
+    const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
+    const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+    const limit = balanceIsDebit
+      ? Amounts.sub(debitThreshold, balance).amount
+      : Amounts.add(balance, debitThreshold).amount;
+    if (!balance) return <Fragment />;
+    return (
+      <Fragment>
+        <section id="assets">
+          <div class="asset-summary">
+            <h2>{i18n.str`Bank account balance`}</h2>
+            {!balance ? (
+              <div class="large-amount" style={{ color: "gray" }}>
+                Waiting server response...
+              </div>
+            ) : (
+              <div class="large-amount amount">
+                {balanceIsDebit ? <b>-</b> : null}
+                <span class="value">{`${Amounts.stringifyValue(balance)}`}</span>
+                &nbsp;
+                <span class="currency">{`${balance.currency}`}</span>
+              </div>
+            )}
+          </div>
+        </section>
+        <PaytoWireTransferForm
+          focus
+          limit={limit}
+          onSuccess={() => {
+            notifyInfo(i18n.str`Wire transfer created!`);
+          }}
+          onCancel={undefined}
+        />
+      </Fragment>
+    );
+  }
+  
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
new file mode 100644
index 000000000..9ca0323a1
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -0,0 +1,219 @@
+import { VNode,h  } from "preact";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { PartialButDefined, RecursivePartial, WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js";
+import { useState } from "preact/hooks";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { parsePaytoUri } from "@gnu-taler/taler-util";
+
+const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
+const EMAIL_REGEX =
+  /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
+
+/**
+ * Create valid account object to update or create
+ * Take template as initial values for the form
+ * Purpose indicate if all field al read only (show), part of them (update)
+ * or none (create)
+ * @param param0
+ * @returns
+ */
+export function AccountForm({
+    template,
+    purpose,
+    onChange,
+  }: {
+    template: SandboxBackend.Circuit.CircuitAccountData | undefined;
+    onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
+    purpose: "create" | "update" | "show";
+  }): VNode {
+    const initial = initializeFromTemplate(template);
+    const [form, setForm] = useState(initial);
+    const [errors, setErrors] = useState<
+      RecursivePartial<typeof initial> | undefined
+    >(undefined);
+    const { i18n } = useTranslationContext();
+  
+    function updateForm(newForm: typeof initial): void {
+      const parsed = !newForm.cashout_address
+        ? undefined
+        : parsePaytoUri(newForm.cashout_address);
+  
+      const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
+        cashout_address: !newForm.cashout_address
+          ? i18n.str`required`
+          : !parsed
+            ? i18n.str`does not follow the pattern`
+            : !parsed.isKnown || parsed.targetType !== "iban"
+              ? i18n.str`only "IBAN" target are supported`
+              : !IBAN_REGEX.test(parsed.iban)
+                ? i18n.str`IBAN should have just uppercased letters and numbers`
+                : validateIBAN(parsed.iban, i18n),
+        contact_data: undefinedIfEmpty({
+          email: !newForm.contact_data?.email
+            ? i18n.str`required`
+            : !EMAIL_REGEX.test(newForm.contact_data.email)
+              ? i18n.str`it should be an email`
+              : undefined,
+          phone: !newForm.contact_data?.phone
+            ? i18n.str`required`
+            : !newForm.contact_data.phone.startsWith("+")
+              ? i18n.str`should start with +`
+              : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
+                ? i18n.str`phone number can't have other than numbers`
+                : undefined,
+        }),
+        iban: !newForm.iban
+          ? undefined //optional field
+          : !IBAN_REGEX.test(newForm.iban)
+            ? i18n.str`IBAN should have just uppercased letters and numbers`
+            : validateIBAN(newForm.iban, i18n),
+        name: !newForm.name ? i18n.str`required` : undefined,
+        username: !newForm.username ? i18n.str`required` : undefined,
+      });
+      setErrors(errors);
+      setForm(newForm);
+      onChange(errors === undefined ? (newForm as any) : undefined);
+    }
+  
+    return (
+      <form class="pure-form">
+        <fieldset>
+          <label for="username">
+            {i18n.str`Username`}
+            {purpose === "create" && <b style={{ color: "red" }}>*</b>}
+          </label>
+          <input
+            name="username"
+            type="text"
+            disabled={purpose !== "create"}
+            value={form.username}
+            onChange={(e) => {
+              form.username = e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />{" "}
+          <ShowInputErrorLabel
+            message={errors?.username}
+            isDirty={form.username !== undefined}
+          />
+        </fieldset>
+        <fieldset>
+          <label>
+            {i18n.str`Name`}
+            {purpose === "create" && <b style={{ color: "red" }}>*</b>}
+          </label>
+          <input
+            disabled={purpose !== "create"}
+            value={form.name ?? ""}
+            onChange={(e) => {
+              form.name = e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />
+          <ShowInputErrorLabel
+            message={errors?.name}
+            isDirty={form.name !== undefined}
+          />
+        </fieldset>
+        {purpose !== "create" && (
+          <fieldset>
+            <label>{i18n.str`Internal IBAN`}</label>
+            <input
+              disabled={true}
+              value={form.iban ?? ""}
+              onChange={(e) => {
+                form.iban = e.currentTarget.value;
+                updateForm(structuredClone(form));
+              }}
+            />
+            <ShowInputErrorLabel
+              message={errors?.iban}
+              isDirty={form.iban !== undefined}
+            />
+          </fieldset>
+        )}
+        <fieldset>
+          <label>
+            {i18n.str`Email`}
+            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+          </label>
+          <input
+            disabled={purpose === "show"}
+            value={form.contact_data.email ?? ""}
+            onChange={(e) => {
+              form.contact_data.email = e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />
+          <ShowInputErrorLabel
+            message={errors?.contact_data?.email}
+            isDirty={form.contact_data.email !== undefined}
+          />
+        </fieldset>
+        <fieldset>
+          <label>
+            {i18n.str`Phone`}
+            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+          </label>
+          <input
+            disabled={purpose === "show"}
+            value={form.contact_data.phone ?? ""}
+            onChange={(e) => {
+              form.contact_data.phone = e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />
+          <ShowInputErrorLabel
+            message={errors?.contact_data?.phone}
+            isDirty={form.contact_data?.phone !== undefined}
+          />
+        </fieldset>
+        <fieldset>
+          <label>
+            {i18n.str`Cashout address`}
+            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
+          </label>
+          <input
+            disabled={purpose === "show"}
+            value={(form.cashout_address ?? "").substring("payto://iban/".length)}
+            onChange={(e) => {
+              form.cashout_address = "payto://iban/" + e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />
+          <ShowInputErrorLabel
+            message={errors?.cashout_address}
+            isDirty={form.cashout_address !== undefined}
+          />
+        </fieldset>
+      </form>
+    );
+  }
+  
+  function initializeFromTemplate(
+    account: SandboxBackend.Circuit.CircuitAccountData | undefined,
+  ): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
+    const emptyAccount = {
+      cashout_address: undefined,
+      iban: undefined,
+      name: undefined,
+      username: undefined,
+      contact_data: undefined,
+    };
+    const emptyContact = {
+      email: undefined,
+      phone: undefined,
+    };
+  
+    const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
+      structuredClone(account) ?? emptyAccount;
+    if (typeof initial.contact_data === "undefined") {
+      initial.contact_data = emptyContact;
+    }
+    initial.contact_data.email;
+    return initial as any;
+  }
+  
+  
+  
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
new file mode 100644
index 000000000..56b15818b
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -0,0 +1,120 @@
+import { h, VNode } from "preact";
+import { useBusinessAccounts } from "../../hooks/circuit.js";
+import { handleNotOkResult } from "../HomePage.js";
+import { AccountAction } from "./Home.js";
+import { Amounts } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+
+interface Props {
+    onAction: (type: AccountAction, account: string) => void;
+    account: string | undefined;
+    onRegister: () => void;
+
+}
+
+export function AccountList({ account, onAction, onRegister }: Props): VNode {
+    const result = useBusinessAccounts({ account });
+    const { i18n } = useTranslationContext();
+
+    if (result.loading) return <div />;
+    if (!result.ok) {
+        return handleNotOkResult(i18n, onRegister)(result);
+    }
+
+    const { customers } = result.data;
+    return <section
+        id="main"
+        style={{ width: 600, marginLeft: "auto", marginRight: "auto" }}
+    >
+        {!customers.length ? (
+            <div></div>
+        ) : (
+            <article>
+                <h2>{i18n.str`Accounts:`}</h2>
+                <div class="results">
+                    <table class="pure-table pure-table-striped">
+                        <thead>
+                            <tr>
+                                <th>{i18n.str`Username`}</th>
+                                <th>{i18n.str`Name`}</th>
+                                <th>{i18n.str`Balance`}</th>
+                                <th>{i18n.str`Actions`}</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            {customers.map((item, idx) => {
+                                const balance = !item.balance
+                                    ? undefined
+                                    : Amounts.parse(item.balance.amount);
+                                const balanceIsDebit =
+                                    item.balance &&
+                                    item.balance.credit_debit_indicator == "debit";
+                                return (
+                                    <tr key={idx}>
+                                        <td>
+                                            <a
+                                                href="#"
+                                                onClick={(e) => {
+                                                    e.preventDefault();
+                                                    onAction("show-details", item.username)
+                                                }}
+                                            >
+                                                {item.username}
+                                            </a>
+                                        </td>
+                                        <td>{item.name}</td>
+                                        <td>
+                                            {!balance ? (
+                                                i18n.str`unknown`
+                                            ) : (
+                                                <span class="amount">
+                                                    {balanceIsDebit ? <b>-</b> : null}
+                                                    <span class="value">{`${Amounts.stringifyValue(
+                                                        balance,
+                                                    )}`}</span>
+                                                    &nbsp;
+                                                    <span class="currency">{`${balance.currency}`}</span>
+                                                </span>
+                                            )}
+                                        </td>
+                                        <td>
+                                            <a
+                                                href="#"
+                                                onClick={(e) => {
+                                                    e.preventDefault();
+                                                    onAction("update-password", item.username)
+                                                }}
+                                            >
+                                                change password
+                                            </a>
+                                            &nbsp;
+                                            <a
+                                                href="#"
+                                                onClick={(e) => {
+                                                    e.preventDefault();
+                                                    onAction("show-cashout", item.username)
+                                                }}
+                                            >
+                                                cashouts
+                                            </a>
+                                            &nbsp;
+                                            <a
+                                                href="#"
+                                                onClick={(e) => {
+                                                    e.preventDefault();
+                                                    onAction("remove-account", item.username)
+                                                }}
+                                            >
+                                                remove
+                                            </a>
+                                        </td>
+                                    </tr>
+                                );
+                            })}
+                        </tbody>
+                    </table>
+                </div>
+            </article>
+        )}
+    </section>
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
new file mode 100644
index 000000000..90835d52b
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -0,0 +1,107 @@
+import { RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h, Fragment } from "preact";
+import { useAdminAccountAPI } from "../../hooks/circuit.js";
+import { useState } from "preact/hooks";
+import { buildRequestErrorMessage } from "../../utils.js";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { getRandomPassword } from "../rnd.js";
+import { AccountForm } from "./AccountForm.js";
+
+export function CreateNewAccount({
+    onClose,
+    onCreateSuccess,
+}: {
+    onClose: () => void;
+    onCreateSuccess: (password: string) => void;
+}): VNode {
+    const { i18n } = useTranslationContext();
+    const { createAccount } = useAdminAccountAPI();
+    const [submitAccount, setSubmitAccount] = useState<
+        SandboxBackend.Circuit.CircuitAccountData | undefined
+    >();
+    return (
+        <div>
+            <div>
+                <h1 class="nav welcome-text">
+                    <i18n.Translate>New account</i18n.Translate>
+                </h1>
+            </div>
+
+            <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
+                <AccountForm
+                    template={undefined}
+                    purpose="create"
+                    onChange={(a) => {
+                        setSubmitAccount(a);
+                    }}
+                />
+
+                <p>
+                    <div style={{ display: "flex", justifyContent: "space-between" }}>
+                        <div>
+                            <input
+                                class="pure-button"
+                                type="submit"
+                                value={i18n.str`Close`}
+                                onClick={async (e) => {
+                                    e.preventDefault();
+                                    onClose();
+                                }}
+                            />
+                        </div>
+                        <div>
+                            <input
+                                id="select-exchange"
+                                class="pure-button pure-button-primary content"
+                                disabled={!submitAccount}
+                                type="submit"
+                                value={i18n.str`Confirm`}
+                                onClick={async (e) => {
+                                    e.preventDefault();
+
+                                    if (!submitAccount) return;
+                                    try {
+                                        const account: SandboxBackend.Circuit.CircuitAccountRequest =
+                                        {
+                                            cashout_address: submitAccount.cashout_address,
+                                            contact_data: submitAccount.contact_data,
+                                            internal_iban: submitAccount.iban,
+                                            name: submitAccount.name,
+                                            username: submitAccount.username,
+                                            password: getRandomPassword(),
+                                        };
+
+                                        await createAccount(account);
+                                        onCreateSuccess(account.password);
+                                    } catch (error) {
+                                        if (error instanceof RequestError) {
+                                            notify(
+                                                buildRequestErrorMessage(i18n, error.cause, {
+                                                    onClientError: (status) =>
+                                                        status === HttpStatusCode.Forbidden
+                                                            ? i18n.str`The rights to perform the operation are not sufficient`
+                                                            : status === HttpStatusCode.BadRequest
+                                                                ? i18n.str`Input data was invalid`
+                                                                : status === HttpStatusCode.Conflict
+                                                                    ? i18n.str`At least one registration detail was not available`
+                                                                    : undefined,
+                                                }),
+                                            );
+                                        } else {
+                                            notifyError(
+                                                i18n.str`Operation failed, please report`,
+                                                (error instanceof Error
+                                                    ? error.message
+                                                    : JSON.stringify(error)) as TranslatedString
+                                            )
+                                        }
+                                    }
+                                }}
+                            />
+                        </div>
+                    </div>
+                </p>
+            </div>
+        </div>
+    );
+}
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
new file mode 100644
index 000000000..e1ec6cfe0
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -0,0 +1,162 @@
+import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { Cashouts } from "../../components/Cashouts/index.js";
+import { ShowCashoutDetails } from "../business/Home.js";
+import { handleNotOkResult } from "../HomePage.js";
+import { ShowAccountDetails } from "../ShowAccountDetails.js";
+import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
+import { AdminAccount } from "./Account.js";
+import { AccountList } from "./AccountList.js";
+import { CreateNewAccount } from "./CreateNewAccount.js";
+import { RemoveAccount } from "./RemoveAccount.js";
+
+/**
+ * Query account information and show QR code if there is pending withdrawal
+ */
+interface Props {
+  onRegister: () => void;
+}
+export type AccountAction = "show-details" | 
+  "show-cashout" | 
+  "update-password" | 
+  "remove-account" | 
+  "show-cashouts-details";
+
+export function AdminHome({ onRegister }: Props): VNode {
+  const [action, setAction] = useState<{
+    type: AccountAction,
+    account: string
+  }>()
+
+  const [createAccount, setCreateAccount] = useState(false);
+
+  const { i18n } = useTranslationContext();
+
+  if (action) {
+    switch (action.type) {
+      case "show-details": return <ShowCashoutDetails
+        id={action.account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onCancel={() => {
+          setAction(undefined);
+        }}
+      />
+      case "show-cashout": return (
+        <div>
+          <div>
+            <h1 class="nav welcome-text">
+              <i18n.Translate>Cashout for account {action.account}</i18n.Translate>
+            </h1>
+          </div>
+          <Cashouts
+            account={action.account}
+            onSelected={(id) => {
+              setAction({
+                type: "show-cashouts-details",
+                account: action.account
+              });
+            }}
+          />
+          <p>
+            <input
+              class="pure-button"
+              type="submit"
+              value={i18n.str`Close`}
+              onClick={async (e) => {
+                e.preventDefault();
+                setAction(undefined);
+              }}
+            />
+          </p>
+        </div>
+      )
+      case "update-password": return <UpdateAccountPassword
+        account={action.account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onUpdateSuccess={() => {
+          notifyInfo(i18n.str`Password changed`);
+          setAction(undefined);
+        }}
+        onClear={() => {
+          setAction(undefined);
+        }}
+      />
+      case "remove-account": return <RemoveAccount
+        account={action.account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onUpdateSuccess={() => {
+          notifyInfo(i18n.str`Account removed`);
+          setAction(undefined);
+        }}
+        onClear={() => {
+          setAction(undefined);
+        }}
+      />
+      case "show-cashouts-details": return <ShowAccountDetails
+        account={action.account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onChangePassword={() => {
+          setAction({
+            type: "update-password",
+            account: action.account,
+          })
+        }}
+        onUpdateSuccess={() => {
+          notifyInfo(i18n.str`Account updated`);
+          setAction(undefined);
+        }}
+        onClear={() => {
+          setAction(undefined);
+        }}
+      />
+    }
+  }
+
+  if (createAccount) {
+    return (
+      <CreateNewAccount
+        onClose={() => setCreateAccount(false)}
+        onCreateSuccess={(password) => {
+          notifyInfo(
+            i18n.str`Account created with password "${password}". The user must change the password on the next login.`,
+          );
+          setCreateAccount(false);
+        }}
+      />
+    );
+  }
+
+  return (
+    <Fragment>
+      <div>
+        <h1 class="nav welcome-text">
+          <i18n.Translate>Admin panel</i18n.Translate>
+        </h1>
+      </div>
+
+      <p>
+        <div style={{ display: "flex", justifyContent: "space-between" }}>
+          <div></div>
+          <div>
+            <input
+              class="pure-button pure-button-primary content"
+              type="submit"
+              value={i18n.str`Create account`}
+              onClick={async (e) => {
+                e.preventDefault();
+
+                setCreateAccount(true);
+              }}
+            />
+          </div>
+        </div>
+      </p>
+
+      <AdminAccount onRegister={onRegister} />
+
+      <AccountList account={undefined} onAction={(type,account) => setAction({account, type})} onRegister={onRegister}/>
+
+    </Fragment>
+  );
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
new file mode 100644
index 000000000..2900db9d2
--- /dev/null
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -0,0 +1,112 @@
+import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode,h,Fragment } from "preact";
+import { useAccountDetails } from "../../hooks/access.js";
+import { useAdminAccountAPI } from "../../hooks/circuit.js";
+import { Amounts, HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { buildRequestErrorMessage } from "../../utils.js";
+
+export function RemoveAccount({
+    account,
+    onClear,
+    onUpdateSuccess,
+    onLoadNotOk,
+  }: {
+    onLoadNotOk: <T>(
+      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+    ) => VNode;
+    onClear: () => void;
+    onUpdateSuccess: () => void;
+    account: string;
+  }): VNode {
+    const { i18n } = useTranslationContext();
+    const result = useAccountDetails(account);
+    const { deleteAccount } = useAdminAccountAPI();
+  
+    if (!result.ok) {
+      if (result.loading || result.type === ErrorType.TIMEOUT) {
+        return onLoadNotOk(result);
+      }
+      if (result.status === HttpStatusCode.NotFound) {
+        return <div>account not found</div>;
+      }
+      return onLoadNotOk(result);
+    }
+  
+    const balance = Amounts.parse(result.data.balance.amount);
+    if (!balance) {
+      return <div>there was an error reading the balance</div>;
+    }
+    const isBalanceEmpty = Amounts.isZero(balance);
+    return (
+      <div>
+        <div>
+          <h1 class="nav welcome-text">
+            <i18n.Translate>Remove account: {account}</i18n.Translate>
+          </h1>
+        </div>
+        {/* {FXME: SHOW WARNING} */}
+        {/* {!isBalanceEmpty && (
+          <ErrorBannerFloat
+            error={{
+              title: i18n.str`Can't delete the account`,
+              description: i18n.str`Balance is not empty`,
+            }}
+            onClear={() => saveError(undefined)}
+          />
+        )} */}
+  
+        <p>
+          <div style={{ display: "flex", justifyContent: "space-between" }}>
+            <div>
+              <input
+                class="pure-button"
+                type="submit"
+                value={i18n.str`Cancel`}
+                onClick={async (e) => {
+                  e.preventDefault();
+                  onClear();
+                }}
+              />
+            </div>
+            <div>
+              <input
+                id="select-exchange"
+                class="pure-button pure-button-primary content"
+                disabled={!isBalanceEmpty}
+                type="submit"
+                value={i18n.str`Confirm`}
+                onClick={async (e) => {
+                  e.preventDefault();
+                  try {
+                    const r = await deleteAccount(account);
+                    onUpdateSuccess();
+                  } catch (error) {
+                    if (error instanceof RequestError) {
+                      notify(
+                        buildRequestErrorMessage(i18n, error.cause, {
+                          onClientError: (status) =>
+                            status === HttpStatusCode.Forbidden
+                              ? i18n.str`The administrator specified a institutional username`
+                              : status === HttpStatusCode.NotFound
+                                ? i18n.str`The username was not found`
+                                : status === HttpStatusCode.PreconditionFailed
+                                  ? i18n.str`Balance was not zero`
+                                  : undefined,
+                        }),
+                      );
+                    } else {
+                      notifyError(i18n.str`Operation failed, please report`,
+                        (error instanceof Error
+                            ? error.message
+                            : JSON.stringify(error)) as TranslatedString);
+                    }
+                  }
+                }}
+              />
+            </div>
+          </div>
+        </p>
+      </div>
+    );
+  }
+  
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
new file mode 100644
index 000000000..8beea640a
--- /dev/null
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -0,0 +1,757 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+  AmountJson,
+  Amounts,
+  HttpStatusCode,
+  TranslatedString
+} from "@gnu-taler/taler-util";
+import {
+  HttpResponse,
+  HttpResponsePaginated,
+  RequestError,
+  notify,
+  notifyError,
+  notifyInfo,
+  useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { Cashouts } from "../../components/Cashouts/index.js";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { useBackendContext } from "../../context/backend.js";
+import { useAccountDetails } from "../../hooks/access.js";
+import {
+  useCashoutDetails,
+  useCircuitAccountAPI,
+  useEstimator,
+  useRatiosAndFeeConfig,
+} from "../../hooks/circuit.js";
+import {
+  TanChannel,
+  buildRequestErrorMessage,
+  undefinedIfEmpty,
+} from "../../utils.js";
+import { handleNotOkResult } from "../HomePage.js";
+import { LoginForm } from "../LoginForm.js";
+import { Amount } from "../PaytoWireTransferForm.js";
+import { ShowAccountDetails } from "../ShowAccountDetails.js";
+import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
+
+interface Props {
+  account: string,
+  onClose: () => void;
+  onRegister: () => void;
+  onLoadNotOk: () => void;
+}
+export function BusinessAccount({
+  onClose,
+  account,
+  onLoadNotOk,
+  onRegister,
+}: Props): VNode {
+  const { i18n } = useTranslationContext();
+  const [updatePassword, setUpdatePassword] = useState(false);
+  const [newCashout, setNewcashout] = useState(false);
+  const [showCashoutDetails, setShowCashoutDetails] = useState<
+    string | undefined
+  >();
+
+
+  if (newCashout) {
+    return (
+      <CreateCashout
+        account={account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onCancel={() => {
+          setNewcashout(false);
+        }}
+        onComplete={(id) => {
+          notifyInfo(
+            i18n.str`Cashout created. You need to confirm the operation to complete the transaction.`,
+          );
+          setNewcashout(false);
+          setShowCashoutDetails(id);
+        }}
+      />
+    );
+  }
+  if (showCashoutDetails) {
+    return (
+      <ShowCashoutDetails
+        id={showCashoutDetails}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onCancel={() => {
+          setShowCashoutDetails(undefined);
+        }}
+      />
+    );
+  }
+  if (updatePassword) {
+    return (
+      <UpdateAccountPassword
+        account={account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onUpdateSuccess={() => {
+          notifyInfo(i18n.str`Password changed`);
+          setUpdatePassword(false);
+        }}
+        onClear={() => {
+          setUpdatePassword(false);
+        }}
+      />
+    );
+  }
+  return (
+    <div>
+      <ShowAccountDetails
+        account={account}
+        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onUpdateSuccess={() => {
+          notifyInfo(i18n.str`Account updated`);
+        }}
+        onChangePassword={() => {
+          setUpdatePassword(true);
+        }}
+        onClear={onClose}
+      />
+      <section style={{ marginTop: "2em" }}>
+        <div class="active">
+          <h3>{i18n.str`Latest cashouts`}</h3>
+          <Cashouts
+            account={account}
+            onSelected={(id) => {
+              setShowCashoutDetails(id);
+            }}
+          />
+        </div>
+        <br />
+        <div style={{ display: "flex", justifyContent: "space-between" }}>
+          <div />
+          <input
+            class="pure-button pure-button-primary content"
+            type="submit"
+            value={i18n.str`New cashout`}
+            onClick={async (e) => {
+              e.preventDefault();
+              setNewcashout(true);
+            }}
+          />
+        </div>
+      </section>
+    </div>
+  );
+}
+
+interface PropsCashout {
+  account: string;
+  onComplete: (id: string) => void;
+  onCancel: () => void;
+  onLoadNotOk: <T>(
+    error:
+      | HttpResponsePaginated<T, SandboxBackend.SandboxError>
+      | HttpResponse<T, SandboxBackend.SandboxError>,
+  ) => VNode;
+}
+
+type FormType = {
+  isDebit: boolean;
+  amount: string;
+  subject: string;
+  channel: TanChannel;
+};
+type ErrorFrom<T> = {
+  [P in keyof T]+?: string;
+};
+
+// check #7719
+function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
+  SandboxBackend.Circuit.Config & { hasChanged?: boolean },
+  SandboxBackend.SandboxError
+> {
+  const result = useRatiosAndFeeConfig();
+  const [oldResult, setOldResult] = useState<
+    SandboxBackend.Circuit.Config | undefined
+  >(undefined);
+  const dataFromBackend = result.ok ? result.data : undefined;
+  useEffect(() => {
+    // save only the first result of /config to the backend
+    if (!dataFromBackend || oldResult !== undefined) return;
+    setOldResult(dataFromBackend);
+  }, [dataFromBackend]);
+
+  if (!result.ok) return result;
+
+  const data = !oldResult ? result.data : oldResult;
+  const hasChanged =
+    oldResult &&
+    (result.data.name !== oldResult.name ||
+      result.data.version !== oldResult.version ||
+      result.data.ratios_and_fees.buy_at_ratio !==
+        oldResult.ratios_and_fees.buy_at_ratio ||
+      result.data.ratios_and_fees.buy_in_fee !==
+        oldResult.ratios_and_fees.buy_in_fee ||
+      result.data.ratios_and_fees.sell_at_ratio !==
+        oldResult.ratios_and_fees.sell_at_ratio ||
+      result.data.ratios_and_fees.sell_out_fee !==
+        oldResult.ratios_and_fees.sell_out_fee ||
+      result.data.fiat_currency !== oldResult.fiat_currency);
+
+  return {
+    ...result,
+    data: { ...data, hasChanged },
+  };
+}
+
+function CreateCashout({
+  account,
+  onComplete,
+  onCancel,
+  onLoadNotOk,
+}: PropsCashout): VNode {
+  const { i18n } = useTranslationContext();
+  const ratiosResult = useRatiosAndFeeConfig();
+  const result = useAccountDetails(account);
+  const {
+    estimateByCredit: calculateFromCredit,
+    estimateByDebit: calculateFromDebit,
+  } = useEstimator();
+  const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
+
+  const { createCashout } = useCircuitAccountAPI();
+  if (!result.ok) return onLoadNotOk(result);
+  if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
+  const config = ratiosResult.data;
+
+  const balance = Amounts.parseOrThrow(result.data.balance.amount);
+  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
+  const zero = Amounts.zeroOfCurrency(balance.currency);
+  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+  const limit = balanceIsDebit
+    ? Amounts.sub(debitThreshold, balance).amount
+    : Amounts.add(balance, debitThreshold).amount;
+
+  const zeroCalc = { debit: zero, credit: zero, beforeFee: zero };
+  const [calc, setCalc] = useState(zeroCalc);
+  const sellRate = config.ratios_and_fees.sell_at_ratio;
+  const sellFee = !config.ratios_and_fees.sell_out_fee
+    ? zero
+    : Amounts.parseOrThrow(
+        `${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
+      );
+  const fiatCurrency = config.fiat_currency;
+
+  if (!sellRate || sellRate < 0) return <div>error rate</div>;
+
+  const amount = Amounts.parseOrThrow(
+    `${!form.isDebit ? fiatCurrency : balance.currency}:${
+      !form.amount ? "0" : form.amount
+    }`,
+  );
+
+  useEffect(() => {
+    if (form.isDebit) {
+      calculateFromDebit(amount, sellFee, sellRate)
+        .then((r) => {
+          setCalc(r);
+        })
+        .catch((error) => {
+          notify(
+            error instanceof RequestError
+              ? buildRequestErrorMessage(i18n, error.cause)
+              : {
+                  type: "error",
+                  title: i18n.str`Could not estimate the cashout`,
+                  description: error.message as TranslatedString
+                },
+          );
+        });
+    } else {
+      calculateFromCredit(amount, sellFee, sellRate)
+        .then((r) => {
+          setCalc(r);
+        })
+        .catch((error) => {
+          notify(
+            error instanceof RequestError
+              ? buildRequestErrorMessage(i18n, error.cause)
+              : {
+                  type: "error",
+                  title: i18n.str`Could not estimate the cashout`,
+                  description: error.message,
+                },
+          );
+        });
+    }
+  }, [form.amount, form.isDebit]);
+
+  const balanceAfter = Amounts.sub(balance, calc.debit).amount;
+
+  function updateForm(newForm: typeof form): void {
+    setForm(newForm);
+  }
+  const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
+    amount: !form.amount
+      ? i18n.str`required`
+      : !amount
+      ? i18n.str`could not be parsed`
+      : Amounts.cmp(limit, calc.debit) === -1
+      ? i18n.str`balance is not enough`
+      : Amounts.cmp(calc.beforeFee, sellFee) === -1
+      ? i18n.str`the total amount to transfer does not cover the fees`
+      : Amounts.isZero(calc.credit)
+      ? i18n.str`the total transfer at destination will be zero`
+      : undefined,
+    channel: !form.channel ? i18n.str`required` : undefined,
+  });
+
+  return (
+    <div>
+      <h1>New cashout</h1>
+      <form class="pure-form">
+        <fieldset>
+          <label>{i18n.str`Subject`}</label>
+          <input
+            value={form.subject ?? ""}
+            onChange={(e) => {
+              form.subject = e.currentTarget.value;
+              updateForm(structuredClone(form));
+            }}
+          />
+          <ShowInputErrorLabel
+            message={errors?.subject}
+            isDirty={form.subject !== undefined}
+          />
+        </fieldset>
+        <fieldset>
+          <label for="amount">
+            {form.isDebit
+              ? i18n.str`Amount to send`
+              : i18n.str`Amount to receive`}
+            
+          </label>
+          <div style={{ display: "flex" }}>
+            <Amount
+              name="amount"
+              currency={amount.currency}
+              value={form.amount}
+              onChange={(v) => {
+                form.amount = v;
+                updateForm(structuredClone(form));
+              }}
+              error={errors?.amount}
+            />
+            <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
+              <input
+                class="toggle-checkbox"
+                type="checkbox"
+                name="asd"
+                onChange={(e): void => {
+                  console.log("asdasd", form.isDebit);
+                  form.isDebit = !form.isDebit;
+                  updateForm(structuredClone(form));
+                }}
+              />
+              <div class="toggle-switch"></div>
+            </label>
+          </div>
+        </fieldset>
+        <fieldset>
+          <label>{i18n.str`Conversion rate`}</label>
+          <input value={sellRate} disabled />
+        </fieldset>
+        <fieldset>
+          <label for="balance-now">{i18n.str`Balance now`}</label>
+          <Amount
+            name="banace-now"
+            currency={balance.currency}
+            value={Amounts.stringifyValue(balance)}
+          />
+        </fieldset>
+        <fieldset>
+          <label for="total-cost"
+            style={{ fontWeight: "bold", color: "red" }}
+          >{i18n.str`Total cost`}</label>
+          <Amount
+            name="total-cost"
+            currency={balance.currency}
+            value={Amounts.stringifyValue(calc.debit)}
+          />
+        </fieldset>
+        <fieldset>
+          <label for="balance-after">{i18n.str`Balance after`}</label>
+          <Amount
+            name="balance-after"
+            currency={balance.currency}
+            value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
+          />
+        </fieldset>{" "}
+        {Amounts.isZero(sellFee) ? undefined : (
+          <Fragment>
+            <fieldset>
+              <label for="amount-conversiojn">{i18n.str`Amount after conversion`}</label>
+              <Amount
+                name="amount-conversion"
+                currency={fiatCurrency}
+                value={Amounts.stringifyValue(calc.beforeFee)}
+              />
+            </fieldset>
+
+            <fieldset>
+              <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
+              <Amount
+                name="cashout-fee"
+                currency={fiatCurrency}
+                value={Amounts.stringifyValue(sellFee)}
+              />
+            </fieldset>
+          </Fragment>
+        )}
+        <fieldset>
+          <label for="total"
+            style={{ fontWeight: "bold", color: "green" }}
+          >{i18n.str`Total cashout transfer`}</label>
+          <Amount
+            name="total"
+            currency={fiatCurrency}
+            value={Amounts.stringifyValue(calc.credit)}
+          />
+        </fieldset>
+        <fieldset>
+          <label>{i18n.str`Confirmation channel`}</label>
+
+          <div class="channel">
+            <input
+              class={
+                "pure-button content " +
+                (form.channel === TanChannel.EMAIL
+                  ? "pure-button-primary"
+                  : "pure-button-secondary")
+              }
+              type="submit"
+              value={i18n.str`Email`}
+              onClick={async (e) => {
+                e.preventDefault();
+                form.channel = TanChannel.EMAIL;
+                updateForm(structuredClone(form));
+              }}
+            />
+            <input
+              class={
+                "pure-button content " +
+                (form.channel === TanChannel.SMS
+                  ? "pure-button-primary"
+                  : "pure-button-secondary")
+              }
+              type="submit"
+              value={i18n.str`SMS`}
+              onClick={async (e) => {
+                e.preventDefault();
+                form.channel = TanChannel.SMS;
+                updateForm(structuredClone(form));
+              }}
+            />
+            <input
+              class={
+                "pure-button content " +
+                (form.channel === TanChannel.FILE
+                  ? "pure-button-primary"
+                  : "pure-button-secondary")
+              }
+              type="submit"
+              value={i18n.str`FILE`}
+              onClick={async (e) => {
+                e.preventDefault();
+                form.channel = TanChannel.FILE;
+                updateForm(structuredClone(form));
+              }}
+            />
+          </div>
+          <ShowInputErrorLabel
+            message={errors?.channel}
+            isDirty={form.channel !== undefined}
+          />
+        </fieldset>
+        <br />
+        <div style={{ display: "flex", justifyContent: "space-between" }}>
+          <button
+            class="pure-button pure-button-secondary btn-cancel"
+            onClick={(e) => {
+              e.preventDefault();
+              onCancel();
+            }}
+          >
+            {i18n.str`Cancel`}
+          </button>
+
+          <button
+            class="pure-button pure-button-primary btn-register"
+            type="submit"
+            disabled={!!errors}
+            onClick={async (e) => {
+              e.preventDefault();
+
+              if (errors) return;
+              try {
+                const res = await createCashout({
+                  amount_credit: Amounts.stringify(calc.credit),
+                  amount_debit: Amounts.stringify(calc.debit),
+                  subject: form.subject,
+                  tan_channel: form.channel,
+                });
+                onComplete(res.data.uuid);
+              } catch (error) {
+                if (error instanceof RequestError) {
+                  notify(
+                    buildRequestErrorMessage(i18n, error.cause, {
+                      onClientError: (status) =>
+                        status === HttpStatusCode.BadRequest
+                          ? i18n.str`The exchange rate was incorrectly applied`
+                          : status === HttpStatusCode.Forbidden
+                          ? i18n.str`A institutional user tried the operation`
+                          : status === HttpStatusCode.Conflict
+                          ? i18n.str`Need a contact data where to send the TAN`
+                          : status === HttpStatusCode.PreconditionFailed
+                          ? i18n.str`The account does not have sufficient funds`
+                          : undefined,
+                      onServerError: (status) =>
+                        status === HttpStatusCode.ServiceUnavailable
+                          ? i18n.str`The bank does not support the TAN channel for this operation`
+                          : undefined,
+                    }),
+                  );
+                } else {
+                  notifyError(
+                    i18n.str`Operation failed, please report`,
+                    (error instanceof Error
+                      ? error.message
+                      : JSON.stringify(error)) as TranslatedString
+                  )
+            }
+              }
+            }}
+          >
+            {i18n.str`Create`}
+          </button>
+        </div>
+      </form>
+    </div>
+  );
+}
+
+interface ShowCashoutProps {
+  id: string;
+  onCancel: () => void;
+  onLoadNotOk: <T>(
+    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+  ) => VNode;
+}
+export function ShowCashoutDetails({
+  id,
+  onCancel,
+  onLoadNotOk,
+}: ShowCashoutProps): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useCashoutDetails(id);
+  const { abortCashout, confirmCashout } = useCircuitAccountAPI();
+  const [code, setCode] = useState<string | undefined>(undefined);
+  if (!result.ok) return onLoadNotOk(result);
+  const errors = undefinedIfEmpty({
+    code: !code ? i18n.str`required` : undefined,
+  });
+  const isPending = String(result.data.status).toUpperCase() === "PENDING";
+  return (
+    <div>
+      <h1>Cashout details {id}</h1>
+      <form class="pure-form">
+        <fieldset>
+          <label>
+            <i18n.Translate>Subject</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.subject} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Created</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.creation_time ?? ""} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Confirmed</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.confirmation_time ?? ""} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Debited</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.amount_debit} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Credit</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.amount_credit} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Status</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.status} />
+        </fieldset>
+        <fieldset>
+          <label>
+            <i18n.Translate>Destination</i18n.Translate>
+          </label>
+          <input readOnly value={result.data.cashout_address} />
+        </fieldset>
+        {isPending ? (
+          <fieldset>
+            <label>
+              <i18n.Translate>Code</i18n.Translate>
+            </label>
+            <input
+              value={code ?? ""}
+              onChange={(e) => {
+                setCode(e.currentTarget.value);
+              }}
+            />
+            <ShowInputErrorLabel
+              message={errors?.code}
+              isDirty={code !== undefined}
+            />
+          </fieldset>
+        ) : undefined}
+      </form>
+      <br />
+      <div style={{ display: "flex", justifyContent: "space-between" }}>
+        <button
+          class="pure-button pure-button-secondary btn-cancel"
+          onClick={(e) => {
+            e.preventDefault();
+            onCancel();
+          }}
+        >
+          {i18n.str`Back`}
+        </button>
+        {isPending ? (
+          <div>
+            <button
+              type="submit"
+              class="pure-button pure-button-primary button-error"
+              onClick={async (e) => {
+                e.preventDefault();
+                try {
+                  await abortCashout(id);
+                  onCancel();
+                } catch (error) {
+                  if (error instanceof RequestError) {
+                    notify(
+                      buildRequestErrorMessage(i18n, error.cause, {
+                        onClientError: (status) =>
+                          status === HttpStatusCode.NotFound
+                            ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
+                            : status === HttpStatusCode.PreconditionFailed
+                            ? i18n.str`Cashout was already confimed`
+                            : undefined,
+                      }),
+                    );
+                  } else {
+                    notifyError(
+                      i18n.str`Operation failed, please report`,
+                      (error instanceof Error
+                        ? error.message
+                        : JSON.stringify(error)) as TranslatedString
+                    )
+                }
+                }
+              }}
+            >
+              {i18n.str`Abort`}
+            </button>
+            &nbsp;
+            <button
+              type="submit"
+              disabled={!code}
+              class="pure-button pure-button-primary "
+              onClick={async (e) => {
+                e.preventDefault();
+                try {
+                  if (!code) return;
+                  const rest = await confirmCashout(id, {
+                    tan: code,
+                  });
+                } catch (error) {
+                  if (error instanceof RequestError) {
+                    notify(
+                      buildRequestErrorMessage(i18n, error.cause, {
+                        onClientError: (status) =>
+                          status === HttpStatusCode.NotFound
+                            ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
+                            : status === HttpStatusCode.PreconditionFailed
+                            ? i18n.str`Cashout was already confimed`
+                            : status === HttpStatusCode.Conflict
+                            ? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
+                            : status === HttpStatusCode.Forbidden
+                            ? i18n.str`Invalid code`
+                            : undefined,
+                      }),
+                    );
+                  } else {
+                    notifyError(
+                      i18n.str`Operation failed, please report`,
+                      (error instanceof Error
+                        ? error.message
+                        : JSON.stringify(error)) as TranslatedString
+                    )
+                }
+                }
+              }}
+            >
+              {i18n.str`Confirm`}
+            </button>
+          </div>
+        ) : (
+          <div />
+        )}
+      </div>
+    </div>
+  );
+}
+
+const MAX_AMOUNT_DIGIT = 2;
+/**
+ * Truncate the amount of digits to display
+ * in the form based on the fee calculations
+ *
+ * Backend must have the same truncation
+ * @param a
+ * @returns
+ */
+function truncate(a: AmountJson): AmountJson {
+  const str = Amounts.stringify(a);
+  const idx = str.indexOf(".");
+  if (idx === -1) {
+    return a;
+  }
+  const truncated = str.substring(0, idx + 1 + MAX_AMOUNT_DIGIT);
+  return Amounts.parseOrThrow(truncated);
+}
+
+export function assertUnreachable(x: never): never {
+  throw new Error("Didn't expect to get here");
+}
-- 
cgit v1.2.3


From 0b7bbed99d155ba030a1328e357ab6751bdbb835 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Thu, 21 Sep 2023 13:10:16 -0300
Subject: more ui: business and admin

---
 packages/demobank-ui/src/components/Routing.tsx    |  17 +-
 .../src/components/ShowInputErrorLabel.tsx         |   4 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     |   5 +-
 .../demobank-ui/src/pages/AccountPage/state.ts     |   7 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |  29 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  32 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |   6 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  | 131 +++---
 .../src/pages/PaytoWireTransferForm.tsx            |   6 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |   2 +-
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   | 278 ++++++------
 .../src/pages/UpdateAccountPassword.tsx            | 278 +++++++-----
 packages/demobank-ui/src/pages/admin/Account.tsx   |  72 ++-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    | 494 +++++++++++++--------
 .../demobank-ui/src/pages/admin/AccountList.tsx    | 182 +++++---
 .../src/pages/admin/CreateNewAccount.tsx           | 174 ++++----
 packages/demobank-ui/src/pages/admin/Home.tsx      |  51 +--
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  | 302 +++++++++----
 packages/demobank-ui/src/pages/business/Home.tsx   |   2 +-
 19 files changed, 1181 insertions(+), 891 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index ef11af76e..b8e39948b 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -33,12 +33,7 @@ export function Routing(): VNode {
   const backend = useBackendContext();
 
   if (backend.state.status === "loggedOut") {
-    return <BankFrame
-      account={undefined}
-      goToBusinessAccount={() => {
-        route("/business");
-      }}
-    >
+    return <BankFrame >
       <Router history={history}>
         <Route
           path="/login"
@@ -67,12 +62,7 @@ export function Routing(): VNode {
   const { isUserAdministrator, username } = backend.state
 
   return (
-    <BankFrame
-      account={backend.state.username}
-      goToBusinessAccount={() => {
-        route("/business");
-      }}
-    >
+    <BankFrame account={backend.state.username}>
       <Router history={history}>
         <Route
           path="/test"
@@ -121,6 +111,9 @@ export function Routing(): VNode {
                 onPendingOperationFound={(wopid) => {
                   route(`/operation/${wopid}`);
                 }}
+                goToBusinessAccount={() => {
+                  route("/business");
+                }}
                 onRegister={() => {
                   route("/register");
                 }}
diff --git a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx b/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
index dacffe20a..c5840cad9 100644
--- a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
+++ b/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
@@ -24,6 +24,6 @@ export function ShowInputErrorLabel({
   isDirty: boolean;
 }): VNode {
   if (message && isDirty)
-    return <div style={{ marginTop: 8, color: "red" }}>{message}</div>;
-  return <Fragment />;
+    return <div class="text-base" style={{ color: "red" }}>{message}</div>;
+  return <div class="text-base" style={{ }}> </div>;
 }
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index ed6945f84..128a6d30f 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -28,6 +28,7 @@ export interface Props {
   onLoadNotOk: <T>(
     error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
   ) => VNode;
+  goToBusinessAccount: () => void;
 }
 
 export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
@@ -51,10 +52,8 @@ export namespace State {
     status: "ready";
     error: undefined;
     account: string, 
-    payto: PaytoUriIBAN | PaytoUriTalerBank, 
-    balance: AmountJson, 
-    balanceIsDebit: boolean, 
     limit: AmountJson,
+    goToBusinessAccount: () => void;
   }
 
   export interface InvalidIban {
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index 2249b743e..a57e19901 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -20,7 +20,7 @@ import { useBackendContext } from "../../context/backend.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { Props, State } from "./index.js";
 
-export function useComponentState({ account, onLoadNotOk }: Props): State {
+export function useComponentState({ account, goToBusinessAccount }: Props): State {
   const result = useAccountDetails(account);
   const backend = useBackendContext();
   const { i18n } = useTranslationContext();
@@ -60,7 +60,6 @@ export function useComponentState({ account, onLoadNotOk }: Props): State {
   const payto = parsePaytoUri(data.paytoUri);
 
   if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
-    console.log(payto)
     return {
       status: "invalid-iban",
       error: result
@@ -75,11 +74,9 @@ export function useComponentState({ account, onLoadNotOk }: Props): State {
 
   return {
     status: "ready",
+    goToBusinessAccount,
     error: undefined,
     account,
-    balance,
-    balanceIsDebit,
     limit,
-    payto
   };
 }
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index f2cbbba6c..abd14848f 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -22,6 +22,7 @@ import { PaymentOptions } from "../PaymentOptions.js";
 import { State } from "./index.js";
 import { CopyButton } from "../../components/CopyButton.js";
 import { bankUiSettings } from "../../settings.js";
+import { useBusinessAccountDetails } from "../../hooks/circuit.js";
 
 export function InvalidIbanView({ error }: State.InvalidIban) {
   return (
@@ -77,11 +78,35 @@ function ImportantMessage(): VNode {
 
 }
 
-export function ReadyView({ account, balance, balanceIsDebit, limit, payto }: State.Ready): VNode<{}> {
-  const { i18n } = useTranslationContext();
+export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> {
   return <Fragment>
+    <MaybeBusinessButton account={account} onClick={goToBusinessAccount} />
     <PaymentOptions limit={limit} />
     <Transactions account={account} />
   </Fragment>;
 }
 
+function MaybeBusinessButton({
+  account,
+  onClick,
+}: {
+  account: string;
+  onClick: () => void;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useBusinessAccountDetails(account);
+  if (!result.ok) return <Fragment />;
+  return (
+    <div class="w-full flex justify-end">
+      <button
+        class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+        onClick={(e) => {
+          e.preventDefault()
+          onClick()
+        }}
+      >
+        <i18n.Translate>Business Profile</i18n.Translate>
+      </button>
+    </div>
+  );
+}
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 80a8224d4..4b23686d6 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -39,36 +39,12 @@ const versionText = VERSION
 
 const logger = new Logger("BankFrame");
 
-function MaybeBusinessButton({
-  account,
-  onClick,
-}: {
-  account: string;
-  onClick: () => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  if (!result.ok) return <Fragment />;
-  return (
-    <a
-      href="#"
-      class="pure-button pure-button-primary"
-      onClick={(e) => {
-        e.preventDefault();
-        onClick();
-      }}
-    >{i18n.str`Business Profile`}</a>
-  );
-}
-
 export function BankFrame({
   children,
-  goToBusinessAccount,
   account,
 }: {
-  account: string | undefined,
+  account?: string,
   children: ComponentChildren;
-  goToBusinessAccount?: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendContext();
@@ -489,5 +465,9 @@ function AccountBalance({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
   if (!result.ok) return <div />
 
-  return <div>{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""} {Amounts.currencyOf(result.data.balance.amount)} {Amounts.stringifyValue(result.data.balance.amount)}</div>
+  return <div>
+    {Amounts.currencyOf(result.data.balance.amount)}
+    &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
+    {Amounts.stringifyValue(result.data.balance.amount)}
+    </div>
 }
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index a911f347c..40cc147a6 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -31,14 +31,11 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
-import { useBackendContext } from "../context/backend.js";
 import { getInitialBackendBaseURL } from "../hooks/backend.js";
 import { useSettings } from "../hooks/settings.js";
 import { AccountPage } from "./AccountPage/index.js";
-import { AdminHome } from "./admin/Home.js";
 import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
-import { error } from "console";
 
 const logger = new Logger("AccountPage");
 
@@ -56,10 +53,12 @@ export function HomePage({
   onRegister,
   account,
   onPendingOperationFound,
+  goToBusinessAccount,
 }: {
   account: string,
   onPendingOperationFound: (id: string) => void;
   onRegister: () => void;
+  goToBusinessAccount: () => void;
 }): VNode {
   const [settings] = useSettings();
   const { i18n } = useTranslationContext();
@@ -72,6 +71,7 @@ export function HomePage({
   return (
     <AccountPage
       account={account}
+      goToBusinessAccount={goToBusinessAccount}
       onLoadNotOk={handleNotOkResult(i18n, onRegister)}
     />
   );
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index c82c1b28d..5cb09a5d4 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -33,76 +33,79 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
   const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
   // const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
 
-  return (<fieldset>
-    <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
-      <i18n.Translate>Send money to</i18n.Translate>
-    </legend>
+  return (
+    <fieldset>
+      <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
+        <i18n.Translate>Send money to</i18n.Translate>
+      </legend>
 
-    <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
-      {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
-      <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
-        <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
-          setTab("charge-wallet")
-        }} />
-        <span class="flex flex-1">
-          <span class="flex flex-col">
-            <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
-              <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
-            </span>
-            <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
-              <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
+      <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
+        {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+        <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+          <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
+            setTab("charge-wallet")
+          }} />
+          <span class="flex flex-1">
+            <span class="flex flex-col">
+              <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
+                <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
+              </span>
+              <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
+              </span>
             </span>
           </span>
-        </span>
-        <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-          <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
-        </svg>
-      </label>
+          <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+          </svg>
+        </label>
 
 
-      <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
-        <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
-          setTab("wire-transfer")
-        }} />
-        <span class="flex flex-1">
-          <span class="flex flex-col">
-            <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
-              <i18n.Translate>another bank account</i18n.Translate>
-            </span>
-            <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
-              <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+        <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+          <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
+            setTab("wire-transfer")
+          }} />
+          <span class="flex flex-1">
+            <span class="flex flex-col">
+              <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                <i18n.Translate>another bank account</i18n.Translate>
+              </span>
+              <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+              </span>
             </span>
           </span>
-        </span>
-        <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-          <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
-        </svg>
-      </label>
-    </div>
-    {tab === "charge-wallet" && (
-      <WalletWithdrawForm
-        focus
-        limit={limit}
-        onSuccess={(id) => {
-          updateSettings("currentWithdrawalOperationId", id);
-        }}
-        onCancel={() => {
-          setTab(undefined)
-        }}
-      />
-    )}
-    {tab === "wire-transfer" && (
-      <PaytoWireTransferForm
-        focus
-        limit={limit}
-        onSuccess={() => {
-          notifyInfo(i18n.str`Wire transfer created!`);
-        }}
-        onCancel={() => {
-          setTab(undefined)
-        }}
-      />
-    )}
+          <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+          </svg>
+        </label>
+      </div>
+      {tab === "charge-wallet" && (
+        <WalletWithdrawForm
+          focus
+          limit={limit}
+          onSuccess={(id) => {
+            updateSettings("currentWithdrawalOperationId", id);
+          }}
+          onCancel={() => {
+            setTab(undefined)
+          }}
+        />
+      )}
+      {tab === "wire-transfer" && (
+        <PaytoWireTransferForm
+          focus
+          title={i18n.str`Transfer details`}
+          limit={limit}
+          onSuccess={() => {
+            notifyInfo(i18n.str`Wire transfer created!`);
+          }}
+          onCancel={() => {
+            setTab(undefined)
+          }}
+        />
+      )}
 
-  </fieldset>)
+    </fieldset>
+  )
 }
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index cdaf363e3..af6f7caee 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -44,10 +44,12 @@ const logger = new Logger("PaytoWireTransferForm");
 
 export function PaytoWireTransferForm({
   focus,
+  title,
   onSuccess,
   onCancel,
   limit,
 }: {
+  title: TranslatedString,
   focus?: boolean;
   onSuccess: () => void;
   onCancel: (() => void) | undefined;
@@ -158,7 +160,9 @@ export function PaytoWireTransferForm({
 
   return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
     <div class="px-4 sm:px-0">
-      <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Transfer details</i18n.Translate></h2>
+      <h2 class="text-base font-semibold leading-7 text-gray-900">
+        {title}
+      </h2>
       <div>
         <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-1 sm:gap-x-4">
           <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (!isRawPayto ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index b912b9060..f972e0380 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -45,7 +45,7 @@ export function RegistrationPage({
   return <RegistrationForm onComplete={onComplete} />;
 }
 
-export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9]*$/;
+export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
 
 /**
  * Collect and submit registration data.
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
index 91b50b84c..6acf0361e 100644
--- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -1,5 +1,5 @@
 import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode,h } from "preact";
+import { VNode, h } from "preact";
 import { useAdminAccountAPI, useBusinessAccountDetails } from "../hooks/circuit.js";
 import { useState } from "preact/hooks";
 import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
@@ -7,137 +7,161 @@ import { buildRequestErrorMessage } from "../utils.js";
 import { AccountForm } from "./admin/AccountForm.js";
 
 export function ShowAccountDetails({
-    account,
-    onClear,
-    onUpdateSuccess,
-    onLoadNotOk,
-    onChangePassword,
-  }: {
-    onLoadNotOk: <T>(
-      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-    ) => VNode;
-    onClear?: () => void;
-    onChangePassword: () => void;
-    onUpdateSuccess: () => void;
-    account: string;
-  }): VNode {
-    const { i18n } = useTranslationContext();
-    const result = useBusinessAccountDetails(account);
-    const { updateAccount } = useAdminAccountAPI();
-    const [update, setUpdate] = useState(false);
-    const [submitAccount, setSubmitAccount] = useState<
-      SandboxBackend.Circuit.CircuitAccountData | undefined
-    >();
-  
-    if (!result.ok) {
-      if (result.loading || result.type === ErrorType.TIMEOUT) {
-        return onLoadNotOk(result);
-      }
-      if (result.status === HttpStatusCode.NotFound) {
-        return <div>account not found</div>;
-      }
+  account,
+  onClear,
+  onUpdateSuccess,
+  onLoadNotOk,
+  onChangePassword,
+}: {
+  onLoadNotOk: <T>(
+    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+  ) => VNode;
+  onClear?: () => void;
+  onChangePassword: () => void;
+  onUpdateSuccess: () => void;
+  account: string;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useBusinessAccountDetails(account);
+  const { updateAccount } = useAdminAccountAPI();
+  const [update, setUpdate] = useState(false);
+  const [submitAccount, setSubmitAccount] = useState<
+    SandboxBackend.Circuit.CircuitAccountData | undefined
+  >();
+
+  if (!result.ok) {
+    if (result.loading || result.type === ErrorType.TIMEOUT) {
       return onLoadNotOk(result);
     }
-  
-    return (
-      <div>
-        <div>
-          <h1 class="nav welcome-text">
-            <i18n.Translate>Business account details</i18n.Translate>
-          </h1>
+    if (result.status === HttpStatusCode.NotFound) {
+      return <div>account not found</div>;
+    }
+    return onLoadNotOk(result);
+  }
+
+  async function doUpdate() {
+    if (!update) {
+      setUpdate(true);
+    } else {
+      if (!submitAccount) return;
+      try {
+        await updateAccount(account, {
+          cashout_address: submitAccount.cashout_address,
+          contact_data: submitAccount.contact_data,
+        });
+        onUpdateSuccess();
+      } catch (error) {
+        if (error instanceof RequestError) {
+          notify(
+            buildRequestErrorMessage(i18n, error.cause, {
+              onClientError: (status) =>
+                status === HttpStatusCode.Forbidden
+                  ? i18n.str`The rights to change the account are not sufficient`
+                  : status === HttpStatusCode.NotFound
+                    ? i18n.str`The username was not found`
+                    : undefined,
+            }),
+          );
+        } else {
+          notifyError(
+            i18n.str`Operation failed, please report`,
+            (error instanceof Error
+              ? error.message
+              : JSON.stringify(error)) as TranslatedString
+          )
+        }
+      }
+    }
+  }
+
+  return (
+    <div>
+      <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+        <div class="px-4 sm:px-0">
+          <h2 class="text-base font-semibold leading-7 text-gray-900">
+            {update ?
+              <i18n.Translate>Update account</i18n.Translate>
+              :
+              <i18n.Translate>Account details</i18n.Translate>
+            }
+          </h2>
+          <div class="mt-4">
+        <div class="flex items-center justify-between">
+          <span class="flex flex-grow flex-col">
+            <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+              <i18n.Translate>change the account details</i18n.Translate>
+            </span>
+          </span>
+          <button type="button" data-enabled={!update} class="bg-indigo-600 data-[enabled=true]:bg-gray-200 relative inline-flex h-5 w-10 flex-shrink-0 cursor-pointer rounded-full ring-2 border-gray-600 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
+            onClick={() => {
+              setUpdate(!update)
+            }}>
+            <span aria-hidden="true" data-enabled={!update} class="translate-x-5 data-[enabled=true]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+          </button>
         </div>
-        <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-          <AccountForm
-            template={result.data}
-            purpose={update ? "update" : "show"}
-            onChange={(a) => setSubmitAccount(a)}
-          />
-  
-          <p class="buttons-account">
-            <div
-              style={{
-                display: "flex",
-                justifyContent: "space-between",
-                flexFlow: "wrap-reverse",
-              }}
-            >
+      </div>
+
+        </div>
+        <AccountForm
+          template={result.data}
+          purpose={update ? "update" : "show"}
+          onChange={(a) => setSubmitAccount(a)}
+        >
+
+        </AccountForm>
+
+        <p class="buttons-account">
+          <div
+            style={{
+              display: "flex",
+              justifyContent: "space-between",
+              flexFlow: "wrap-reverse",
+            }}
+          >
+            <div>
+              {onClear ? (
+                <input
+                  class="pure-button"
+                  type="submit"
+                  value={i18n.str`Close`}
+                  onClick={async (e) => {
+                    e.preventDefault();
+                    onClear();
+                  }}
+                />
+              ) : undefined}
+            </div>
+            <div style={{ display: "flex" }}>
               <div>
-                {onClear ? (
-                  <input
-                    class="pure-button"
-                    type="submit"
-                    value={i18n.str`Close`}
-                    onClick={async (e) => {
-                      e.preventDefault();
-                      onClear();
-                    }}
-                  />
-                ) : undefined}
+                <input
+                  id="select-exchange"
+                  class="pure-button pure-button-primary content"
+                  disabled={update && !submitAccount}
+                  type="submit"
+                  value={i18n.str`Change password`}
+                  onClick={async (e) => {
+                    e.preventDefault();
+                    onChangePassword();
+                  }}
+                />
               </div>
-              <div style={{ display: "flex" }}>
-                <div>
-                  <input
-                    id="select-exchange"
-                    class="pure-button pure-button-primary content"
-                    disabled={update && !submitAccount}
-                    type="submit"
-                    value={i18n.str`Change password`}
-                    onClick={async (e) => {
-                      e.preventDefault();
-                      onChangePassword();
-                    }}
-                  />
-                </div>
-                <div>
-                  <input
-                    id="select-exchange"
-                    class="pure-button pure-button-primary content"
-                    disabled={update && !submitAccount}
-                    type="submit"
-                    value={update ? i18n.str`Confirm` : i18n.str`Update`}
-                    onClick={async (e) => {
-                      e.preventDefault();
-  
-                      if (!update) {
-                        setUpdate(true);
-                      } else {
-                        if (!submitAccount) return;
-                        try {
-                          await updateAccount(account, {
-                            cashout_address: submitAccount.cashout_address,
-                            contact_data: submitAccount.contact_data,
-                          });
-                          onUpdateSuccess();
-                        } catch (error) {
-                          if (error instanceof RequestError) {
-                            notify(
-                              buildRequestErrorMessage(i18n, error.cause, {
-                                onClientError: (status) =>
-                                  status === HttpStatusCode.Forbidden
-                                    ? i18n.str`The rights to change the account are not sufficient`
-                                    : status === HttpStatusCode.NotFound
-                                      ? i18n.str`The username was not found`
-                                      : undefined,
-                              }),
-                            );
-                          } else {
-                            notifyError(
-                              i18n.str`Operation failed, please report`,
-                              (error instanceof Error
-                                ? error.message
-                                : JSON.stringify(error)) as TranslatedString
-                            )
-                          }
-                        }
-                      }
-                    }}
-                  />
-                </div>
+              <div>
+                <input
+                  id="select-exchange"
+                  class="pure-button pure-button-primary content"
+                  disabled={update && !submitAccount}
+                  type="submit"
+                  value={update ? i18n.str`Confirm` : i18n.str`Update`}
+                  onClick={async (e) => {
+                    e.preventDefault();
+                    doUpdate()
+                  }}
+                />
               </div>
             </div>
-          </p>
-        </div>
+          </div>
+        </p>
       </div>
-    );
-  }
-  
\ No newline at end of file
+    </div>
+  );
+}
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index 084a5b643..d19c411f3 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -1,131 +1,181 @@
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
 import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useEffect, useRef, useState } from "preact/hooks";
+import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useAdminAccountAPI, useBusinessAccountDetails } from "../hooks/circuit.js";
-import { useState } from "preact/hooks";
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { VNode,h ,Fragment} from "preact";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 
 export function UpdateAccountPassword({
-    account,
-    onClear,
-    onUpdateSuccess,
-    onLoadNotOk,
-  }: {
-    onLoadNotOk: <T>(
-      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-    ) => VNode;
-    onClear: () => void;
-    onUpdateSuccess: () => void;
-    account: string;
-  }): VNode {
-    const { i18n } = useTranslationContext();
-    const result = useBusinessAccountDetails(account);
-    const { changePassword } = useAdminAccountAPI();
-    const [password, setPassword] = useState<string | undefined>();
-    const [repeat, setRepeat] = useState<string | undefined>();
-  
-    if (!result.ok) {
-      if (result.loading || result.type === ErrorType.TIMEOUT) {
-        return onLoadNotOk(result);
-      }
-      if (result.status === HttpStatusCode.NotFound) {
-        return <div>account not found</div>;
-      }
+  account,
+  onCancel,
+  onUpdateSuccess,
+  onLoadNotOk,
+  focus,
+}: {
+  onLoadNotOk: <T>(
+    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+  ) => VNode;
+  onCancel: () => void;
+  focus?: boolean,
+  onUpdateSuccess: () => void;
+  account: string;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useBusinessAccountDetails(account);
+  const { changePassword } = useAdminAccountAPI();
+  const [password, setPassword] = useState<string | undefined>();
+  const [repeat, setRepeat] = useState<string | undefined>();
+
+  const ref = useRef<HTMLInputElement>(null);
+  useEffect(() => {
+    if (focus) ref.current?.focus();
+  }, [focus]);
+
+  if (!result.ok) {
+    if (result.loading || result.type === ErrorType.TIMEOUT) {
       return onLoadNotOk(result);
     }
-  
-    const errors = undefinedIfEmpty({
-      password: !password ? i18n.str`required` : undefined,
-      repeat: !repeat
-        ? i18n.str`required`
-        : password !== repeat
-          ? i18n.str`password doesn't match`
-          : undefined,
-    });
-  
-    return (
-      <div>
-        <div>
-          <h1 class="nav welcome-text">
-            <i18n.Translate>Update password for {account}</i18n.Translate>
-          </h1>
-        </div>
-  
-        <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-          <form class="pure-form">
-            <fieldset>
-              <label>{i18n.str`Password`}</label>
-              <input
-                type="password"
-                value={password ?? ""}
-                onChange={(e) => {
-                  setPassword(e.currentTarget.value);
-                }}
-              />
-              <ShowInputErrorLabel
-                message={errors?.password}
-                isDirty={password !== undefined}
-              />
-            </fieldset>
-            <fieldset>
-              <label>{i18n.str`Repeat password`}</label>
-              <input
-                type="password"
-                value={repeat ?? ""}
-                onChange={(e) => {
-                  setRepeat(e.currentTarget.value);
-                }}
-              />
-              <ShowInputErrorLabel
-                message={errors?.repeat}
-                isDirty={repeat !== undefined}
-              />
-            </fieldset>
-          </form>
-          <p>
-            <div style={{ display: "flex", justifyContent: "space-between" }}>
-              <div>
+    if (result.status === HttpStatusCode.NotFound) {
+      return <div>account not found</div>;
+    }
+    return onLoadNotOk(result);
+  }
+
+  const errors = undefinedIfEmpty({
+    password: !password ? i18n.str`required` : undefined,
+    repeat: !repeat
+      ? i18n.str`required`
+      : password !== repeat
+        ? i18n.str`password doesn't match`
+        : undefined,
+  });
+
+  async function doChangePassword() {
+    if (!!errors || !password) return;
+    try {
+      const r = await changePassword(account, {
+        new_password: password,
+      });
+      onUpdateSuccess();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(buildRequestErrorMessage(i18n, error.cause));
+      } else {
+        notifyError(i18n.str`Operation failed, please report`, (error instanceof Error
+          ? error.message
+          : JSON.stringify(error)) as TranslatedString)
+      }
+    }
+  }
+
+  return (
+    <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+      <div class="px-4 sm:px-0">
+        <h2 class="text-base font-semibold leading-7 text-gray-900">
+          <i18n.Translate>Update password for account "{account}"</i18n.Translate>
+        </h2>
+      </div>
+      <form
+        class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+        autoCapitalize="none"
+        autoCorrect="off"
+        onSubmit={e => {
+          e.preventDefault()
+        }}
+      >
+        <div class="px-4 py-6 sm:p-8">
+          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+
+            <div class="sm:col-span-5">
+              <label
+                class="block text-sm font-medium leading-6 text-gray-900"
+                for="password"
+              >
+                {i18n.str`New password`}
+              </label>
+              <div class="mt-2">
                 <input
-                  class="pure-button"
-                  type="submit"
-                  value={i18n.str`Close`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    onClear();
+                  ref={ref}
+                  type="password"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  name="password"
+                  id="password"
+                  data-error={!!errors?.password && password !== undefined}
+                  value={password ?? ""}
+                  onChange={(e) => {
+                    setPassword(e.currentTarget.value)
                   }}
+                  // placeholder=""
+                  autocomplete="off"
+                />
+                <ShowInputErrorLabel
+                  message={errors?.password}
+                  isDirty={password !== undefined}
                 />
               </div>
-              <div>
+              {/* <p class="mt-2 text-sm text-gray-500" >
+              <i18n.Translate>user </i18n.Translate>
+            </p> */}
+            </div>
+
+            <div class="sm:col-span-5">
+              <label
+                class="block text-sm font-medium leading-6 text-gray-900"
+                for="repeat"
+              >
+                {i18n.str`Type it again`}
+              </label>
+              <div class="mt-2">
                 <input
-                  id="select-exchange"
-                  class="pure-button pure-button-primary content"
-                  disabled={!!errors}
-                  type="submit"
-                  value={i18n.str`Confirm`}
-                  onClick={async (e) => {
-                    e.preventDefault();
-                    if (!!errors || !password) return;
-                    try {
-                      const r = await changePassword(account, {
-                        new_password: password,
-                      });
-                      onUpdateSuccess();
-                    } catch (error) {
-                      if (error instanceof RequestError) {
-                        notify(buildRequestErrorMessage(i18n, error.cause));
-                      } else {
-                        notifyError(i18n.str`Operation failed, please report`, (error instanceof Error
-                          ? error.message
-                          : JSON.stringify(error)) as TranslatedString)
-                      }
-                    }
+                  type="password"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  name="repeat"
+                  id="repeat"
+                  data-error={!!errors?.repeat && repeat !== undefined}
+                  value={repeat ?? ""}
+                  onChange={(e) => {
+                    setRepeat(e.currentTarget.value)
                   }}
+                  // placeholder=""
+                  autocomplete="off"
+                />
+                <ShowInputErrorLabel
+                  message={errors?.repeat}
+                  isDirty={repeat !== undefined}
                 />
               </div>
+              <p class="mt-2 text-sm text-gray-500" >
+                <i18n.Translate>repeat the same password</i18n.Translate>
+              </p>
             </div>
-          </p>
+
+
+
+          </div>
         </div>
-      </div>
-    );
-  }
\ No newline at end of file
+        <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+          {onCancel ?
+            <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+              onClick={onCancel}
+            >
+              <i18n.Translate>Cancel</i18n.Translate>
+            </button>
+            : <div />
+          }
+          <button type="submit"
+            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+            disabled={!!errors}
+            onClick={(e) => {
+              e.preventDefault()
+              doChangePassword()
+            }}
+          >
+            <i18n.Translate>Change</i18n.Translate>
+          </button>
+        </div>
+      </form>
+    </div>
+
+  );
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
index 8ab3e1323..90ddd611d 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -7,50 +7,30 @@ import { notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 
 export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode {
-    const { i18n } = useTranslationContext();
-    const r = useBackendContext();
-    const account = r.state.status === "loggedIn" ? r.state.username : "admin";
-    const result = useAccountDetails(account);
-  
-    if (!result.ok) {
-      return handleNotOkResult(i18n, onRegister)(result);
-    }
-    const { data } = result;
-    const balance = Amounts.parseOrThrow(data.balance.amount);
-    const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
-    const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
-    const limit = balanceIsDebit
-      ? Amounts.sub(debitThreshold, balance).amount
-      : Amounts.add(balance, debitThreshold).amount;
-    if (!balance) return <Fragment />;
-    return (
-      <Fragment>
-        <section id="assets">
-          <div class="asset-summary">
-            <h2>{i18n.str`Bank account balance`}</h2>
-            {!balance ? (
-              <div class="large-amount" style={{ color: "gray" }}>
-                Waiting server response...
-              </div>
-            ) : (
-              <div class="large-amount amount">
-                {balanceIsDebit ? <b>-</b> : null}
-                <span class="value">{`${Amounts.stringifyValue(balance)}`}</span>
-                &nbsp;
-                <span class="currency">{`${balance.currency}`}</span>
-              </div>
-            )}
-          </div>
-        </section>
-        <PaytoWireTransferForm
-          focus
-          limit={limit}
-          onSuccess={() => {
-            notifyInfo(i18n.str`Wire transfer created!`);
-          }}
-          onCancel={undefined}
-        />
-      </Fragment>
-    );
+  const { i18n } = useTranslationContext();
+  const r = useBackendContext();
+  const account = r.state.status === "loggedIn" ? r.state.username : "admin";
+  const result = useAccountDetails(account);
+
+  if (!result.ok) {
+    return handleNotOkResult(i18n, onRegister)(result);
   }
-  
\ No newline at end of file
+  const { data } = result;
+  const balance = Amounts.parseOrThrow(data.balance.amount);
+  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
+  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+  const limit = balanceIsDebit
+    ? Amounts.sub(debitThreshold, balance).amount
+    : Amounts.add(balance, debitThreshold).amount;
+  if (!balance) return <Fragment />;
+  return (
+    <PaytoWireTransferForm
+      title={i18n.str`Make a wire transfer`}
+      limit={limit}
+      onSuccess={() => {
+        notifyInfo(i18n.str`Wire transfer created!`);
+      }}
+      onCancel={undefined}
+    />
+  );
+}
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index 9ca0323a1..02df824a2 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -1,9 +1,9 @@
-import { VNode,h  } from "preact";
+import { ComponentChildren, VNode, h } from "preact";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
 import { PartialButDefined, RecursivePartial, WithIntermediate, undefinedIfEmpty, validateIBAN } from "../../utils.js";
-import { useState } from "preact/hooks";
+import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { parsePaytoUri } from "@gnu-taler/taler-util";
+import { buildPayto, parsePaytoUri } from "@gnu-taler/taler-util";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
 const EMAIL_REGEX =
@@ -19,201 +19,301 @@ const REGEX_JUST_NUMBERS_REGEX = /^\+[0-9 ]*$/;
  * @returns
  */
 export function AccountForm({
-    template,
-    purpose,
-    onChange,
-  }: {
-    template: SandboxBackend.Circuit.CircuitAccountData | undefined;
-    onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
-    purpose: "create" | "update" | "show";
-  }): VNode {
-    const initial = initializeFromTemplate(template);
-    const [form, setForm] = useState(initial);
-    const [errors, setErrors] = useState<
-      RecursivePartial<typeof initial> | undefined
-    >(undefined);
-    const { i18n } = useTranslationContext();
-  
-    function updateForm(newForm: typeof initial): void {
-      const parsed = !newForm.cashout_address
-        ? undefined
-        : parsePaytoUri(newForm.cashout_address);
-  
-      const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
-        cashout_address: !newForm.cashout_address
+  template,
+  purpose,
+  onChange,
+  focus,
+  children,
+}: {
+  focus?: boolean,
+  children: ComponentChildren,
+  template: SandboxBackend.Circuit.CircuitAccountData | undefined;
+  onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
+  purpose: "create" | "update" | "show";
+}): VNode {
+  const initial = initializeFromTemplate(template);
+  const [form, setForm] = useState(initial);
+  const [errors, setErrors] = useState<
+    RecursivePartial<typeof initial> | undefined
+  >(undefined);
+  const { i18n } = useTranslationContext();
+  const ref = useRef<HTMLInputElement>(null);
+  useEffect(() => {
+    if (focus) ref.current?.focus();
+  }, [focus]);
+
+  function updateForm(newForm: typeof initial): void {
+
+    const parsed = !newForm.cashout_address
+      ? undefined
+      : buildPayto("iban", newForm.cashout_address, undefined);;
+
+    const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
+      cashout_address: !newForm.cashout_address
+        ? i18n.str`required`
+        : !parsed
+          ? i18n.str`does not follow the pattern`
+          : !parsed.isKnown || parsed.targetType !== "iban"
+            ? i18n.str`only "IBAN" target are supported`
+            : !IBAN_REGEX.test(parsed.iban)
+              ? i18n.str`IBAN should have just uppercased letters and numbers`
+              : validateIBAN(parsed.iban, i18n),
+      contact_data: undefinedIfEmpty({
+        email: !newForm.contact_data?.email
           ? i18n.str`required`
-          : !parsed
-            ? i18n.str`does not follow the pattern`
-            : !parsed.isKnown || parsed.targetType !== "iban"
-              ? i18n.str`only "IBAN" target are supported`
-              : !IBAN_REGEX.test(parsed.iban)
-                ? i18n.str`IBAN should have just uppercased letters and numbers`
-                : validateIBAN(parsed.iban, i18n),
-        contact_data: undefinedIfEmpty({
-          email: !newForm.contact_data?.email
-            ? i18n.str`required`
-            : !EMAIL_REGEX.test(newForm.contact_data.email)
-              ? i18n.str`it should be an email`
+          : !EMAIL_REGEX.test(newForm.contact_data.email)
+            ? i18n.str`it should be an email`
+            : undefined,
+        phone: !newForm.contact_data?.phone
+          ? i18n.str`required`
+          : !newForm.contact_data.phone.startsWith("+")
+            ? i18n.str`should start with +`
+            : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
+              ? i18n.str`phone number can't have other than numbers`
               : undefined,
-          phone: !newForm.contact_data?.phone
-            ? i18n.str`required`
-            : !newForm.contact_data.phone.startsWith("+")
-              ? i18n.str`should start with +`
-              : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
-                ? i18n.str`phone number can't have other than numbers`
-                : undefined,
-        }),
-        iban: !newForm.iban
-          ? undefined //optional field
-          : !IBAN_REGEX.test(newForm.iban)
-            ? i18n.str`IBAN should have just uppercased letters and numbers`
-            : validateIBAN(newForm.iban, i18n),
-        name: !newForm.name ? i18n.str`required` : undefined,
-        username: !newForm.username ? i18n.str`required` : undefined,
-      });
-      setErrors(errors);
-      setForm(newForm);
-      onChange(errors === undefined ? (newForm as any) : undefined);
-    }
-  
-    return (
-      <form class="pure-form">
-        <fieldset>
-          <label for="username">
-            {i18n.str`Username`}
-            {purpose === "create" && <b style={{ color: "red" }}>*</b>}
-          </label>
-          <input
-            name="username"
-            type="text"
-            disabled={purpose !== "create"}
-            value={form.username}
-            onChange={(e) => {
-              form.username = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />{" "}
-          <ShowInputErrorLabel
-            message={errors?.username}
-            isDirty={form.username !== undefined}
-          />
-        </fieldset>
-        <fieldset>
-          <label>
-            {i18n.str`Name`}
-            {purpose === "create" && <b style={{ color: "red" }}>*</b>}
-          </label>
-          <input
-            disabled={purpose !== "create"}
-            value={form.name ?? ""}
-            onChange={(e) => {
-              form.name = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.name}
-            isDirty={form.name !== undefined}
-          />
-        </fieldset>
-        {purpose !== "create" && (
-          <fieldset>
-            <label>{i18n.str`Internal IBAN`}</label>
-            <input
-              disabled={true}
-              value={form.iban ?? ""}
-              onChange={(e) => {
-                form.iban = e.currentTarget.value;
-                updateForm(structuredClone(form));
-              }}
-            />
-            <ShowInputErrorLabel
-              message={errors?.iban}
-              isDirty={form.iban !== undefined}
-            />
-          </fieldset>
-        )}
-        <fieldset>
-          <label>
-            {i18n.str`Email`}
-            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-          </label>
-          <input
-            disabled={purpose === "show"}
-            value={form.contact_data.email ?? ""}
-            onChange={(e) => {
-              form.contact_data.email = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.contact_data?.email}
-            isDirty={form.contact_data.email !== undefined}
-          />
-        </fieldset>
-        <fieldset>
-          <label>
-            {i18n.str`Phone`}
-            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-          </label>
-          <input
-            disabled={purpose === "show"}
-            value={form.contact_data.phone ?? ""}
-            onChange={(e) => {
-              form.contact_data.phone = e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.contact_data?.phone}
-            isDirty={form.contact_data?.phone !== undefined}
-          />
-        </fieldset>
-        <fieldset>
-          <label>
-            {i18n.str`Cashout address`}
-            {purpose !== "show" && <b style={{ color: "red" }}>*</b>}
-          </label>
-          <input
-            disabled={purpose === "show"}
-            value={(form.cashout_address ?? "").substring("payto://iban/".length)}
-            onChange={(e) => {
-              form.cashout_address = "payto://iban/" + e.currentTarget.value;
-              updateForm(structuredClone(form));
-            }}
-          />
-          <ShowInputErrorLabel
-            message={errors?.cashout_address}
-            isDirty={form.cashout_address !== undefined}
-          />
-        </fieldset>
-      </form>
-    );
+      }),
+      // iban: !newForm.iban
+      //   ? undefined //optional field
+      //   : !IBAN_REGEX.test(newForm.iban)
+      //     ? i18n.str`IBAN should have just uppercased letters and numbers`
+      //     : validateIBAN(newForm.iban, i18n),
+      name: !newForm.name ? i18n.str`required` : undefined,
+      username: !newForm.username ? i18n.str`required` : undefined,
+    });
+    setErrors(errors);
+    setForm(newForm);
+    onChange(errors === undefined ? (newForm as any) : undefined);
   }
-  
-  function initializeFromTemplate(
-    account: SandboxBackend.Circuit.CircuitAccountData | undefined,
-  ): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
-    const emptyAccount = {
-      cashout_address: undefined,
-      iban: undefined,
-      name: undefined,
-      username: undefined,
-      contact_data: undefined,
-    };
-    const emptyContact = {
-      email: undefined,
-      phone: undefined,
-    };
-  
-    const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
-      structuredClone(account) ?? emptyAccount;
-    if (typeof initial.contact_data === "undefined") {
-      initial.contact_data = emptyContact;
-    }
-    initial.contact_data.email;
-    return initial as any;
+
+  return (
+    <form
+      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+      autoCapitalize="none"
+      autoCorrect="off"
+      onSubmit={e => {
+        e.preventDefault()
+      }}
+    >
+      <div class="px-4 py-6 sm:p-8">
+        <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+
+
+          <div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="username"
+            >
+              {i18n.str`Username`}
+              {purpose === "create" && <b style={{ color: "red" }}> *</b>}
+            </label>
+            <div class="mt-2">
+              <input
+                ref={ref}
+                type="text"
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="username"
+                id="username"
+                data-error={!!errors?.username && form.username !== undefined}
+                disabled={purpose !== "create"}
+                value={form.username ?? ""}
+                onChange={(e) => {
+                  form.username = e.currentTarget.value;
+                  updateForm(structuredClone(form));
+                }}
+                // placeholder=""
+                autocomplete="off"
+              />
+              <ShowInputErrorLabel
+                message={errors?.username}
+                isDirty={form.username !== undefined}
+              />
+            </div>
+            <p class="mt-2 text-sm text-gray-500" >
+              <i18n.Translate>account identification in the bank</i18n.Translate>
+            </p>
+          </div>
+
+          <div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="name"
+            >
+              {i18n.str`Name`}
+              {purpose === "create" && <b style={{ color: "red" }}> *</b>}
+            </label>
+            <div class="mt-2">
+              <input
+                type="text"
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="name"
+                data-error={!!errors?.name && form.name !== undefined}
+                id="name"
+                disabled={purpose !== "create"}
+                value={form.name ?? ""}
+                onChange={(e) => {
+                  form.name = e.currentTarget.value;
+                  updateForm(structuredClone(form));
+                }}
+                // placeholder=""
+                autocomplete="off"
+              />
+              <ShowInputErrorLabel
+                message={errors?.name}
+                isDirty={form.name !== undefined}
+              />
+            </div>
+            <p class="mt-2 text-sm text-gray-500" >
+              <i18n.Translate>name of the person owner the account</i18n.Translate>
+            </p>
+          </div>
+
+
+          {purpose !== "create" && (<div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="internal-iban"
+            >
+              {i18n.str`Internal IBAN`}
+            </label>
+            <div class="mt-2">
+              <input
+                type="text"
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="internal-iban"
+                id="internal-iban"
+                disabled={true}
+                value={form.iban ?? ""}
+              />
+            </div>
+            <p class="mt-2 text-sm text-gray-500" >
+              <i18n.Translate>international bank account number</i18n.Translate>
+            </p>
+          </div>)}
+
+          <div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="email"
+            >
+              {i18n.str`Email`}
+              {purpose === "create" && <b style={{ color: "red" }}> *</b>}
+            </label>
+            <div class="mt-2">
+              <input
+                type="email"
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="email"
+                id="email"
+                data-error={!!errors?.contact_data?.email && form.contact_data.email !== undefined}
+                disabled={purpose !== "create"}
+                value={form.contact_data.email ?? ""}
+                onChange={(e) => {
+                  form.contact_data.email = e.currentTarget.value;
+                  updateForm(structuredClone(form));
+                }}
+                autocomplete="off"
+              />
+              <ShowInputErrorLabel
+                message={errors?.contact_data?.email}
+                isDirty={form.contact_data.email !== undefined}
+              />
+            </div>
+          </div>
+
+          <div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="phone"
+            >
+              {i18n.str`Phone`}
+              {purpose === "create" && <b style={{ color: "red" }}> *</b>}
+            </label>
+            <div class="mt-2">
+              <input
+                type="text"
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="phone"
+                id="phone"
+                disabled={purpose !== "create"}
+                value={form.contact_data.phone ?? ""}
+                data-error={!!errors?.contact_data?.phone && form.contact_data.phone !== undefined}
+                onChange={(e) => {
+                  form.contact_data.phone = e.currentTarget.value;
+                  updateForm(structuredClone(form));
+                }}
+                // placeholder=""
+                autocomplete="off"
+              />
+              <ShowInputErrorLabel
+                message={errors?.contact_data?.phone}
+                isDirty={form.contact_data.phone !== undefined}
+              />
+            </div>
+          </div>
+
+
+          <div class="sm:col-span-5">
+            <label
+              class="block text-sm font-medium leading-6 text-gray-900"
+              for="cashout"
+            >
+              {i18n.str`Cashout IBAN`}
+              {purpose !== "show" && <b style={{ color: "red" }}> *</b>}
+            </label>
+            <div class="mt-2">
+              <input
+                type="text"
+                data-error={!!errors?.cashout_address && form.cashout_address !== undefined}
+                class="block w-full disabled:bg-gray-100 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                name="cashout"
+                id="cashout"
+                disabled={purpose === "show"}
+                value={form.cashout_address ?? ""}
+                onChange={(e) => {
+                  form.cashout_address = e.currentTarget.value;
+                  updateForm(structuredClone(form));
+                }}
+                autocomplete="off"
+              />
+              <ShowInputErrorLabel
+                message={errors?.cashout_address}
+                isDirty={form.cashout_address !== undefined}
+              />
+            </div>
+            <p class="mt-2 text-sm text-gray-500" >
+              <i18n.Translate>account number where the money is going to be sent when doing cashouts</i18n.Translate>
+            </p>
+          </div>
+
+        </div>
+      </div>
+      {children}
+    </form>
+  );
+}
+
+function initializeFromTemplate(
+  account: SandboxBackend.Circuit.CircuitAccountData | undefined,
+): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
+  const emptyAccount = {
+    cashout_address: undefined,
+    iban: undefined,
+    name: undefined,
+    username: undefined,
+    contact_data: undefined,
+  };
+  const emptyContact = {
+    email: undefined,
+    phone: undefined,
+  };
+
+  const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
+    structuredClone(account) ?? emptyAccount;
+  if (typeof initial.contact_data === "undefined") {
+    initial.contact_data = emptyContact;
   }
-  
-  
-  
\ No newline at end of file
+  initial.contact_data.email;
+  return initial as any;
+}
+
+
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index 56b15818b..56d9c45f9 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -9,10 +9,10 @@ interface Props {
     onAction: (type: AccountAction, account: string) => void;
     account: string | undefined;
     onRegister: () => void;
-
+    onCreateAccount: () => void;
 }
 
-export function AccountList({ account, onAction, onRegister }: Props): VNode {
+export function AccountList({ account, onAction, onCreateAccount, onRegister }: Props): VNode {
     const result = useBusinessAccounts({ account });
     const { i18n } = useTranslationContext();
 
@@ -22,48 +22,60 @@ export function AccountList({ account, onAction, onRegister }: Props): VNode {
     }
 
     const { customers } = result.data;
-    return <section
-        id="main"
-        style={{ width: 600, marginLeft: "auto", marginRight: "auto" }}
-    >
-        {!customers.length ? (
-            <div></div>
-        ) : (
-            <article>
-                <h2>{i18n.str`Accounts:`}</h2>
-                <div class="results">
-                    <table class="pure-table pure-table-striped">
-                        <thead>
-                            <tr>
-                                <th>{i18n.str`Username`}</th>
-                                <th>{i18n.str`Name`}</th>
-                                <th>{i18n.str`Balance`}</th>
-                                <th>{i18n.str`Actions`}</th>
-                            </tr>
-                        </thead>
-                        <tbody>
-                            {customers.map((item, idx) => {
-                                const balance = !item.balance
-                                    ? undefined
-                                    : Amounts.parse(item.balance.amount);
-                                const balanceIsDebit =
-                                    item.balance &&
-                                    item.balance.credit_debit_indicator == "debit";
-                                return (
-                                    <tr key={idx}>
-                                        <td>
-                                            <a
-                                                href="#"
-                                                onClick={(e) => {
-                                                    e.preventDefault();
-                                                    onAction("show-details", item.username)
-                                                }}
-                                            >
-                                                {item.username}
-                                            </a>
+    return <div class="px-4 sm:px-6 lg:px-8">
+        <div class="sm:flex sm:items-center">
+            <div class="sm:flex-auto">
+                <h1 class="text-base font-semibold leading-6 text-gray-900">
+                    <i18n.Translate>Accounts</i18n.Translate>
+                </h1>
+                <p class="mt-2 text-sm text-gray-700">
+                    <i18n.Translate>A list of all business account in the bank.</i18n.Translate>
+                </p>
+            </div>
+            <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
+                <button type="button" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+                    onClick={(e) => {
+                        e.preventDefault()
+                        onCreateAccount()
+                    }}>
+                    <i18n.Translate>Create account</i18n.Translate>
+                </button>
+            </div>
+        </div>
+        <div class="mt-8 flow-root">
+            <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
+                <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
+                    {!customers.length ? (
+                        <div></div>
+                    ) : (
+                        <table class="min-w-full divide-y divide-gray-300">
+                            <thead>
+                                <tr>
+                                    <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th>
+                                    <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Name`}</th>
+                                    <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Balance`}</th>
+                                    <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
+                                        <span class="sr-only">{i18n.str`Actions`}</span>
+                                    </th>
+                                </tr>
+                            </thead>
+                            <tbody class="divide-y divide-gray-200">
+                                {customers.map((item, idx) => {
+                                    const balance = !item.balance
+                                        ? undefined
+                                        : Amounts.parse(item.balance.amount);
+                                    const balanceIsDebit =
+                                        item.balance &&
+                                        item.balance.credit_debit_indicator == "debit";
+
+                                    return <tr key={idx}>
+                                        <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">
+                                            {item.username}
                                         </td>
-                                        <td>{item.name}</td>
-                                        <td>
+                                        <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
+                                            {item.name}
+                                        </td>
+                                        <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
                                             {!balance ? (
                                                 i18n.str`unknown`
                                             ) : (
@@ -77,9 +89,8 @@ export function AccountList({ account, onAction, onRegister }: Props): VNode {
                                                 </span>
                                             )}
                                         </td>
-                                        <td>
-                                            <a
-                                                href="#"
+                                        <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
+                                            <a href="#" class="text-indigo-600 hover:text-indigo-900"
                                                 onClick={(e) => {
                                                     e.preventDefault();
                                                     onAction("update-password", item.username)
@@ -87,34 +98,71 @@ export function AccountList({ account, onAction, onRegister }: Props): VNode {
                                             >
                                                 change password
                                             </a>
-                                            &nbsp;
-                                            <a
-                                                href="#"
-                                                onClick={(e) => {
-                                                    e.preventDefault();
-                                                    onAction("show-cashout", item.username)
-                                                }}
+                                            <br/>
+                                            <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => {
+                                                e.preventDefault();
+                                                onAction("show-cashout", item.username)
+                                            }}
                                             >
                                                 cashouts
                                             </a>
-                                            &nbsp;
-                                            <a
-                                                href="#"
-                                                onClick={(e) => {
-                                                    e.preventDefault();
-                                                    onAction("remove-account", item.username)
-                                                }}
+                                            <br/>
+                                            <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => {
+                                                e.preventDefault();
+                                                onAction("remove-account", item.username)
+                                            }}
                                             >
                                                 remove
                                             </a>
                                         </td>
                                     </tr>
-                                );
-                            })}
-                        </tbody>
-                    </table>
+                                })}
+
+                                {/* <!-- More people... --> */}
+                            </tbody>
+                        </table>
+                    )}
                 </div>
-            </article>
-        )}
-    </section>
+            </div>
+        </div>
+    </div>
+
+    //     return <section
+    //         id="main"
+    //         style={{ width: 600, marginLeft: "auto", marginRight: "auto" }}
+    //     >
+    //             <article>
+    //                 <h2>{i18n.str`Accounts:`}</h2>
+    //                 <div class="results">
+    //                     <table class="pure-table pure-table-striped">
+    //                         <tbody>
+    //                                 return (
+    //                                     <tr key={idx}>
+    //                                         <td>
+    //                                             <a
+    //                                                 href="#"
+    //                                                 onClick={(e) => {
+    //                                                     e.preventDefault();
+    //                                                     onAction("show-details", item.username)
+    //                                                 }}
+    //                                             >
+    // {item.username}
+    //                                             </a>
+    //                                         </td>
+    //                                         <td>{item.name}</td>
+    //                                         <td>
+    //                                             
+    //                                         </td>
+    //                                         <td>
+
+    //                                         </td>
+    //                                     </tr>
+    //                                 );
+    //                             })}
+    //                         </tbody>
+    //                     </table>
+    //                 </div>
+    //             </article>
+    //         )}
+    //     </section>
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index 90835d52b..2146fc6f0 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -8,100 +8,94 @@ import { getRandomPassword } from "../rnd.js";
 import { AccountForm } from "./AccountForm.js";
 
 export function CreateNewAccount({
-    onClose,
-    onCreateSuccess,
+  onCancel,
+  onCreateSuccess,
 }: {
-    onClose: () => void;
-    onCreateSuccess: (password: string) => void;
+  onCancel: () => void;
+  onCreateSuccess: (password: string) => void;
 }): VNode {
-    const { i18n } = useTranslationContext();
-    const { createAccount } = useAdminAccountAPI();
-    const [submitAccount, setSubmitAccount] = useState<
-        SandboxBackend.Circuit.CircuitAccountData | undefined
-    >();
-    return (
-        <div>
-            <div>
-                <h1 class="nav welcome-text">
-                    <i18n.Translate>New account</i18n.Translate>
-                </h1>
-            </div>
+  const { i18n } = useTranslationContext();
+  const { createAccount } = useAdminAccountAPI();
+  const [submitAccount, setSubmitAccount] = useState<
+    SandboxBackend.Circuit.CircuitAccountData | undefined
+  >();
 
-            <div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
-                <AccountForm
-                    template={undefined}
-                    purpose="create"
-                    onChange={(a) => {
-                        setSubmitAccount(a);
-                    }}
-                />
+  async function doCreate() {
+    if (!submitAccount) return;
+    try {
+      const account: SandboxBackend.Circuit.CircuitAccountRequest =
+      {
+        cashout_address: submitAccount.cashout_address,
+        contact_data: submitAccount.contact_data,
+        internal_iban: submitAccount.iban,
+        name: submitAccount.name,
+        username: submitAccount.username,
+        password: getRandomPassword(),
+      };
 
-                <p>
-                    <div style={{ display: "flex", justifyContent: "space-between" }}>
-                        <div>
-                            <input
-                                class="pure-button"
-                                type="submit"
-                                value={i18n.str`Close`}
-                                onClick={async (e) => {
-                                    e.preventDefault();
-                                    onClose();
-                                }}
-                            />
-                        </div>
-                        <div>
-                            <input
-                                id="select-exchange"
-                                class="pure-button pure-button-primary content"
-                                disabled={!submitAccount}
-                                type="submit"
-                                value={i18n.str`Confirm`}
-                                onClick={async (e) => {
-                                    e.preventDefault();
+      await createAccount(account);
+      onCreateSuccess(account.password);
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Forbidden
+                ? i18n.str`The rights to perform the operation are not sufficient`
+                : status === HttpStatusCode.BadRequest
+                  ? i18n.str`Server replied that input data was invalid`
+                  : status === HttpStatusCode.Conflict
+                    ? i18n.str`At least one registration detail was not available`
+                    : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
 
-                                    if (!submitAccount) return;
-                                    try {
-                                        const account: SandboxBackend.Circuit.CircuitAccountRequest =
-                                        {
-                                            cashout_address: submitAccount.cashout_address,
-                                            contact_data: submitAccount.contact_data,
-                                            internal_iban: submitAccount.iban,
-                                            name: submitAccount.name,
-                                            username: submitAccount.username,
-                                            password: getRandomPassword(),
-                                        };
-
-                                        await createAccount(account);
-                                        onCreateSuccess(account.password);
-                                    } catch (error) {
-                                        if (error instanceof RequestError) {
-                                            notify(
-                                                buildRequestErrorMessage(i18n, error.cause, {
-                                                    onClientError: (status) =>
-                                                        status === HttpStatusCode.Forbidden
-                                                            ? i18n.str`The rights to perform the operation are not sufficient`
-                                                            : status === HttpStatusCode.BadRequest
-                                                                ? i18n.str`Input data was invalid`
-                                                                : status === HttpStatusCode.Conflict
-                                                                    ? i18n.str`At least one registration detail was not available`
-                                                                    : undefined,
-                                                }),
-                                            );
-                                        } else {
-                                            notifyError(
-                                                i18n.str`Operation failed, please report`,
-                                                (error instanceof Error
-                                                    ? error.message
-                                                    : JSON.stringify(error)) as TranslatedString
-                                            )
-                                        }
-                                    }
-                                }}
-                            />
-                        </div>
-                    </div>
-                </p>
-            </div>
+  return (
+    <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+      <div class="px-4 sm:px-0">
+        <h2 class="text-base font-semibold leading-7 text-gray-900">
+          <i18n.Translate>New business account</i18n.Translate>
+        </h2>
+      </div>
+      <AccountForm
+        template={undefined}
+        purpose="create"
+        onChange={(a) => {
+          setSubmitAccount(a);
+        }}
+      >
+        <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+          {onCancel ?
+            <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+              onClick={onCancel}
+            >
+              <i18n.Translate>Cancel</i18n.Translate>
+            </button>
+            : <div />
+          }
+          <button type="submit"
+            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+            disabled={!submitAccount}
+            onClick={(e) => {
+              e.preventDefault()
+              doCreate()
+            }}
+          >
+            <i18n.Translate>Create</i18n.Translate>
+          </button>
         </div>
-    );
+
+      </AccountForm>
+    </div>
+  );
 }
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
index e1ec6cfe0..f7d4e426e 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -17,17 +17,20 @@ import { RemoveAccount } from "./RemoveAccount.js";
 interface Props {
   onRegister: () => void;
 }
-export type AccountAction = "show-details" | 
-  "show-cashout" | 
-  "update-password" | 
-  "remove-account" | 
+export type AccountAction = "show-details" |
+  "show-cashout" |
+  "update-password" |
+  "remove-account" |
   "show-cashouts-details";
 
 export function AdminHome({ onRegister }: Props): VNode {
   const [action, setAction] = useState<{
     type: AccountAction,
     account: string
-  }>()
+  } | undefined>({
+    type:"remove-account",
+    account:"gnunet-at-sandbox"
+  })
 
   const [createAccount, setCreateAccount] = useState(false);
 
@@ -78,7 +81,7 @@ export function AdminHome({ onRegister }: Props): VNode {
           notifyInfo(i18n.str`Password changed`);
           setAction(undefined);
         }}
-        onClear={() => {
+        onCancel={() => {
           setAction(undefined);
         }}
       />
@@ -89,7 +92,7 @@ export function AdminHome({ onRegister }: Props): VNode {
           notifyInfo(i18n.str`Account removed`);
           setAction(undefined);
         }}
-        onClear={() => {
+        onCancel={() => {
           setAction(undefined);
         }}
       />
@@ -116,7 +119,7 @@ export function AdminHome({ onRegister }: Props): VNode {
   if (createAccount) {
     return (
       <CreateNewAccount
-        onClose={() => setCreateAccount(false)}
+        onCancel={() => setCreateAccount(false)}
         onCreateSuccess={(password) => {
           notifyInfo(
             i18n.str`Account created with password "${password}". The user must change the password on the next login.`,
@@ -129,34 +132,18 @@ export function AdminHome({ onRegister }: Props): VNode {
 
   return (
     <Fragment>
-      <div>
-        <h1 class="nav welcome-text">
-          <i18n.Translate>Admin panel</i18n.Translate>
-        </h1>
-      </div>
 
-      <p>
-        <div style={{ display: "flex", justifyContent: "space-between" }}>
-          <div></div>
-          <div>
-            <input
-              class="pure-button pure-button-primary content"
-              type="submit"
-              value={i18n.str`Create account`}
-              onClick={async (e) => {
-                e.preventDefault();
-
-                setCreateAccount(true);
-              }}
-            />
-          </div>
-        </div>
-      </p>
+      <AccountList
+        onCreateAccount={() => {
+          setCreateAccount(true);
+        }}
+        account={undefined}
+        onAction={(type, account) => setAction({ account, type })}
+        onRegister={onRegister}
+      />
 
       <AdminAccount onRegister={onRegister} />
 
-      <AccountList account={undefined} onAction={(type,account) => setAction({account, type})} onRegister={onRegister}/>
-
     </Fragment>
   );
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 2900db9d2..050f1fb8a 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -1,112 +1,218 @@
 import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode,h,Fragment } from "preact";
+import { VNode, h, Fragment } from "preact";
 import { useAccountDetails } from "../../hooks/access.js";
 import { useAdminAccountAPI } from "../../hooks/circuit.js";
 import { Amounts, HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { buildRequestErrorMessage } from "../../utils.js";
+import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
+import { useEffect, useRef, useState } from "preact/hooks";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
 
 export function RemoveAccount({
-    account,
-    onClear,
-    onUpdateSuccess,
-    onLoadNotOk,
-  }: {
-    onLoadNotOk: <T>(
-      error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-    ) => VNode;
-    onClear: () => void;
-    onUpdateSuccess: () => void;
-    account: string;
-  }): VNode {
-    const { i18n } = useTranslationContext();
-    const result = useAccountDetails(account);
-    const { deleteAccount } = useAdminAccountAPI();
-  
-    if (!result.ok) {
-      if (result.loading || result.type === ErrorType.TIMEOUT) {
-        return onLoadNotOk(result);
-      }
-      if (result.status === HttpStatusCode.NotFound) {
-        return <div>account not found</div>;
-      }
+  account,
+  onCancel,
+  onUpdateSuccess,
+  onLoadNotOk,
+  focus,
+}: {
+  onLoadNotOk: <T>(
+    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
+  ) => VNode;
+  focus?: boolean;
+  onCancel: () => void;
+  onUpdateSuccess: () => void;
+  account: string;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useAccountDetails(account);
+  const [accountName, setAccountName] = useState<string | undefined>()
+  const { deleteAccount } = useAdminAccountAPI();
+
+  if (!result.ok) {
+    if (result.loading || result.type === ErrorType.TIMEOUT) {
       return onLoadNotOk(result);
     }
-  
-    const balance = Amounts.parse(result.data.balance.amount);
-    if (!balance) {
-      return <div>there was an error reading the balance</div>;
+    if (result.status === HttpStatusCode.NotFound) {
+      return <div>account not found</div>;
     }
-    const isBalanceEmpty = Amounts.isZero(balance);
-    return (
-      <div>
-        <div>
-          <h1 class="nav welcome-text">
-            <i18n.Translate>Remove account: {account}</i18n.Translate>
-          </h1>
+    return onLoadNotOk(result);
+  }
+  const ref = useRef<HTMLInputElement>(null);
+  useEffect(() => {
+    if (focus) ref.current?.focus();
+  }, [focus]);
+
+  const balance = Amounts.parse(result.data.balance.amount);
+  if (!balance) {
+    return <div>there was an error reading the balance</div>;
+  }
+  const isBalanceEmpty = Amounts.isZero(balance);
+  if (!isBalanceEmpty) {
+    return <div>
+      <div class="rounded-md bg-yellow-50 p-4">
+        <div class="flex">
+          <div class="flex-shrink-0">
+            <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
+            </svg>
+          </div>
+          <div class="ml-3">
+            <h3 class="text-sm font-medium text-yellow-800">
+              <i18n.Translate>Can't delete the account</i18n.Translate>
+            </h3>
+            <div class="mt-2 text-sm text-yellow-700">
+              <p>
+                <i18n.Translate>The account can be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
+              </p>
+            </div>
+          </div>
+
         </div>
-        {/* {FXME: SHOW WARNING} */}
-        {/* {!isBalanceEmpty && (
-          <ErrorBannerFloat
-            error={{
-              title: i18n.str`Can't delete the account`,
-              description: i18n.str`Balance is not empty`,
-            }}
-            onClear={() => saveError(undefined)}
-          />
-        )} */}
-  
-        <p>
-          <div style={{ display: "flex", justifyContent: "space-between" }}>
-            <div>
-              <input
-                class="pure-button"
-                type="submit"
-                value={i18n.str`Cancel`}
-                onClick={async (e) => {
-                  e.preventDefault();
-                  onClear();
-                }}
-              />
+      </div>
+      <div class="mt-2 flex justify-end">
+        <button type="button" class="rounded-md ring-1 ring-gray-400 bg-white px-3 py-2 text-sm font-semibold shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 "
+          onClick={() => {
+            onCancel()
+          }}>
+          <i18n.Translate>Go back</i18n.Translate>
+        </button>
+      </div>
+    </div>
+  }
+
+  async function doRemove() {
+    try {
+      const r = await deleteAccount(account);
+      onUpdateSuccess();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Forbidden
+                ? i18n.str`The administrator specified a institutional username`
+                : status === HttpStatusCode.NotFound
+                  ? i18n.str`The username was not found`
+                  : status === HttpStatusCode.PreconditionFailed
+                    ? i18n.str`Balance was not zero`
+                    : undefined,
+          }),
+        );
+      } else {
+        notifyError(i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString);
+      }
+    }
+  }
+
+  const errors = undefinedIfEmpty({
+    accountName: !accountName
+      ? i18n.str`required`
+      : account !== accountName
+        ? i18n.str`name doesn't match`
+        : undefined,
+  });
+
+
+  return (
+    <div>
+      <div class="rounded-md bg-yellow-50 p-4">
+        <div class="flex">
+          <div class="flex-shrink-0">
+            <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
+            </svg>
+          </div>
+          <div class="ml-3">
+            <h3 class="text-sm font-bold text-yellow-800">
+              <i18n.Translate>You are going to remove the account</i18n.Translate>
+            </h3>
+            <div class="mt-2 text-sm text-yellow-700">
+              <p>
+                <i18n.Translate>This step can't be undone.</i18n.Translate>
+              </p>
             </div>
-            <div>
-              <input
-                id="select-exchange"
-                class="pure-button pure-button-primary content"
-                disabled={!isBalanceEmpty}
-                type="submit"
-                value={i18n.str`Confirm`}
-                onClick={async (e) => {
-                  e.preventDefault();
-                  try {
-                    const r = await deleteAccount(account);
-                    onUpdateSuccess();
-                  } catch (error) {
-                    if (error instanceof RequestError) {
-                      notify(
-                        buildRequestErrorMessage(i18n, error.cause, {
-                          onClientError: (status) =>
-                            status === HttpStatusCode.Forbidden
-                              ? i18n.str`The administrator specified a institutional username`
-                              : status === HttpStatusCode.NotFound
-                                ? i18n.str`The username was not found`
-                                : status === HttpStatusCode.PreconditionFailed
-                                  ? i18n.str`Balance was not zero`
-                                  : undefined,
-                        }),
-                      );
-                    } else {
-                      notifyError(i18n.str`Operation failed, please report`,
-                        (error instanceof Error
-                            ? error.message
-                            : JSON.stringify(error)) as TranslatedString);
-                    }
-                  }
-                }}
-              />
+          </div>
+
+        </div>
+      </div>
+
+      <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
+        <div class="px-4 sm:px-0">
+          <h2 class="text-base font-semibold leading-7 text-gray-900">
+            <i18n.Translate>Deleting account "{account}"</i18n.Translate>
+          </h2>
+        </div>
+        <form
+          class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+          autoCapitalize="none"
+          autoCorrect="off"
+          onSubmit={e => {
+            e.preventDefault()
+          }}
+        >
+          <div class="px-4 py-6 sm:p-8">
+            <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+
+              <div class="sm:col-span-5">
+                <label
+                  class="block text-sm font-medium leading-6 text-gray-900"
+                  for="password"
+                >
+                  {i18n.str`Verification`}
+                </label>
+                <div class="mt-2">
+                  <input
+                    ref={ref}
+                    type="text"
+                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                    name="password"
+                    id="password"
+                    data-error={!!errors?.accountName && accountName !== undefined}
+                    value={accountName ?? ""}
+                    onChange={(e) => {
+                      setAccountName(e.currentTarget.value)
+                    }}
+                    placeholder={account}
+                    autocomplete="off"
+                  />
+                  <ShowInputErrorLabel
+                    message={errors?.accountName}
+                    isDirty={accountName !== undefined}
+                  />
+                </div>
+                <p class="mt-2 text-sm text-gray-500" >
+                  <i18n.Translate>enter the account name that is going to be deleted</i18n.Translate>
+                </p>
+              </div>
+
+
+
             </div>
           </div>
-        </p>
+          <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+            {onCancel ?
+              <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+                onClick={onCancel}
+              >
+                <i18n.Translate>Cancel</i18n.Translate>
+              </button>
+              : <div />
+            }
+            <button type="submit"
+              class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
+              disabled={!!errors}
+              onClick={(e) => {
+                e.preventDefault()
+                doRemove()
+              }}
+            >
+              <i18n.Translate>Delete</i18n.Translate>
+            </button>
+          </div>
+        </form>
       </div>
-    );
-  }
-  
\ No newline at end of file
+    </div>
+  );
+}
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index 8beea640a..318a4cfda 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -109,7 +109,7 @@ export function BusinessAccount({
           notifyInfo(i18n.str`Password changed`);
           setUpdatePassword(false);
         }}
-        onClear={() => {
+        onCancel={() => {
           setUpdatePassword(false);
         }}
       />
-- 
cgit v1.2.3


From 56a6f47c7daae088c2017c0d9781ddcf7cee175b Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Thu, 21 Sep 2023 15:44:17 -0300
Subject: more ui

---
 packages/demobank-ui/src/components/Routing.tsx    |  12 +-
 packages/demobank-ui/src/hooks/settings.ts         |   8 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |  50 ++++++
 packages/demobank-ui/src/pages/BankFrame.tsx       | 133 ++++++++-------
 packages/demobank-ui/src/pages/HomePage.tsx        |  20 +--
 packages/demobank-ui/src/pages/PaymentOptions.tsx  | 137 +++++++--------
 packages/demobank-ui/src/pages/QrCodeSection.tsx   | 106 ++++++++++++
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   | 183 +++++++++++----------
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     | 159 +++++++++++++++---
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |   2 +-
 10 files changed, 553 insertions(+), 257 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index b8e39948b..e1fd93737 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -76,9 +76,9 @@ export function Routing(): VNode {
               onContinue={() => {
                 route("/account");
               }}
-              onLoadNotOk={() => {
-                route("/account");
-              }}
+              // onLoadNotOk={() => {
+              //   route("/account");
+              // }}
             />
           )}
         />
@@ -108,9 +108,9 @@ export function Routing(): VNode {
             } else {
               return <HomePage
                 account={username}
-                onPendingOperationFound={(wopid) => {
-                  route(`/operation/${wopid}`);
-                }}
+                // onPendingOperationFound={(wopid) => {
+                //   route(`/operation/${wopid}`);
+                // }}
                 goToBusinessAccount={() => {
                   route("/business");
                 }}
diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts
index 43e803726..c2fd93a0c 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -29,20 +29,26 @@ import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
 interface Settings {
   currentWithdrawalOperationId: string | undefined;
   showWithdrawalSuccess: boolean;
+  showDemoDescription: boolean;
   maxWithdrawalAmount: number;
+  fastWithdrawal: boolean;
 }
 
 export const codecForSettings = (): Codec<Settings> =>
   buildCodecForObject<Settings>()
     .property("currentWithdrawalOperationId", codecOptional(codecForString()))
     .property("showWithdrawalSuccess", (codecForBoolean()))
+    .property("showDemoDescription", (codecForBoolean()))
+    .property("fastWithdrawal", (codecForBoolean()))
     .property("maxWithdrawalAmount", codecForNumber())
     .build("Settings");
 
 const defaultSettings: Settings = {
   currentWithdrawalOperationId: undefined,
   showWithdrawalSuccess: true,
-  maxWithdrawalAmount: 25
+  showDemoDescription: true,
+  maxWithdrawalAmount: 25,
+  fastWithdrawal: false,
 };
 
 const DEMOBANK_SETTINGS_KEY = buildStorageKey(
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index abd14848f..0187989af 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -23,6 +23,7 @@ import { State } from "./index.js";
 import { CopyButton } from "../../components/CopyButton.js";
 import { bankUiSettings } from "../../settings.js";
 import { useBusinessAccountDetails } from "../../hooks/circuit.js";
+import { useSettings } from "../../hooks/settings.js";
 
 export function InvalidIbanView({ error }: State.InvalidIban) {
   return (
@@ -78,9 +79,58 @@ function ImportantMessage(): VNode {
 
 }
 
+function ShowDemoInfo():VNode {
+  const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings();
+  if (!settings.showDemoDescription) return <Fragment />
+  return <div class="rounded-md bg-blue-50 p-4">
+  <div class="flex">
+    <div class="flex-shrink-0">
+      <svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+        <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
+      </svg>
+    </div>
+    <div class="ml-3">
+      <h3 class="text-sm font-bold text-blue-800">
+        <i18n.Translate>This is a demo bank!</i18n.Translate>
+      </h3>
+      <div class="mt-2 text-sm text-blue-700">
+        <p>
+          <i18n.Translate>
+            This part of the demo shows how a bank that supports Taler
+            directly would work. In addition to using your own bank
+            account, you can also see the transaction history of some{" "}
+            <a href="/public-accounts">Public Accounts</a>.
+          </i18n.Translate>
+        </p>
+        <p class="mt-3 text-sm flex justify-end">
+          <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
+            onClick={(e) => {
+              e.preventDefault();
+              updateSettings("showDemoDescription", false);
+            }}
+          >
+            Close
+            <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
+            </svg>
+          </button>
+        </p>
+
+      </div>
+    </div>
+  </div>
+</div>
+}
+
 export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> {
+  const { i18n } = useTranslationContext();
+
   return <Fragment>
     <MaybeBusinessButton account={account} onClick={goToBusinessAccount} />
+
+    <ShowDemoInfo />
+    
     <PaymentOptions limit={limit} />
     <Transactions account={account} />
   </Fragment>;
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 4b23686d6..d1c94135b 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -27,7 +27,6 @@ import { CopyButton, CopyIcon } from "../components/CopyButton.js";
 import logo from "../assets/logo-2021.svg";
 import { useAccountDetails } from "../hooks/access.js";
 
-const IS_PUBLIC_ACCOUNT_ENABLED = false;
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
 
@@ -142,24 +141,40 @@ export function BankFrame({
                         <nav class="flex flex-1 flex-col" aria-label="Sidebar">
                           <ul role="list" class="flex flex-1 flex-col gap-y-7">
                             <li>
-                              <ul role="list" class="-mx-2 space-y-1">
-                                <li>
-                                  <a href="#"
-                                    class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
-                                    onClick={() => {
-                                      backend.logOut();
-                                      setOpen(false)
-                                      updateSettings("currentWithdrawalOperationId", undefined);
-                                    }}
-                                  >
-                                    <svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-                                      <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
-                                    </svg>
-                                    Log out
-                                    {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
-                                  </a>
-                                </li>
-                                <li>
+                              <a href="#"
+                                class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold"
+                                onClick={() => {
+                                  backend.logOut();
+                                  setOpen(false)
+                                  updateSettings("currentWithdrawalOperationId", undefined);
+                                }}
+                              >
+                                <svg class="h-6 w-6 shrink-0 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+                                  <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
+                                </svg>
+                                Log out
+                                {/* <span class="ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-gray-50 px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200" aria-hidden="true">5</span> */}
+                              </a>
+                            </li>
+                            <li class="sm:hidden">
+                              <div class="text-xs font-semibold leading-6 text-gray-400">
+                                <i18n.Translate>Sites</i18n.Translate>
+                              </div>
+                              <ul role="list" class="-mx-2 mt-2 space-y-1">
+                                {bankUiSettings.demoSites.map(([name, url]) => {
+                                  return <li>
+                                    <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
+                                      <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
+                                      <span class="truncate">{name}</span>
+                                    </a>
+                                  </li>
+                                })}
+                              </ul>
+                            </li>
+
+                            <li>
+                              <ul role="list" class="space-y-1">
+                                <li class="mt-2">
                                   <div class="flex items-center justify-between">
                                     <span class="flex flex-grow flex-col">
                                       <span class="text-sm text-black font-medium leading-6 " id="availability-label">
@@ -169,29 +184,43 @@ export function BankFrame({
                                     <button type="button" data-enabled={settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
 
                                       onClick={() => {
-                                        console.log(settings.showWithdrawalSuccess)
                                         updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
                                       }}>
                                       <span aria-hidden="true" data-enabled={settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
                                     </button>
                                   </div>
                                 </li>
-                              </ul>
-                            </li>
-
-
+                                <li class="mt-2">
+                                  <div class="flex items-center justify-between">
+                                    <span class="flex flex-grow flex-col">
+                                      <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+                                        <i18n.Translate>Show demo description</i18n.Translate>
+                                      </span>
+                                    </span>
+                                    <button type="button" data-enabled={settings.showDemoDescription} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
 
-                            <li class="sm:hidden">
-                              <div class="text-xs font-semibold leading-6 text-gray-400">
-                                <i18n.Translate>Sites</i18n.Translate>
-                              </div>
-                              <ul role="list" class="-mx-2 mt-2 space-y-1">
-                                {bankUiSettings.demoSites.map(([name, url]) => {
-                                  return <a href={url} target="_blank" rel="noopener noreferrer" class="text-gray-700 hover:text-indigo-600 hover:bg-gray-100 group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
-                                    <span class="flex h-6 w-6 shrink-0 items-center justify-center rounded-lg border text-[0.625rem] font-medium bg-white text-gray-400 border-gray-200 group-hover:border-indigo-600 group-hover:text-indigo-600">&gt;</span>
-                                    <span class="truncate">{name}</span>
-                                  </a>
-                                })}
+                                      onClick={() => {
+                                        updateSettings("showDemoDescription", !settings.showDemoDescription);
+                                      }}>
+                                      <span aria-hidden="true" data-enabled={settings.showDemoDescription} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+                                    </button>
+                                  </div>
+                                </li>
+                                <li class="mt-2">
+                                  <div class="flex items-center justify-between">
+                                    <span class="flex flex-grow flex-col">
+                                      <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+                                        <i18n.Translate>Use fast withdrawal</i18n.Translate>
+                                      </span>
+                                    </span>
+                                    <button type="button" data-enabled={settings.fastWithdrawal} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
+                                      onClick={() => {
+                                        updateSettings("fastWithdrawal", !settings.fastWithdrawal);
+                                      }}>
+                                      <span aria-hidden="true" data-enabled={settings.fastWithdrawal} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+                                    </button>
+                                  </div>
+                                </li>
                               </ul>
                             </li>
                           </ul>
@@ -240,11 +269,6 @@ export function BankFrame({
     //   >
     //     <a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
     //     <div style="max-width: 50em; margin-left: 2em; margin-right: 2em;">
-    //       <h1>
-    //         <span class="it">
-    //           <a href="/">{bankUiSettings.bankName}</a>
-    //         </span>
-    //       </h1>
     //       {maybeDemoContent(
     //         <p>
     //           {IS_PUBLIC_ACCOUNT_ENABLED ? (
@@ -298,33 +322,6 @@ export function BankFrame({
   );
 }
 
-// function maybeDemoContent(content: VNode): VNode {
-//   if (bankUiSettings.showDemoNav) {
-//     return content;
-//   }
-//   return <Fragment />;
-// }
-
-// export function ErrorBannerFloat({
-//   error,
-//   onClear,
-// }: {
-//   error: ErrorMessage;
-//   onClear?: () => void;
-// }): VNode {
-//   return (
-//     <div
-//       style={{
-//         position: "fixed",
-//         top: 10,
-//         zIndex: 200,
-//         width: "90%",
-//       }}
-//     >
-//       <ErrorBanner error={error} onClear={onClear} />
-//     </div>
-//   );
-// }
 
 function StatusBanner(): VNode {
   const notifs = useNotifications()
@@ -469,5 +466,5 @@ function AccountBalance({ account }: { account: string }): VNode {
     {Amounts.currencyOf(result.data.balance.amount)}
     &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
     {Amounts.stringifyValue(result.data.balance.amount)}
-    </div>
+  </div>
 }
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index 40cc147a6..2acfc9b57 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -52,21 +52,21 @@ const logger = new Logger("AccountPage");
 export function HomePage({
   onRegister,
   account,
-  onPendingOperationFound,
+  // onPendingOperationFound,
   goToBusinessAccount,
 }: {
   account: string,
-  onPendingOperationFound: (id: string) => void;
+  // onPendingOperationFound: (id: string) => void;
   onRegister: () => void;
   goToBusinessAccount: () => void;
 }): VNode {
   const [settings] = useSettings();
   const { i18n } = useTranslationContext();
 
-  if (settings.currentWithdrawalOperationId) {
-    onPendingOperationFound(settings.currentWithdrawalOperationId);
-    return <Loading />;
-  }
+  // if (settings.currentWithdrawalOperationId) {
+  //   onPendingOperationFound(settings.currentWithdrawalOperationId);
+  //   return <Loading />;
+  // }
 
   return (
     <AccountPage
@@ -79,11 +79,9 @@ export function HomePage({
 
 export function WithdrawalOperationPage({
   operationId,
-  onLoadNotOk,
   onContinue,
 }: {
   operationId: string;
-  onLoadNotOk: () => void;
   onContinue: () => void;
 }): VNode {
   //FIXME: libeufin sandbox should return show to create the integration api endpoint
@@ -95,6 +93,7 @@ export function WithdrawalOperationPage({
   });
   const parsedUri = parseWithdrawUri(uri);
   const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings();
 
   if (!parsedUri) {
     notifyError(
@@ -107,8 +106,9 @@ export function WithdrawalOperationPage({
   return (
     <WithdrawalQRCode
       withdrawUri={parsedUri}
-      onContinue={onContinue}
-      onLoadNotOk={onLoadNotOk}
+      onClose={() => {
+        updateSettings("currentWithdrawalOperationId", undefined)
+      }}
     />
   );
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 1728074a3..573f8c769 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -34,80 +34,83 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
   // const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
 
   return (
-    <fieldset>
-      <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
-        <i18n.Translate>Send money to</i18n.Translate>
-      </legend>
+    <div class="mt-2">
 
-      <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
-        {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
-        <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
-          <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
-            setTab("charge-wallet")
-          }} />
-          <span class="flex flex-1">
-            <div class="text-lg mr-2">&#x1F4B5;</div>
-            <span class="flex flex-col">
-              <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
-                <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
-              </span>
-              <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
-                <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
+      <fieldset>
+        <legend class="px-4 text-base font-semibold leading-6 text-gray-900">
+          <i18n.Translate>Send money to</i18n.Translate>
+        </legend>
+
+        <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
+          {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
+          <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+            <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
+              setTab("charge-wallet")
+            }} />
+            <span class="flex flex-1">
+              <div class="text-lg mr-2">&#x1F4B5;</div>
+              <span class="flex flex-col">
+                <span id="project-type-0-label" class="block text-sm font-medium text-gray-900">
+                  <i18n.Translate>a <b>Taler</b> wallet</i18n.Translate>
+                </span>
+                <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                  <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
+                </span>
               </span>
             </span>
-          </span>
-          <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
-          </svg>
-        </label>
+            <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+            </svg>
+          </label>
 
 
-        <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
-          <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
-            setTab("wire-transfer")
-          }} />
-          <span class="flex flex-1">
-          <div class="text-lg mr-2">&#x2194;</div>
-            <span class="flex flex-col">
-              <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
-                <i18n.Translate>another bank account</i18n.Translate>
-              </span>
-              <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
-                <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+          <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
+            <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
+              setTab("wire-transfer")
+            }} />
+            <span class="flex flex-1">
+              <div class="text-lg mr-2">&#x2194;</div>
+              <span class="flex flex-col">
+                <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                  <i18n.Translate>another bank account</i18n.Translate>
+                </span>
+                <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                  <i18n.Translate>Make a wire transfer to an account which you know the address.</i18n.Translate>
+                </span>
               </span>
             </span>
-          </span>
-          <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
-          </svg>
-        </label>
-      </div>
-      {tab === "charge-wallet" && (
-        <WalletWithdrawForm
-          focus
-          limit={limit}
-          onSuccess={(id) => {
-            updateSettings("currentWithdrawalOperationId", id);
-          }}
-          onCancel={() => {
-            setTab(undefined)
-          }}
-        />
-      )}
-      {tab === "wire-transfer" && (
-        <PaytoWireTransferForm
-          focus
-          title={i18n.str`Transfer details`}
-          limit={limit}
-          onSuccess={() => {
-            notifyInfo(i18n.str`Wire transfer created!`);
-          }}
-          onCancel={() => {
-            setTab(undefined)
-          }}
-        />
-      )}
+            <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "wire-transfer" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+            </svg>
+          </label>
+        </div>
+        {tab === "charge-wallet" && (
+          <WalletWithdrawForm
+            focus
+            limit={limit}
+            onSuccess={(id) => {
+              updateSettings("currentWithdrawalOperationId", id);
+            }}
+            onCancel={() => {
+              setTab(undefined)
+            }}
+          />
+        )}
+        {tab === "wire-transfer" && (
+          <PaytoWireTransferForm
+            focus
+            title={i18n.str`Transfer details`}
+            limit={limit}
+            onSuccess={() => {
+              notifyInfo(i18n.str`Wire transfer created!`);
+            }}
+            onCancel={() => {
+              setTab(undefined)
+            }}
+          />
+        )}
 
-    </fieldset>
+      </fieldset>
+    </div>
   )
 }
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 7c1b3bdc5..416c714e2 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -135,3 +135,109 @@ export function QrCodeSection({
     </Fragment>
   );
 }
+
+
+export function QrCodeSectionSimpler({
+  withdrawUri,
+  onAborted,
+}: {
+  withdrawUri: WithdrawUriResult;
+  onAborted: () => void;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  useEffect(() => {
+    //Taler Wallet WebExtension is listening to headers response and tab updates.
+    //In the SPA there is no header response with the Taler URI so
+    //this hack manually triggers the tab update after the QR is in the DOM.
+    // WebExtension will be using
+    // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
+    document.title = `${document.title} ${withdrawUri.withdrawalOperationId}`;
+  }, []);
+  const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
+
+  const { abortWithdrawal } = useAccessAnonAPI();
+
+  async function doAbort() {
+    try {
+      await abortWithdrawal(withdrawUri.withdrawalOperationId);
+      onAborted();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
+
+  return (
+    <Fragment>
+      <div class="bg-white shadow-xl sm:rounded-lg">
+        <div class="p2 ">
+          <h3 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>If you have a Taler wallet installed in this device</i18n.Translate>
+          </h3>
+          <div class="mt-4">
+            <a href={talerWithdrawUri}
+              // class="text-sm font-semibold leading-6 text-gray-900 btn "
+              class="inline-flex items-center  disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+            >
+              <i18n.Translate>Click here to start</i18n.Translate>
+            </a>
+          </div>
+          <div class="mt-4 max-w-xl text-sm text-gray-500">
+            <p><i18n.Translate>
+              You will see the details of the operation in your wallet including the fees (if applies).
+              If you still one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
+            </i18n.Translate></p>
+          </div>
+          <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
+            <div />
+            <button type="button"
+              class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
+              onClick={doAbort}
+            >
+              Cancel withdrawal
+            </button>
+          </div>
+        </div>
+      </div>
+
+      <div class="bg-white shadow-xl sm:rounded-lg mt-8">
+        <div class="px-4 py-5 sm:p-6">
+          <h3 class="text-base font-semibold leading-6 text-gray-900">
+            <i18n.Translate>Or if you have the wallet in another device</i18n.Translate>
+          </h3>
+          <div class="mt-4 max-w-xl text-sm text-gray-500">
+            <i18n.Translate>Scan the QR below to start the withdrawal</i18n.Translate>
+          </div>
+          <div class="mt-2 max-w-md ml-auto mr-auto">
+            <QR text={talerWithdrawUri} />
+          </div>
+        </div>
+        <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+          <button type="button"
+            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
+            onClick={doAbort}
+          >
+            Cancel withdrawal
+          </button>
+        </div>
+      </div>
+
+    </Fragment>
+  );
+}
+
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 8c41f7576..08f706919 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -20,6 +20,7 @@ import {
   HttpStatusCode,
   Logger,
   TranslatedString,
+  WithdrawUriResult,
   parseWithdrawUri,
 } from "@gnu-taler/taler-util";
 import {
@@ -34,6 +35,9 @@ import { useEffect, useRef, useState } from "preact/hooks";
 import { useAccessAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { Amount } from "./PaytoWireTransferForm.js";
+import { useSettings } from "../hooks/settings.js";
+import { WithdrawalOperationState } from "./WithdrawalQRCode.js";
+import { Loading } from "../components/Loading.js";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(Amount);
@@ -51,8 +55,9 @@ export function WalletWithdrawForm({
 }): VNode {
   const { i18n } = useTranslationContext();
   const { createWithdrawal } = useAccessAPI();
+  const [settings, updateSettings] = useSettings()
 
-  const [amountStr, setAmountStr] = useState<string | undefined>("5.00");
+  const [amountStr, setAmountStr] = useState<string | undefined>(`${settings.maxWithdrawalAmount}`);
   const ref = useRef<HTMLInputElement>(null);
   useEffect(() => {
     if (focus) ref.current?.focus();
@@ -78,7 +83,6 @@ export function WalletWithdrawForm({
   async function doStart() {
     if (!parsedAmount) return;
     try {
-      console.log("ASDASD")
       const result = await createWithdrawal({
         amount: Amounts.stringify(parsedAmount),
       });
@@ -109,7 +113,6 @@ export function WalletWithdrawForm({
         )
       }
     }
-
   }
 
   return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
@@ -122,91 +125,103 @@ export function WalletWithdrawForm({
         <i18n.Translate>After using your wallet you will be redirected here to confirm or cancel the operation.</i18n.Translate>
       </p>
     </div>
-    <form
-      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
-      autoCapitalize="none"
-      autoCorrect="off"
-      onSubmit={e => {
-        e.preventDefault()
-      }}
-    >
-      <div class="px-4 py-6 sm:p-8">
-        <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
-          <div class="sm:col-span-5">
-            <label for="withdraw-amount">{i18n.str`Amount`}</label>
-            <RefAmount
-              currency={limit.currency}
-              value={amountStr}
-              name="withdraw-amount"
-              onChange={(v) => {
-                setAmountStr(v);
-              }}
-              error={errors?.amount}
-              ref={ref}
-            />
-          </div>
-          <div class="sm:col-span-5">
-            <span class="isolate inline-flex rounded-md shadow-sm">
-              <button type="button"
-                class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                onClick={(e) => {
-                  e.preventDefault();
-                  setAmountStr("50.00")
-                }}
-              >
-                50.00
-              </button>
-              <button type="button"
-                class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                onClick={(e) => {
-                  e.preventDefault();
-                  setAmountStr("25.00")
-                }}
-              >
-
-                25.00
-              </button>
-              <button type="button"
-                class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                onClick={(e) => {
-                  e.preventDefault();
-                  setAmountStr("10.00")
-                }}
-              >
-                10.00
-              </button>
-              <button type="button"
-                class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                onClick={(e) => {
-                  e.preventDefault();
-                  setAmountStr("5.00")
+
+    {!settings.fastWithdrawal ?
+      <form
+        class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+        autoCapitalize="none"
+        autoCorrect="off"
+        onSubmit={e => {
+          e.preventDefault()
+        }}
+      >
+        <div class="px-4 py-6 sm:p-8">
+          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+            <div class="sm:col-span-5">
+              <label for="withdraw-amount">{i18n.str`Amount`}</label>
+              <RefAmount
+                currency={limit.currency}
+                value={amountStr}
+                name="withdraw-amount"
+                onChange={(v) => {
+                  setAmountStr(v);
                 }}
-              >
-                5.00
-              </button>
-            </span>
-          </div>
+                error={errors?.amount}
+                ref={ref}
+              />
+            </div>
+            <div class="sm:col-span-5">
+              <span class="isolate inline-flex rounded-md shadow-sm">
+                <button type="button"
+                  class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                  onClick={(e) => {
+                    e.preventDefault();
+                    setAmountStr("50.00")
+                  }}
+                >
+                  50.00
+                </button>
+                <button type="button"
+                  class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                  onClick={(e) => {
+                    e.preventDefault();
+                    setAmountStr("25.00")
+                  }}
+                >
+
+                  25.00
+                </button>
+                <button type="button"
+                  class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                  onClick={(e) => {
+                    e.preventDefault();
+                    setAmountStr("10.00")
+                  }}
+                >
+                  10.00
+                </button>
+                <button type="button"
+                  class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+                  onClick={(e) => {
+                    e.preventDefault();
+                    setAmountStr("5.00")
+                  }}
+                >
+                  5.00
+                </button>
+              </span>
+            </div>
 
+          </div>
+        </div>
+        <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+          <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+            onClick={onCancel}
+          >
+            <i18n.Translate>Cancel</i18n.Translate></button>
+          <button type="submit"
+            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+            // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+            onClick={(e) => {
+              e.preventDefault()
+              doStart()
+            }}
+          >
+            <i18n.Translate>Continue</i18n.Translate>
+          </button>
         </div>
-      </div>
-      <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
-        <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
-          onClick={onCancel}
-        >
-          <i18n.Translate>Cancel</i18n.Translate></button>
-        <button type="submit"
-          class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-          // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
-          onClick={(e) => {
-            e.preventDefault()
-            doStart()
-          }}
-        >
-          <i18n.Translate>Continue</i18n.Translate>
-        </button>
-      </div>
 
-    </form>
+      </form>
+      : settings.currentWithdrawalOperationId === undefined ?
+        <Loading /> :
+        <WithdrawalOperationState
+          currentOperation={settings.currentWithdrawalOperationId}
+          currency={limit.currency}
+          onClose={() => {
+            onCancel()
+          }}
+        />
+    }
   </div>
   );
 }
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 2a3a1ec2c..9976babdb 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -18,24 +18,29 @@ import {
   Amounts,
   HttpStatusCode,
   Logger,
+  TranslatedString,
   WithdrawUriResult,
   parsePaytoUri,
+  parseWithdrawUri,
+  stringifyWithdrawUri,
 } from "@gnu-taler/taler-util";
-import { ErrorType, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
-import { useWithdrawalDetails } from "../hooks/access.js";
+import { useAccessAPI, useWithdrawalDetails } from "../hooks/access.js";
 import { useSettings } from "../hooks/settings.js";
 import { handleNotOkResult } from "./HomePage.js";
-import { QrCodeSection } from "./QrCodeSection.js";
+import { QrCodeSection, QrCodeSectionSimpler } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js";
+import { useEffect, useState } from "preact/hooks";
+import { buildRequestErrorMessage } from "../utils.js";
+import { getInitialBackendBaseURL } from "../hooks/backend.js";
 
 const logger = new Logger("WithdrawalQRCode");
 
 interface Props {
   withdrawUri: WithdrawUriResult;
-  onContinue: () => void;
-  onLoadNotOk: () => void;
+  onClose: () => void;
 }
 /**
  * Offer the QR code (and a clickable taler://-link) to
@@ -44,14 +49,9 @@ interface Props {
  */
 export function WithdrawalQRCode({
   withdrawUri,
-  onContinue,
-  onLoadNotOk,
+  onClose,
 }: Props): VNode {
   const [settings, updateSettings] = useSettings();
-  function clearCurrentWithdrawal(): void {
-    updateSettings("currentWithdrawalOperationId", undefined);
-    onContinue();
-  }
   const { i18n } = useTranslationContext();
   const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId);
   if (!result.ok) {
@@ -62,7 +62,7 @@ export function WithdrawalQRCode({
       result.type === ErrorType.CLIENT &&
       result.status === HttpStatusCode.NotFound
     ) {
-      clearCurrentWithdrawal()
+      onClose()
       return <div>operation not found</div>;
     }
     // onLoadNotOk();
@@ -89,8 +89,7 @@ export function WithdrawalQRCode({
           style={{ float: "right" }}
           onClick={async (e) => {
             e.preventDefault();
-            clearCurrentWithdrawal()
-            onContinue()
+            onClose()
           }}>
           {i18n.str`Continue`}
         </a>
@@ -149,8 +148,7 @@ export function WithdrawalQRCode({
           class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
           onClick={async (e) => {
             e.preventDefault();
-            clearCurrentWithdrawal()
-            onContinue()
+            onClose()
           }}>
           <i18n.Translate>Continue</i18n.Translate>
         </button>
@@ -165,8 +163,7 @@ export function WithdrawalQRCode({
         withdrawUri={withdrawUri}
         onAborted={() => {
           notifyInfo(i18n.str`Operation canceled`);
-          clearCurrentWithdrawal()
-          onContinue()
+          onClose()
         }}
       />
     );
@@ -196,9 +193,131 @@ export function WithdrawalQRCode({
       }}
       onAborted={() => {
         notifyInfo(i18n.str`Operation canceled`);
-        clearCurrentWithdrawal()
-        onContinue()
+        onClose()
       }}
     />
   );
+}
+
+
+export function WithdrawalOperationState({
+  currency,
+  currentOperation,
+  onClose,
+}: {currency:string, currentOperation: string, onClose: () => void}): VNode {
+  const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings()
+  const { createWithdrawal } = useAccessAPI();
+
+  const amount = settings.maxWithdrawalAmount
+  async function doSilentStart() {
+    //FIXME: if amount is not enough use balance
+    const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`)
+
+    try {
+      const result = await createWithdrawal({
+        amount: Amounts.stringify(parsedAmount),
+      });
+      const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
+      if (!uri) {
+        return notifyError(
+          i18n.str`Server responded with an invalid withdraw URI`,
+          i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
+      } else {
+        updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId)
+      }
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Forbidden
+                ? i18n.str`The operation was rejected due to insufficient funds`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+  }
+
+  useEffect(() => {
+    doSilentStart()
+  }, [settings.fastWithdrawal, amount])
+
+  const result = useWithdrawalDetails(currentOperation);
+  if (!result.ok) {
+    if (result.loading) {
+      return <Loading />;
+    }
+    if (
+      result.type === ErrorType.CLIENT &&
+      result.status === HttpStatusCode.NotFound
+    ) {
+      onClose()
+      return <div>operation not found</div>;
+    }
+    // onLoadNotOk();
+    return handleNotOkResult(i18n)(result);
+  }
+  const { data } = result;
+
+  const baseUrl = getInitialBackendBaseURL()
+  const uri = stringifyWithdrawUri({
+    bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`,
+    withdrawalOperationId: currentOperation,
+  });
+  const parsedUri = parseWithdrawUri(uri);
+
+  if (data.aborted) {
+    return <div>
+      the operation was aborted, you can create another one
+    </div>
+  }
+
+  if (data.confirmation_done) {
+    return <div>
+      the wire transfer is made, you coin should arrive shortly
+    </div>
+  }
+  if (!parsedUri) {
+    return <div>
+      the operation is not valid, create another one
+    </div>
+  }
+  if (!data.selection_done) {
+    return (
+      <QrCodeSectionSimpler
+        withdrawUri={parsedUri}
+        onAborted={() => {
+          notifyInfo(i18n.str`Operation canceled`);
+          onClose()
+        }}
+      />
+    );
+  }
+
+  if (!data.selected_reserve_pub) {
+    return <div>
+      the exchange is selcted but no reserve pub
+    </div>
+  }
+
+  const account = !data.selected_exchange_account ? undefined : parsePaytoUri(data.selected_exchange_account)
+
+  if (!account) {
+    return <div>
+      the exchange is selected but no account
+    </div>
+  }
+
+  return <div>
+    the operation is wating for the question to be answered
+  </div>;
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 050f1fb8a..1e5370afc 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -61,7 +61,7 @@ export function RemoveAccount({
             </h3>
             <div class="mt-2 text-sm text-yellow-700">
               <p>
-                <i18n.Translate>The account can be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
+                <i18n.Translate>The account can't be delete while still holding some balance. First make sure that the owner make a complete cashout.</i18n.Translate>
               </p>
             </div>
           </div>
-- 
cgit v1.2.3


From a59df74fb2b4374fd58f68fd4abaffe623cd54d6 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 22 Sep 2023 15:29:19 -0300
Subject: more ui

---
 packages/demobank-ui/src/components/Routing.tsx    |  27 +-
 packages/demobank-ui/src/hooks/settings.ts         |   3 +
 .../demobank-ui/src/pages/AccountPage/index.ts     |   2 +
 .../demobank-ui/src/pages/AccountPage/state.ts     |   3 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |   4 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  15 +
 packages/demobank-ui/src/pages/HomePage.tsx        |  17 +-
 .../demobank-ui/src/pages/OperationState/index.ts  |  10 +-
 .../demobank-ui/src/pages/OperationState/state.ts  | 109 ++++++-
 .../demobank-ui/src/pages/OperationState/views.tsx | 363 ++++++++++++++++++++-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  17 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   | 104 ------
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   | 273 ++++++++++------
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     | 163 +--------
 packages/demobank-ui/src/pages/business/Home.tsx   |   1 -
 15 files changed, 693 insertions(+), 418 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index e1fd93737..90d2d4c48 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -45,6 +45,20 @@ export function Routing(): VNode {
             />
           )}
         />
+        <Route
+          path="/operation/:wopid"
+          component={({ wopid }: { wopid: string }) => (
+            <WithdrawalOperationPage
+              operationId={wopid}
+              onContinue={() => {
+                route("/account");
+              }}
+              // onLoadNotOk={() => {
+              //   route("/account");
+              // }}
+            />
+          )}
+        />
         <Route
           path="/register"
           component={() => (
@@ -64,10 +78,6 @@ export function Routing(): VNode {
   return (
     <BankFrame account={backend.state.username}>
       <Router history={history}>
-        <Route
-          path="/test"
-          component={Test}
-        />
         <Route
           path="/operation/:wopid"
           component={({ wopid }: { wopid: string }) => (
@@ -76,9 +86,6 @@ export function Routing(): VNode {
               onContinue={() => {
                 route("/account");
               }}
-              // onLoadNotOk={() => {
-              //   route("/account");
-              // }}
             />
           )}
         />
@@ -108,9 +115,9 @@ export function Routing(): VNode {
             } else {
               return <HomePage
                 account={username}
-                // onPendingOperationFound={(wopid) => {
-                //   route(`/operation/${wopid}`);
-                // }}
+                goToConfirmOperation={(wopid) => {
+                  route(`/operation/${wopid}`);
+                }}
                 goToBusinessAccount={() => {
                   route("/business");
                 }}
diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts
index c2fd93a0c..5f004c6d4 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/settings.ts
@@ -30,6 +30,7 @@ interface Settings {
   currentWithdrawalOperationId: string | undefined;
   showWithdrawalSuccess: boolean;
   showDemoDescription: boolean;
+  showInstallWallet: boolean;
   maxWithdrawalAmount: number;
   fastWithdrawal: boolean;
 }
@@ -39,6 +40,7 @@ export const codecForSettings = (): Codec<Settings> =>
     .property("currentWithdrawalOperationId", codecOptional(codecForString()))
     .property("showWithdrawalSuccess", (codecForBoolean()))
     .property("showDemoDescription", (codecForBoolean()))
+    .property("showInstallWallet", (codecForBoolean()))
     .property("fastWithdrawal", (codecForBoolean()))
     .property("maxWithdrawalAmount", codecForNumber())
     .build("Settings");
@@ -47,6 +49,7 @@ const defaultSettings: Settings = {
   currentWithdrawalOperationId: undefined,
   showWithdrawalSuccess: true,
   showDemoDescription: true,
+  showInstallWallet: true,
   maxWithdrawalAmount: 25,
   fastWithdrawal: false,
 };
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index 128a6d30f..81eeb4a03 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -29,6 +29,7 @@ export interface Props {
     error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
   ) => VNode;
   goToBusinessAccount: () => void;
+  goToConfirmOperation: (id:string) => void;
 }
 
 export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
@@ -54,6 +55,7 @@ export namespace State {
     account: string, 
     limit: AmountJson,
     goToBusinessAccount: () => void;
+    goToConfirmOperation: (id:string) => void;
   }
 
   export interface InvalidIban {
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index a57e19901..1a1475c0d 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -20,7 +20,7 @@ import { useBackendContext } from "../../context/backend.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { Props, State } from "./index.js";
 
-export function useComponentState({ account, goToBusinessAccount }: Props): State {
+export function useComponentState({ account, goToBusinessAccount, goToConfirmOperation }: Props): State {
   const result = useAccountDetails(account);
   const backend = useBackendContext();
   const { i18n } = useTranslationContext();
@@ -75,6 +75,7 @@ export function useComponentState({ account, goToBusinessAccount }: Props): Stat
   return {
     status: "ready",
     goToBusinessAccount,
+    goToConfirmOperation,
     error: undefined,
     account,
     limit,
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 0187989af..23a815bd8 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -123,7 +123,7 @@ function ShowDemoInfo():VNode {
 </div>
 }
 
-export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> {
+export function ReadyView({ account, limit, goToBusinessAccount, goToConfirmOperation }: State.Ready): VNode<{}> {
   const { i18n } = useTranslationContext();
 
   return <Fragment>
@@ -131,7 +131,7 @@ export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready):
 
     <ShowDemoInfo />
     
-    <PaymentOptions limit={limit} />
+    <PaymentOptions limit={limit} goToConfirmOperation={goToConfirmOperation} />
     <Transactions account={account} />
   </Fragment>;
 }
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index d1c94135b..5bfaa63ec 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -206,6 +206,21 @@ export function BankFrame({
                                     </button>
                                   </div>
                                 </li>
+                                <li class="mt-2">
+                                  <div class="flex items-center justify-between">
+                                    <span class="flex flex-grow flex-col">
+                                      <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+                                        <i18n.Translate>Show install wallet first</i18n.Translate>
+                                      </span>
+                                    </span>
+                                    <button type="button" data-enabled={settings.showInstallWallet} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
+                                      onClick={() => {
+                                        updateSettings("showInstallWallet", !settings.showInstallWallet);
+                                      }}>
+                                      <span aria-hidden="true" data-enabled={settings.showInstallWallet} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+                                    </button>
+                                  </div>
+                                </li>
                                 <li class="mt-2">
                                   <div class="flex items-center justify-between">
                                     <span class="flex flex-grow flex-col">
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index 2acfc9b57..8d5e1f3b9 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -36,6 +36,7 @@ import { useSettings } from "../hooks/settings.js";
 import { AccountPage } from "./AccountPage/index.js";
 import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
+import { route } from "preact-router";
 
 const logger = new Logger("AccountPage");
 
@@ -52,25 +53,20 @@ const logger = new Logger("AccountPage");
 export function HomePage({
   onRegister,
   account,
-  // onPendingOperationFound,
+  goToConfirmOperation,
   goToBusinessAccount,
 }: {
   account: string,
-  // onPendingOperationFound: (id: string) => void;
   onRegister: () => void;
   goToBusinessAccount: () => void;
+  goToConfirmOperation: (id:string) => void;
 }): VNode {
-  const [settings] = useSettings();
   const { i18n } = useTranslationContext();
 
-  // if (settings.currentWithdrawalOperationId) {
-  //   onPendingOperationFound(settings.currentWithdrawalOperationId);
-  //   return <Loading />;
-  // }
-
   return (
     <AccountPage
       account={account}
+      goToConfirmOperation={goToConfirmOperation}
       goToBusinessAccount={goToBusinessAccount}
       onLoadNotOk={handleNotOkResult(i18n, onRegister)}
     />
@@ -102,12 +98,13 @@ export function WithdrawalOperationPage({
     );
     return <Loading />;
   }
-
+  
   return (
     <WithdrawalQRCode
       withdrawUri={parsedUri}
       onClose={() => {
         updateSettings("currentWithdrawalOperationId", undefined)
+        onContinue()
       }}
     />
   );
@@ -178,7 +175,7 @@ export function handleNotOkResult(
           assertUnreachable(result);
         }
       }
-
+      route("/")
       return <div>error</div>;
     }
     return <div />;
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts
index 254fcba5f..32302f272 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -26,6 +26,7 @@ import { ErrorLoading } from "../../components/ErrorLoading.js";
 export interface Props {
   currency: string;
   onClose: () => void;
+  goToConfirmOperation: (id: string) => void;
 }
 
 export type State = State.Loading |
@@ -57,26 +58,33 @@ export namespace State {
     error: undefined;
     uri: WithdrawUriResult,
     onClose: () => void;
+    onAbort: () => void;
   }
 
   export interface InvalidPayto {
     status: "invalid-payto",
     error: undefined;
     payto: string | null;
+    onClose: () => void;
   }
   export interface InvalidWithdrawal {
     status: "invalid-withdrawal",
     error: undefined;
+    onClose: () => void;
     uri: string,
   }
   export interface InvalidReserve {
     status: "invalid-reserve",
     error: undefined;
+    onClose: () => void;
     reserve: string | null;
   }
   export interface NeedConfirmation {
     status: "need-confirmation",
+    onAbort: () => void;
+    onConfirm: () => void;
     error: undefined;
+    busy: boolean,
   }
   export interface Aborted {
     status: "aborted",
@@ -111,7 +119,7 @@ const viewMapping: utils.StateViewMap<State> = {
   ready: ReadyView,
 };
 
-export const AccountPage = utils.compose(
+export const OperationState = utils.compose(
   (p: Props) => useComponentState(p),
   viewMapping,
 );
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts
index 6fb7bb28f..ae03ed529 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -15,21 +15,24 @@
  */
 
 import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
-import { ErrorType, RequestError, notify, notifyError, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
+import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
 import { useBackendContext } from "../../context/backend.js";
-import { useAccessAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js";
+import { useAccessAPI, useAccessAnonAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js";
 import { Props, State } from "./index.js";
 import { useSettings } from "../../hooks/settings.js";
-import { buildRequestErrorMessage } from "../../utils.js";
-import { useEffect } from "preact/hooks";
+import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
+import { useEffect, useMemo, useState } from "preact/hooks";
 import { getInitialBackendBaseURL } from "../../hooks/backend.js";
 
-export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> {
+export function useComponentState({ currency, onClose,goToConfirmOperation }: Props): utils.RecursiveState<State> {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
   const { createWithdrawal } = useAccessAPI();
+  const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI();
+  const [busy, setBusy] = useState<Record<string, undefined>>()
 
   const amount = settings.maxWithdrawalAmount
+
   async function doSilentStart() {
     //FIXME: if amount is not enough use balance
     const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`)
@@ -67,12 +70,14 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
     }
   }
 
+  const withdrawalOperationId = settings.currentWithdrawalOperationId
   useEffect(() => {
-    doSilentStart()
+    if (withdrawalOperationId === undefined) {
+      doSilentStart()
+    }
   }, [settings.fastWithdrawal, amount])
 
   const baseUrl = getInitialBackendBaseURL()
-  const withdrawalOperationId = settings.currentWithdrawalOperationId
 
   if (!withdrawalOperationId) {
     return {
@@ -81,6 +86,63 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
     }
   }
 
+  const wid = withdrawalOperationId
+
+  async function doAbort() {
+    try {
+      setBusy({})
+      await abortWithdrawal(wid);
+      onClose();
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
+                : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+    setBusy(undefined)
+  }
+
+  async function doConfirm() {
+    try {
+      setBusy({})
+      await confirmWithdrawal(wid);
+      notifyInfo(i18n.str`Wire transfer completed!`)
+    } catch (error) {
+      if (error instanceof RequestError) {
+        notify(
+          buildRequestErrorMessage(i18n, error.cause, {
+            onClientError: (status) =>
+              status === HttpStatusCode.Conflict
+                ? i18n.str`The withdrawal has been aborted previously and can't be confirmed`
+                : status === HttpStatusCode.UnprocessableEntity
+                  ? i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`
+                  : undefined,
+          }),
+        );
+      } else {
+        notifyError(
+          i18n.str`Operation failed, please report`,
+          (error instanceof Error
+            ? error.message
+            : JSON.stringify(error)) as TranslatedString
+        )
+      }
+    }
+    setBusy(undefined)
+  }  
   const bankIntegrationApiBaseUrl = `${baseUrl}/integration-api`
   const uri = stringifyWithdrawUri({
     bankIntegrationApiBaseUrl,
@@ -92,11 +154,13 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
       status: "invalid-withdrawal",
       error: undefined,
       uri,
+      onClose,
     }
   }
 
   return (): utils.RecursiveState<State> => {
     const result = useWithdrawalDetails(withdrawalOperationId);
+
     if (!result.ok) {
       if (result.loading) {
         return {
@@ -119,10 +183,17 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
     }
 
     if (data.confirmation_done) {
+      if (!settings.showWithdrawalSuccess) {
+        updateSettings("currentWithdrawalOperationId", undefined)
+        onClose()
+      }
       return {
         status: "confirmed",
         error: undefined,
-        onClose,
+        onClose: async () => {
+          updateSettings("currentWithdrawalOperationId", undefined)
+          onClose()
+        },
       }
     }
 
@@ -131,7 +202,12 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
         status: "ready",
         error: undefined,
         uri: parsedUri,
-        onClose
+        onClose: async () => {
+          await doAbort()
+          updateSettings("currentWithdrawalOperationId", undefined)
+          onClose()
+        },
+        onAbort: doAbort,
       }
     }
 
@@ -139,7 +215,8 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
       return {
         status: "invalid-reserve",
         error: undefined,
-        reserve: data.selected_reserve_pub
+        reserve: data.selected_reserve_pub,
+        onClose,
       }
     }
 
@@ -149,13 +226,23 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive
       return {
         status: "invalid-payto",
         error: undefined,
-        payto: data.selected_exchange_account
+        payto: data.selected_exchange_account,
+        onClose,
       }
     }
 
+
+    // goToConfirmOperation(withdrawalOperationId)
     return {
       status: "need-confirmation",
       error: undefined,
+      onAbort: async () => {
+        await doAbort()
+        updateSettings("currentWithdrawalOperationId", undefined)
+        onClose()
+      },
+      busy: !!busy,
+      onConfirm: doConfirm
     }
   }
 
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index db25eaf61..17f1d8457 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import { Amounts, stringifyPaytoUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { Transactions } from "../../components/Transactions/index.js";
@@ -24,42 +24,375 @@ import { CopyButton } from "../../components/CopyButton.js";
 import { bankUiSettings } from "../../settings.js";
 import { useBusinessAccountDetails } from "../../hooks/circuit.js";
 import { useSettings } from "../../hooks/settings.js";
+import { useEffect, useMemo, useState } from "preact/hooks";
+import { undefinedIfEmpty } from "../../utils.js";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { QR } from "../../components/QR.js";
 
-export function InvalidPaytoView({ error }: State.InvalidPayto) {
+export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) {
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div>Payto from server is not valid &quot;{payto}&quot;</div>
   );
 }
-export function InvalidWithdrawalView({ error }: State.InvalidWithdrawal) {
+export function InvalidWithdrawalView({ uri, onClose }: State.InvalidWithdrawal) {
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div>Withdrawal uri from server is not valid &quot;{uri}&quot;</div>
   );
 }
-export function InvalidReserveView({ error }: State.InvalidReserve) {
+export function InvalidReserveView({ reserve, onClose }: State.InvalidReserve) {
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div>Reserve from server is not valid &quot;{reserve}&quot;</div>
   );
 }
 
-export function NeedConfirmationView({ error }: State.NeedConfirmation) {
+export function NeedConfirmationView({ error, onAbort, onConfirm, busy }: State.NeedConfirmation) {
+  const { i18n } = useTranslationContext()
+
+  const captchaNumbers = useMemo(() => {
+    return {
+      a: Math.floor(Math.random() * 10),
+      b: Math.floor(Math.random() * 10),
+    };
+  }, []);
+  const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
+  const answer = parseInt(captchaAnswer ?? "", 10);
+  const errors = undefinedIfEmpty({
+    answer: !captchaAnswer
+      ? i18n.str`Answer the question before continue`
+      : Number.isNaN(answer)
+        ? i18n.str`The answer should be a number`
+        : answer !== captchaNumbers.a + captchaNumbers.b
+          ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
+          : undefined,
+  }) ?? (busy ? {} as Record<string, undefined> : undefined);
+
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div class="bg-white shadow sm:rounded-lg">
+      <div class="px-4 py-5 sm:p-6">
+        <h3 class="text-base font-semibold text-gray-900">
+          <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
+        </h3>
+        <div class="mt-2 max-w-xl text-sm text-gray-500">
+          <div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-4 sm:gap-x-3">
+
+            <label class={"relative sm:col-span-2 flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-noneborder-indigo-600 ring-2 ring-indigo-600"}>
+              <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" />
+              <span class="flex flex-1">
+                <span class="flex flex-col">
+                  <span id="project-type-0-label" class="block text-sm font-medium text-gray-900 ">
+                    <i18n.Translate>challenge response test</i18n.Translate>
+                  </span>
+                </span>
+              </span>
+              <svg class="h-5 w-5 text-indigo-600" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+                <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+              </svg>
+            </label>
+
+
+            <label class="relative flex cursor-pointer rounded-lg border bg-gray-100  p-4 shadow-sm focus:outline-none border-gray-300">
+              <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" />
+              <span class="flex flex-1">
+                <span class="flex flex-col">
+                  <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                    <i18n.Translate>using SMS</i18n.Translate>
+                  </span>
+                  <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                    <i18n.Translate>not available</i18n.Translate>
+                  </span>
+                </span>
+              </span>
+              <svg class="h-5 w-5 text-indigo-600 hidden" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+                <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+              </svg>
+            </label>
+
+            <label class="relative flex cursor-pointer rounded-lg border bg-gray-100 p-4 shadow-sm focus:outline-none border-gray-300">
+              <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" />
+              <span class="flex flex-1">
+                <span class="flex flex-col">
+                  <span id="project-type-1-label" class="block text-sm font-medium text-gray-900">
+                    <i18n.Translate>one time password</i18n.Translate>
+                  </span>
+                  <span id="project-type-1-description-0" class="mt-1 flex items-center text-sm text-gray-500">
+                    <i18n.Translate>not available</i18n.Translate>
+                  </span>
+                </span>
+              </span>
+              <svg class="h-5 w-5 text-indigo-600 hidden" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+                <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+              </svg>
+            </label>
+          </div>
+        </div>
+        <div class="mt-3 text-sm leading-6">
+
+          <form
+            class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
+            autoCapitalize="none"
+            autoCorrect="off"
+            onSubmit={e => {
+              e.preventDefault()
+            }}
+          >
+            <div class="px-4 py-6 sm:p-8">
+              <label for="withdraw-amount">{i18n.str`What is`}&nbsp;
+                <em>
+                  {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
+                </em>
+                ?
+              </label>
+              <div class="mt-2">
+                <div class="relative rounded-md shadow-sm">
+                  <input
+                    type="text"
+                    // class="block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                    aria-describedby="answer"
+                    autoFocus
+                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                    value={captchaAnswer ?? ""}
+                    required
+
+                    name="answer"
+                    id="answer"
+                    autocomplete="off"
+                    onChange={(e): void => {
+                      setCaptchaAnswer(e.currentTarget.value)
+                    }}
+                  />
+                </div>
+                <ShowInputErrorLabel message={errors?.answer} isDirty={captchaAnswer !== undefined} />
+              </div>
+            </div>
+            <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+              <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+                onClick={onAbort}
+              >
+                <i18n.Translate>Cancel</i18n.Translate></button>
+              <button type="submit"
+                class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+                disabled={!!errors}
+                onClick={(e) => {
+                  e.preventDefault()
+                  onConfirm()
+                }}
+              >
+                <i18n.Translate>Transfer</i18n.Translate>
+              </button>
+            </div>
+
+          </form>
+        </div>
+        <div class="px-4 mt-4 ">
+          {/* <div class="w-full">
+          <div class="px-4 sm:px-0 text-sm">
+            <p><i18n.Translate>Wire transfer details</i18n.Translate></p>
+          </div>
+          <div class="mt-6 border-t border-gray-100">
+            <dl class="divide-y divide-gray-100">
+              {((): VNode => {
+                switch (details.account.targetType) {
+                  case "iban": {
+                    const p = details.account as PaytoUriIBAN
+                    const name = p.params["receiver-name"]
+                    return <Fragment>
+                      <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                        <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
+                        <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.iban}</dd>
+                      </div>
+                      {name &&
+                        <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                          <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
+                          <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
+                        </div>
+                      }
+                    </Fragment>
+                  }
+                  case "x-taler-bank": {
+                    const p = details.account as PaytoUriTalerBank
+                    const name = p.params["receiver-name"]
+                    return <Fragment>
+                      <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                        <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
+                        <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.account}</dd>
+                      </div>
+                      {name &&
+                        <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                          <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
+                          <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
+                        </div>
+                      }
+                    </Fragment>
+                  }
+                  default:
+                    return <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                      <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
+                      <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{details.account.targetPath}</dd>
+                    </div>
+
+                }
+              })()}
+              <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                <dt class="text-sm font-medium leading-6 text-gray-900">Withdrawal identification</dt>
+                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0 break-words">{details.reserve}</dd>
+              </div>
+              <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
+                <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
+                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">To be added</dd>
+                // {/* Amounts.stringifyValue(details.amount) 
+              </div>
+            </dl>
+          </div>
+        </div> */}
+
+        </div>
+      </div>
+    </div>
+
   );
 }
-export function AbortedView({ error }: State.Aborted) {
+export function AbortedView({ error, onClose }: State.Aborted) {
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div>aborted</div>
   );
 }
-export function ConfirmedView({ error }: State.Confirmed) {
+export function ConfirmedView({ error, onClose }: State.Confirmed) {
+  const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings()
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <Fragment>
+
+      <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white p-4 text-left shadow-xl transition-all ">
+
+        <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
+          <svg class="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+            <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
+          </svg>
+        </div>
+        <div class="mt-3 text-center sm:mt-5">
+          <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
+            <i18n.Translate>Withdrawal OK</i18n.Translate>
+          </h3>
+          <div class="mt-2">
+            <p class="text-sm text-gray-500">
+              <i18n.Translate>
+                The wire transfer to the Taler exchange bank's account is completed, now the
+                exchange will send the requested amount into your GNU Taler wallet.
+              </i18n.Translate>
+            </p>
+          </div>
+        </div>
+      </div>
+      <div class="mt-4">
+        <div class="flex items-center justify-between">
+          <span class="flex flex-grow flex-col">
+            <span class="text-sm text-black font-medium leading-6 " id="availability-label">
+              <i18n.Translate>Do not show this again</i18n.Translate>
+            </span>
+          </span>
+          <button type="button" data-enabled={!settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
+            onClick={() => {
+              updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
+            }}>
+            <span aria-hidden="true" data-enabled={!settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
+          </button>
+        </div>
+      </div>
+      <div class="mt-5 sm:mt-6">
+        <button type="button"
+          class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+          onClick={async (e) => {
+            e.preventDefault();
+            onClose()
+          }}>
+          <i18n.Translate>Close</i18n.Translate>
+        </button>
+      </div>
+    </Fragment>
+
   );
 }
 
-export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> {
+export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
   const { i18n } = useTranslationContext();
 
-  return <div />
+  useEffect(() => {
+    //Taler Wallet WebExtension is listening to headers response and tab updates.
+    //In the SPA there is no header response with the Taler URI so
+    //this hack manually triggers the tab update after the QR is in the DOM.
+    // WebExtension will be using
+    // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
+    document.title = `${document.title} ${uri.withdrawalOperationId}`;
+  }, []);
+  const talerWithdrawUri = stringifyWithdrawUri(uri);
+  const [show, setShow] = useState(false)
+  return <Fragment>
+
+    <div class="bg-white shadow sm:rounded-lg mt-4">
+      <div class="p-4">
+        <h3 class="text-base font-semibold leading-6 text-gray-900">
+          <i18n.Translate>On this device</i18n.Translate>
+        </h3>
+        <div class="mt-2 sm:flex sm:items-start sm:justify-between">
+          <div class="max-w-xl text-sm text-gray-500">
+            <p>
+              <i18n.Translate>If you are using a desktop browser you can open the popup now or click the link if you have the "Inject Taler support" option enabled.</i18n.Translate>
+            </p>
+          </div>
+          <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
+            <a href={talerWithdrawUri}
+              class="inline-flex items-center  disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+            >
+              <i18n.Translate>Start</i18n.Translate>
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="bg-white shadow sm:rounded-lg mt-2">
+      <div class="p-4">
+        <h3 class="text-base font-semibold leading-6 text-gray-900">
+          <i18n.Translate>On a mobile phone</i18n.Translate>
+        </h3>
+        <div class="mt-2 sm:flex sm:items-start sm:justify-between">
+          <div class="max-w-xl text-sm text-gray-500">
+            <p>
+              <i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate>
+            </p>
+          </div>
+          <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
+            <button type="button"
+              class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
+              onClick={() => {
+                setShow(!show)
+              }}
+            >
+              {!show ?
+                <i18n.Translate>Show QR</i18n.Translate>
+                :
+                <i18n.Translate>Hide QR</i18n.Translate>
+              }
+            </button>
+          </div>
+        </div>
+        {show &&
+          <div class="mt-2 max-w-md ml-auto mr-auto">
+            <QR text={talerWithdrawUri} />
+          </div>
+        }
+      </div>
+    </div>
+
+    <div class="flex justify-end mt-4">
+      <button type="button"
+        class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
+        onClick={() => {
+          onClose()
+        }}
+      >
+        Cancel
+      </button>
+    </div>
+  </Fragment>
 
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 573f8c769..2830f5c1e 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -26,12 +26,11 @@ import { useSettings } from "../hooks/settings.js";
  * Let the user choose a payment option,
  * then specify the details trigger the action.
  */
-export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
+export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJson, goToConfirmOperation: (id: string) => void }): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings();
+  const [settings] = useSettings();
 
   const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
-  // const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined);
 
   return (
     <div class="mt-2">
@@ -56,6 +55,14 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
                 <span id="project-type-0-description-0" class="mt-1 flex items-center text-sm text-gray-500">
                   <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
                 </span>
+                {!!settings.currentWithdrawalOperationId &&
+                  <span class="inline-flex items-center gap-x-1.5 rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-700">
+                    <svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true">
+                      <circle cx="3" cy="3" r="3" />
+                    </svg>
+                    <i18n.Translate>Operation in progress</i18n.Translate>
+                  </span>
+                }
               </span>
             </span>
             <svg class="h-5 w-5 text-indigo-600" style={{ visibility: tab === "charge-wallet" ? "visible" : "hidden" }} viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
@@ -88,9 +95,7 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode {
           <WalletWithdrawForm
             focus
             limit={limit}
-            onSuccess={(id) => {
-              updateSettings("currentWithdrawalOperationId", id);
-            }}
+            goToConfirmOperation={goToConfirmOperation}
             onCancel={() => {
               setTab(undefined)
             }}
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 416c714e2..0a5a386ae 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -137,107 +137,3 @@ export function QrCodeSection({
 }
 
 
-export function QrCodeSectionSimpler({
-  withdrawUri,
-  onAborted,
-}: {
-  withdrawUri: WithdrawUriResult;
-  onAborted: () => void;
-}): VNode {
-  const { i18n } = useTranslationContext();
-  useEffect(() => {
-    //Taler Wallet WebExtension is listening to headers response and tab updates.
-    //In the SPA there is no header response with the Taler URI so
-    //this hack manually triggers the tab update after the QR is in the DOM.
-    // WebExtension will be using
-    // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
-    document.title = `${document.title} ${withdrawUri.withdrawalOperationId}`;
-  }, []);
-  const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
-
-  const { abortWithdrawal } = useAccessAnonAPI();
-
-  async function doAbort() {
-    try {
-      await abortWithdrawal(withdrawUri.withdrawalOperationId);
-      onAborted();
-    } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
-                : undefined,
-          }),
-        );
-      } else {
-        notifyError(
-          i18n.str`Operation failed, please report`,
-          (error instanceof Error
-            ? error.message
-            : JSON.stringify(error)) as TranslatedString
-        )
-      }
-    }
-  }
-
-  return (
-    <Fragment>
-      <div class="bg-white shadow-xl sm:rounded-lg">
-        <div class="p2 ">
-          <h3 class="text-base font-semibold leading-6 text-gray-900">
-            <i18n.Translate>If you have a Taler wallet installed in this device</i18n.Translate>
-          </h3>
-          <div class="mt-4">
-            <a href={talerWithdrawUri}
-              // class="text-sm font-semibold leading-6 text-gray-900 btn "
-              class="inline-flex items-center  disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-            >
-              <i18n.Translate>Click here to start</i18n.Translate>
-            </a>
-          </div>
-          <div class="mt-4 max-w-xl text-sm text-gray-500">
-            <p><i18n.Translate>
-              You will see the details of the operation in your wallet including the fees (if applies).
-              If you still one you can install it from <a class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">here</a>.
-            </i18n.Translate></p>
-          </div>
-          <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 pt-2 mt-2 ">
-            <div />
-            <button type="button"
-              class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
-              onClick={doAbort}
-            >
-              Cancel withdrawal
-            </button>
-          </div>
-        </div>
-      </div>
-
-      <div class="bg-white shadow-xl sm:rounded-lg mt-8">
-        <div class="px-4 py-5 sm:p-6">
-          <h3 class="text-base font-semibold leading-6 text-gray-900">
-            <i18n.Translate>Or if you have the wallet in another device</i18n.Translate>
-          </h3>
-          <div class="mt-4 max-w-xl text-sm text-gray-500">
-            <i18n.Translate>Scan the QR below to start the withdrawal</i18n.Translate>
-          </div>
-          <div class="mt-2 max-w-md ml-auto mr-auto">
-            <QR text={talerWithdrawUri} />
-          </div>
-        </div>
-        <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
-          <button type="button"
-            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600"
-            onClick={doAbort}
-          >
-            Cancel withdrawal
-          </button>
-        </div>
-      </div>
-
-    </Fragment>
-  );
-}
-
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 08f706919..8dbdd9da6 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -36,33 +36,52 @@ import { useAccessAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { Amount } from "./PaytoWireTransferForm.js";
 import { useSettings } from "../hooks/settings.js";
-import { WithdrawalOperationState } from "./WithdrawalQRCode.js";
-import { Loading } from "../components/Loading.js";
+import { OperationState } from "./OperationState/index.js";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(Amount);
 
-export function WalletWithdrawForm({
-  focus,
-  limit,
-  onSuccess,
-  onCancel,
-}: {
+
+function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
   limit: AmountJson;
   focus?: boolean;
-  onSuccess: (operationId: string) => void;
+  goToConfirmOperation: (operationId: string) => void;
   onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const { createWithdrawal } = useAccessAPI();
   const [settings, updateSettings] = useSettings()
 
+  const { createWithdrawal } = useAccessAPI();
   const [amountStr, setAmountStr] = useState<string | undefined>(`${settings.maxWithdrawalAmount}`);
   const ref = useRef<HTMLInputElement>(null);
   useEffect(() => {
     if (focus) ref.current?.focus();
   }, [focus]);
 
+  if (!!settings.currentWithdrawalOperationId) {
+    return <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
+      <div class="flex">
+        <div class="flex-shrink-0">
+          <svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
+          </svg>
+        </div>
+        <div class="ml-3">
+          <h3 class="text-sm font-bold text-yellow-800">
+            <i18n.Translate>There is an operation already</i18n.Translate>
+          </h3>
+          <div class="mt-2 text-sm text-yellow-700">
+            <p>
+              <i18n.Translate>
+                To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
+              </i18n.Translate>
+            </p>
+        </div>
+      </div>
+    </div>
+  </div >
+  }
+
   const trimmedAmountStr = amountStr?.trim();
 
   const parsedAmount = trimmedAmountStr
@@ -92,7 +111,8 @@ export function WalletWithdrawForm({
           i18n.str`Server responded with an invalid  withdraw URI`,
           i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
       } else {
-        onSuccess(uri.withdrawalOperationId);
+        updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId)
+        goToConfirmOperation(uri.withdrawalOperationId);
       }
     } catch (error) {
       if (error instanceof RequestError) {
@@ -115,113 +135,168 @@ export function WalletWithdrawForm({
     }
   }
 
+  return <form
+    class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2 mt-4"
+    autoCapitalize="none"
+    autoCorrect="off"
+    onSubmit={e => {
+      e.preventDefault()
+    }}
+  >
+    <div class="px-4 py-6 sm:p-8">
+      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+        <div class="sm:col-span-5">
+          <label for="withdraw-amount">{i18n.str`Amount`}</label>
+          <RefAmount
+            currency={limit.currency}
+            value={amountStr}
+            name="withdraw-amount"
+            onChange={(v) => {
+              setAmountStr(v);
+            }}
+            error={errors?.amount}
+            ref={ref}
+          />
+        </div>
+        <div class="sm:col-span-5">
+          <span class="isolate inline-flex rounded-md shadow-sm">
+            <button type="button"
+              class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+              onClick={(e) => {
+                e.preventDefault();
+                setAmountStr("50.00")
+              }}
+            >
+              50.00
+            </button>
+            <button type="button"
+              class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+              onClick={(e) => {
+                e.preventDefault();
+                setAmountStr("25.00")
+              }}
+            >
+
+              25.00
+            </button>
+            <button type="button"
+              class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+              onClick={(e) => {
+                e.preventDefault();
+                setAmountStr("10.00")
+              }}
+            >
+              10.00
+            </button>
+            <button type="button"
+              class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+              onClick={(e) => {
+                e.preventDefault();
+                setAmountStr("5.00")
+              }}
+            >
+              5.00
+            </button>
+          </span>
+        </div>
+
+      </div>
+    </div>
+    <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
+      <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
+        onClick={onCancel}
+      >
+        <i18n.Translate>Cancel</i18n.Translate></button>
+      <button type="submit"
+        class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+        // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
+        onClick={(e) => {
+          e.preventDefault()
+          doStart()
+        }}
+      >
+        <i18n.Translate>Continue</i18n.Translate>
+      </button>
+    </div>
+
+  </form>
+}
+
+
+export function WalletWithdrawForm({
+  focus,
+  limit,
+  onCancel,
+  goToConfirmOperation,
+}: {
+  limit: AmountJson;
+  focus?: boolean;
+  goToConfirmOperation: (operationId: string) => void;
+  onCancel: () => void;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const [settings, updateSettings] = useSettings()
+
   return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
     <div class="px-4 sm:px-0">
       <h2 class="text-base font-semibold leading-7 text-gray-900"><i18n.Translate>Prepare your wallet</i18n.Translate></h2>
       <p class="mt-1 text-sm text-gray-500">
-        <i18n.Translate>Upon starting you will receive the money in your digital wallet, if you don't have one please <a target="_blank" rel="noreferrer noopener" class="font-semibold text-gray-500 hover:text-gray-400" href="https://taler.net/en/wallet.html">install one from here</a></i18n.Translate>.
-      </p>
-      <p class="mt-1 text-sm text-gray-500">
-        <i18n.Translate>After using your wallet you will be redirected here to confirm or cancel the operation.</i18n.Translate>
+        <i18n.Translate>After using your wallet you will confirm or cancel the operation.</i18n.Translate>
       </p>
     </div>
 
-    {!settings.fastWithdrawal ?
-      <form
-        class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
-        autoCapitalize="none"
-        autoCorrect="off"
-        onSubmit={e => {
-          e.preventDefault()
-        }}
-      >
-        <div class="px-4 py-6 sm:p-8">
-          <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
-            <div class="sm:col-span-5">
-              <label for="withdraw-amount">{i18n.str`Amount`}</label>
-              <RefAmount
-                currency={limit.currency}
-                value={amountStr}
-                name="withdraw-amount"
-                onChange={(v) => {
-                  setAmountStr(v);
-                }}
-                error={errors?.amount}
-                ref={ref}
-              />
-            </div>
-            <div class="sm:col-span-5">
-              <span class="isolate inline-flex rounded-md shadow-sm">
-                <button type="button"
-                  class="relative               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
+    <div class="col-span-2">
+      {settings.showInstallWallet && <div class="rounded-md bg-blue-50 ring-blue-2 ring-2 p-4">
+        <div class="flex">
+          <div class="flex-shrink-0">
+            <svg class="h-5 w-5 text-blue-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
+            </svg>
+          </div>
+          <div class="ml-3">
+            <h3 class="text-sm font-bold text-blue-800">
+              <i18n.Translate>You need a GNU Taler Wallet</i18n.Translate>
+            </h3>
+            <div class="mt-2 text-sm text-blue-700">
+              <p>
+                <i18n.Translate>
+                  If you dont have one yet you can follow the instruction <a target="_blank" rel="noreferrer noopener" class="font-semibold text-blue-700 hover:text-blue-600" href="https://taler.net/en/wallet.html">here</a>
+                </i18n.Translate>
+              </p>
+              <p class="mt-3 text-sm flex justify-end">
+                <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
                   onClick={(e) => {
                     e.preventDefault();
-                    setAmountStr("50.00")
+                    updateSettings("showInstallWallet", false);
                   }}
                 >
-                  50.00
+                  I know
+                  <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+                    <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
+                  </svg>
                 </button>
-                <button type="button"
-                  class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                  onClick={(e) => {
-                    e.preventDefault();
-                    setAmountStr("25.00")
-                  }}
-                >
+              </p>
 
-                  25.00
-                </button>
-                <button type="button"
-                  class="relative -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                  onClick={(e) => {
-                    e.preventDefault();
-                    setAmountStr("10.00")
-                  }}
-                >
-                  10.00
-                </button>
-                <button type="button"
-                  class="relative               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
-                  onClick={(e) => {
-                    e.preventDefault();
-                    setAmountStr("5.00")
-                  }}
-                >
-                  5.00
-                </button>
-              </span>
             </div>
-
           </div>
         </div>
-        <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
-          <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
-            onClick={onCancel}
-          >
-            <i18n.Translate>Cancel</i18n.Translate></button>
-          <button type="submit"
-            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-            // disabled={isRawPayto ? !!errorsPayto : !!errorsWire}
-            onClick={(e) => {
-              e.preventDefault()
-              doStart()
-            }}
-          >
-            <i18n.Translate>Continue</i18n.Translate>
-          </button>
-        </div>
+      </div>}
 
-      </form>
-      : settings.currentWithdrawalOperationId === undefined ?
-        <Loading /> :
-        <WithdrawalOperationState
-          currentOperation={settings.currentWithdrawalOperationId}
+      {!settings.fastWithdrawal ?
+        <OldWithdrawalForm
+          focus={focus}
+          limit={limit}
+          onCancel={onCancel}
+          goToConfirmOperation={goToConfirmOperation}
+        />
+        :
+        <OperationState
           currency={limit.currency}
-          onClose={() => {
-            onCancel()
-          }}
+          onClose={onCancel}
+          goToConfirmOperation={goToConfirmOperation}
         />
-    }
+      }
+    </div>
   </div>
   );
 }
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 9976babdb..25c571e28 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -18,23 +18,17 @@ import {
   Amounts,
   HttpStatusCode,
   Logger,
-  TranslatedString,
   WithdrawUriResult,
-  parsePaytoUri,
-  parseWithdrawUri,
-  stringifyWithdrawUri,
+  parsePaytoUri
 } from "@gnu-taler/taler-util";
-import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ErrorType, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
-import { useAccessAPI, useWithdrawalDetails } from "../hooks/access.js";
+import { useWithdrawalDetails } from "../hooks/access.js";
 import { useSettings } from "../hooks/settings.js";
 import { handleNotOkResult } from "./HomePage.js";
-import { QrCodeSection, QrCodeSectionSimpler } from "./QrCodeSection.js";
+import { QrCodeSection } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js";
-import { useEffect, useState } from "preact/hooks";
-import { buildRequestErrorMessage } from "../utils.js";
-import { getInitialBackendBaseURL } from "../hooks/backend.js";
 
 const logger = new Logger("WithdrawalQRCode");
 
@@ -54,18 +48,11 @@ export function WithdrawalQRCode({
   const [settings, updateSettings] = useSettings();
   const { i18n } = useTranslationContext();
   const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId);
+
   if (!result.ok) {
     if (result.loading) {
       return <Loading />;
     }
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    ) {
-      onClose()
-      return <div>operation not found</div>;
-    }
-    // onLoadNotOk();
     return handleNotOkResult(i18n)(result);
   }
   const { data } = result;
@@ -127,22 +114,6 @@ export function WithdrawalQRCode({
           </div>
         </div>
       </div>
-      <div class="mt-4">
-        <div class="flex items-center justify-between">
-          <span class="flex flex-grow flex-col">
-            <span class="text-sm text-black font-medium leading-6 " id="availability-label">
-              <i18n.Translate>Do not show this again</i18n.Translate>
-            </span>
-          </span>
-          <button type="button" data-enabled={!settings.showWithdrawalSuccess} class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" aria-labelledby="availability-label" aria-describedby="availability-description"
-
-            onClick={() => {
-              updateSettings("showWithdrawalSuccess", !settings.showWithdrawalSuccess);
-            }}>
-            <span aria-hidden="true" data-enabled={!settings.showWithdrawalSuccess} class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
-          </button>
-        </div>
-      </div>
       <div class="mt-5 sm:mt-6">
         <button type="button"
           class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
@@ -182,7 +153,6 @@ export function WithdrawalQRCode({
       the exchange is selcted but no account
     </div>
   }
-
   return (
     <WithdrawalConfirmationQuestion
       withdrawUri={withdrawUri}
@@ -198,126 +168,3 @@ export function WithdrawalQRCode({
     />
   );
 }
-
-
-export function WithdrawalOperationState({
-  currency,
-  currentOperation,
-  onClose,
-}: {currency:string, currentOperation: string, onClose: () => void}): VNode {
-  const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings()
-  const { createWithdrawal } = useAccessAPI();
-
-  const amount = settings.maxWithdrawalAmount
-  async function doSilentStart() {
-    //FIXME: if amount is not enough use balance
-    const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`)
-
-    try {
-      const result = await createWithdrawal({
-        amount: Amounts.stringify(parsedAmount),
-      });
-      const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
-      if (!uri) {
-        return notifyError(
-          i18n.str`Server responded with an invalid withdraw URI`,
-          i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
-      } else {
-        updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId)
-      }
-    } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Forbidden
-                ? i18n.str`The operation was rejected due to insufficient funds`
-                : undefined,
-          }),
-        );
-      } else {
-        notifyError(
-          i18n.str`Operation failed, please report`,
-          (error instanceof Error
-            ? error.message
-            : JSON.stringify(error)) as TranslatedString
-        )
-      }
-    }
-  }
-
-  useEffect(() => {
-    doSilentStart()
-  }, [settings.fastWithdrawal, amount])
-
-  const result = useWithdrawalDetails(currentOperation);
-  if (!result.ok) {
-    if (result.loading) {
-      return <Loading />;
-    }
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    ) {
-      onClose()
-      return <div>operation not found</div>;
-    }
-    // onLoadNotOk();
-    return handleNotOkResult(i18n)(result);
-  }
-  const { data } = result;
-
-  const baseUrl = getInitialBackendBaseURL()
-  const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`,
-    withdrawalOperationId: currentOperation,
-  });
-  const parsedUri = parseWithdrawUri(uri);
-
-  if (data.aborted) {
-    return <div>
-      the operation was aborted, you can create another one
-    </div>
-  }
-
-  if (data.confirmation_done) {
-    return <div>
-      the wire transfer is made, you coin should arrive shortly
-    </div>
-  }
-  if (!parsedUri) {
-    return <div>
-      the operation is not valid, create another one
-    </div>
-  }
-  if (!data.selection_done) {
-    return (
-      <QrCodeSectionSimpler
-        withdrawUri={parsedUri}
-        onAborted={() => {
-          notifyInfo(i18n.str`Operation canceled`);
-          onClose()
-        }}
-      />
-    );
-  }
-
-  if (!data.selected_reserve_pub) {
-    return <div>
-      the exchange is selcted but no reserve pub
-    </div>
-  }
-
-  const account = !data.selected_exchange_account ? undefined : parsePaytoUri(data.selected_exchange_account)
-
-  if (!account) {
-    return <div>
-      the exchange is selected but no account
-    </div>
-  }
-
-  return <div>
-    the operation is wating for the question to be answered
-  </div>;
-}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index 318a4cfda..f5f77a3ea 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -360,7 +360,6 @@ function CreateCashout({
                 type="checkbox"
                 name="asd"
                 onChange={(e): void => {
-                  console.log("asdasd", form.isDebit);
                   form.isDebit = !form.isDebit;
                   updateForm(structuredClone(form));
                 }}
-- 
cgit v1.2.3


From 15af6c619de70336bcdfbabbd32b9d93aabefc5b Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 22 Sep 2023 18:34:49 -0300
Subject: towards new core bank api

---
 packages/demobank-ui/src/components/Routing.tsx    |   40 +-
 packages/demobank-ui/src/declaration.d.ts          |  166 +-
 packages/demobank-ui/src/hooks/access.ts           |  100 +-
 packages/demobank-ui/src/hooks/backend.ts          |    2 +-
 .../demobank-ui/src/hooks/useCredentialsChecker.ts |   15 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     |    2 +-
 .../demobank-ui/src/pages/AccountPage/state.ts     |   16 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |    2 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |   12 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |    8 +-
 packages/demobank-ui/src/pages/LoginForm.tsx       |  104 +-
 .../demobank-ui/src/pages/PublicHistoriesPage.tsx  |   16 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |   47 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |   50 +-
 packages/demobank-ui/src/pages/admin/Account.tsx   |   11 +-
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |    4 +-
 packages/demobank-ui/src/pages/business/Home.tsx   |   93 +-
 packages/demobank-ui/src/pages/rnd.ts              | 5759 ++++++++++----------
 packages/demobank-ui/src/settings.ts               |    4 +
 19 files changed, 3293 insertions(+), 3158 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 90d2d4c48..d51bd01eb 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -27,6 +27,7 @@ import { Test } from "../pages/Test.js";
 import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
+import { bankUiSettings } from "../settings.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
@@ -45,6 +46,10 @@ export function Routing(): VNode {
             />
           )}
         />
+        <Route
+          path="/public-accounts"
+          component={() => <PublicHistoriesPage />}
+        />
         <Route
           path="/operation/:wopid"
           component={({ wopid }: { wopid: string }) => (
@@ -53,22 +58,21 @@ export function Routing(): VNode {
               onContinue={() => {
                 route("/account");
               }}
-              // onLoadNotOk={() => {
-              //   route("/account");
-              // }}
-            />
-          )}
-        />
-        <Route
-          path="/register"
-          component={() => (
-            <RegistrationPage
-              onComplete={() => {
-                route("/account");
-              }}
             />
           )}
         />
+        {bankUiSettings.allowRegistrations &&
+          <Route
+            path="/register"
+            component={() => (
+              <RegistrationPage
+                onComplete={() => {
+                  route("/account");
+                }}
+              />
+            )}
+          />
+        }
         <Route default component={Redirect} to="/login" />
       </Router>
     </BankFrame>
@@ -93,16 +97,6 @@ export function Routing(): VNode {
           path="/public-accounts"
           component={() => <PublicHistoriesPage />}
         />
-        <Route
-          path="/register"
-          component={() => (
-            <RegistrationPage
-              onComplete={() => {
-                route("/account");
-              }}
-            />
-          )}
-        />
         <Route
           path="/account"
           component={() => {
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index 462287c59..a9573fbcd 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -99,11 +99,6 @@ type Amount = string;
 type UUID = string;
 type Integer = number;
 
-interface Balance {
-  amount: Amount;
-  credit_debit_indicator: "credit" | "debit";
-}
-
 namespace SandboxBackend {
   export interface Config {
     // Name of this API, always "circuit".
@@ -126,7 +121,7 @@ namespace SandboxBackend {
   }
 
   export interface SandboxError {
-    error: SandboxErrorDetail;
+    error?: SandboxErrorDetail;
   }
   interface SandboxErrorDetail {
     // String enum classifying the error.
@@ -152,26 +147,12 @@ namespace SandboxBackend {
     UtilError = "util-error",
   }
 
-  namespace Access {
-    interface PublicAccountsResponse {
-      publicAccounts: PublicAccount[];
-    }
-    interface PublicAccount {
-      iban: string;
-      balance: string;
-      // The account name _and_ the username of the
-      // Sandbox customer that owns such a bank account.
-      accountLabel: string;
-    }
 
-    interface BankAccountBalanceResponse {
-      // Available balance on the account.
-      balance: Balance;
-      // payto://-URI of the account. (New)
-      paytoUri: string;
-      // Number indicating the max debit allowed for the requesting user.
-      debitThreshold: Amount;
-    }
+  type EmailAddress = string;
+  type PhoneNumber = string;
+
+  namespace CoreBank {
+
     interface BankAccountCreateWithdrawalRequest {
       // Amount to withdraw.
       amount: Amount;
@@ -243,11 +224,144 @@ namespace SandboxBackend {
       amount?: string;
     }
 
-    interface BankRegistrationRequest {
+    interface RegisterAccountRequest {
+      // Username
       username: string;
 
+      // Password.
       password: string;
+
+      // Legal name of the account owner
+      name: string;
+
+      // Defaults to false.
+      is_public?: boolean;
+
+      // Is this a taler exchange account?
+      // If true:
+      // - incoming transactions to the account that do not
+      //   have a valid reserve public key are automatically
+      // - the account provides the taler-wire-gateway-api endpoints
+      // Defaults to false.
+      is_taler_exchange?: boolean;
+
+      // Addresses where to send the TAN for transactions.
+      // Currently only used for cashouts.
+      // If missing, cashouts will fail.
+      // In the future, might be used for other transactions
+      // as well.
+      challenge_contact_data?: ChallengeContactData;
+
+      // 'payto' address pointing a bank account
+      // external to the libeufin-bank.
+      // Payments will be sent to this bank account
+      // when the user wants to convert the local currency
+      // back to fiat currency outside libeufin-bank.
+      cashout_payto_uri?: string;
+
+      // Internal payto URI of this bank account.
+      // Used mostly for testing.
+      internal_payto_uri?: string;
+    }
+    interface ChallengeContactData {
+
+      // E-Mail address
+      email?: EmailAddress;
+
+      // Phone number.
+      phone?: PhoneNumber;
+    }
+
+    interface AccountReconfiguration {
+
+      // Addresses where to send the TAN for transactions.
+      // Currently only used for cashouts.
+      // If missing, cashouts will fail.
+      // In the future, might be used for other transactions
+      // as well.
+      challenge_contact_data?: ChallengeContactData;
+
+      // 'payto' address pointing a bank account
+      // external to the libeufin-bank.
+      // Payments will be sent to this bank account
+      // when the user wants to convert the local currency
+      // back to fiat currency outside libeufin-bank.
+      cashout_address?: string;
+
+      // Legal name associated with $username.
+      // When missing, the old name is kept.
+      name?: string;
+
+      // If present, change the is_exchange configuration.
+      // See RegisterAccountRequest
+      is_exchange?: boolean;
+    }
+
+
+    interface AccountPasswordChange {
+
+      // New password.
+      new_password: string;
+    }
+    interface PublicAccountsResponse {
+      public_accounts: PublicAccount[];
+    }
+    interface PublicAccount {
+      payto_uri: string;
+
+      balance: Balance;
+
+      // The account name (=username) of the
+      // libeufin-bank account.
+      account_name: string;
+    }
+
+    interface ListBankAccountsResponse {
+      accounts: AccountMinimalData[];
+    }
+    // interface Balance {
+    //   amount: Amount;
+    //   credit_debit_indicator: "credit" | "debit";
+    // }
+    type Balance = Amount
+    interface AccountMinimalData {
+      // Username
+      username: string;
+    
+      // Legal name of the account owner.
+      name: string;
+    
+      // current balance of the account
+      balance: Balance;
+    
+      // Number indicating the max debit allowed for the requesting user.
+      debit_threshold: Amount;
+    }
+    
+    interface AccountData {
+      // Legal name of the account owner.
+      name: string;
+    
+      // Available balance on the account.
+      balance: Balance;
+    
+      // payto://-URI of the account.
+      payto_uri: string;
+    
+      // Number indicating the max debit allowed for the requesting user.
+      debit_threshold: Amount;
+    
+      contact_data?: ChallengeContactData;
+    
+      // 'payto' address pointing the bank account
+      // where to send cashouts.  This field is optional
+      // because not all the accounts are required to participate
+      // in the merchants' circuit.  One example is the exchange:
+      // that never cashouts.  Registering these accounts can
+      // be done via the access API.
+      cashout_payto_uri?: string;
     }
+    
   }
 
   namespace Circuit {
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index af8381135..13fee71f0 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -44,13 +44,13 @@ export function useAccessAPI(): AccessAPI {
   const account = state.username;
 
   const createWithdrawal = async (
-    data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
+    data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
   ): Promise<
-    HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
+    HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
   > => {
     const res =
-      await request<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>(
-        `access-api/accounts/${account}/withdrawals`,
+      await request<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>(
+        `accounts/${account}/withdrawals`,
         {
           method: "POST",
           data,
@@ -60,10 +60,10 @@ export function useAccessAPI(): AccessAPI {
     return res;
   };
   const createTransaction = async (
-    data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
+    data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
   ): Promise<HttpResponseOk<void>> => {
     const res = await request<void>(
-      `access-api/accounts/${account}/transactions`,
+      `accounts/${account}/transactions`,
       {
         method: "POST",
         data,
@@ -74,7 +74,7 @@ export function useAccessAPI(): AccessAPI {
     return res;
   };
   const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`access-api/accounts/${account}`, {
+    const res = await request<void>(`accounts/${account}`, {
       method: "DELETE",
       contentType: "json",
     });
@@ -94,7 +94,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
   const { request } = useAuthenticatedBackend();
 
   const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`access-api/withdrawals/${id}/abort`, {
+    const res = await request<void>(`accounts/withdrawals/${id}/abort`, {
       method: "POST",
       contentType: "json",
     });
@@ -104,7 +104,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
   const confirmWithdrawal = async (
     id: string,
   ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`access-api/withdrawals/${id}/confirm`, {
+    const res = await request<void>(`withdrawals/${id}/confirm`, {
       method: "POST",
       contentType: "json",
     });
@@ -122,10 +122,10 @@ export function useTestingAPI(): TestingAPI {
   const mutateAll = useMatchMutate();
   const { request: noAuthRequest } = usePublicBackend();
   const register = async (
-    data: SandboxBackend.Access.BankRegistrationRequest,
+    data: SandboxBackend.CoreBank.RegisterAccountRequest,
   ): Promise<HttpResponseOk<void>> => {
     // FIXME: This API is deprecated.  The normal account registration API should be used instead.
-    const res = await noAuthRequest<void>(`access-api/testing/register`, {
+    const res = await noAuthRequest<void>(`accounts`, {
       method: "POST",
       data,
       contentType: "json",
@@ -139,18 +139,18 @@ export function useTestingAPI(): TestingAPI {
 
 export interface TestingAPI {
   register: (
-    data: SandboxBackend.Access.BankRegistrationRequest,
+    data: SandboxBackend.CoreBank.RegisterAccountRequest,
   ) => Promise<HttpResponseOk<void>>;
 }
 
 export interface AccessAPI {
   createWithdrawal: (
-    data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
+    data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
   ) => Promise<
-    HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
+    HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
   >;
   createTransaction: (
-    data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
+    data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
   ) => Promise<HttpResponseOk<void>>;
   deleteAccount: () => Promise<HttpResponseOk<void>>;
 }
@@ -167,15 +167,15 @@ export interface InstanceTemplateFilter {
 export function useAccountDetails(
   account: string,
 ): HttpResponse<
-  SandboxBackend.Access.BankAccountBalanceResponse,
+  SandboxBackend.CoreBank.AccountData,
   SandboxBackend.SandboxError
 > {
   const { fetcher } = useAuthenticatedBackend();
 
   const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Access.BankAccountBalanceResponse>,
+    HttpResponseOk<SandboxBackend.CoreBank.AccountData>,
     RequestError<SandboxBackend.SandboxError>
-  >([`access-api/accounts/${account}`], fetcher, {
+  >([`accounts/${account}`], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -187,28 +187,8 @@ export function useAccountDetails(
     keepPreviousData: true,
   });
 
-  //FIXME: remove optional when libeufin sandbox has implemented the feature
-  if (data && typeof data.data.debitThreshold === "undefined") {
-    data.data.debitThreshold = "0";
-  }
-  //FIXME: sandbox server should return amount string
   if (data) {
-    const isAmount = Amounts.parse(data.data.debitThreshold);
-    if (isAmount) {
-      //server response with correct format
-      return data;
-    }
-    const { currency } = Amounts.parseOrThrow(data.data.balance.amount);
-    const clone = structuredClone(data);
-
-    const theNumber = Number.parseInt(data.data.debitThreshold, 10);
-    const value = Number.isNaN(theNumber) ? 0 : theNumber;
-    clone.data.debitThreshold = Amounts.stringify({
-      currency,
-      value: value,
-      fraction: 0,
-    });
-    return clone;
+    return data;
   }
   if (error) return error.cause;
   return { loading: true };
@@ -218,15 +198,15 @@ export function useAccountDetails(
 export function useWithdrawalDetails(
   wid: string,
 ): HttpResponse<
-  SandboxBackend.Access.BankAccountGetWithdrawalResponse,
+  SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse,
   SandboxBackend.SandboxError
 > {
   const { fetcher } = useAuthenticatedBackend();
 
   const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Access.BankAccountGetWithdrawalResponse>,
+    HttpResponseOk<SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse>,
     RequestError<SandboxBackend.SandboxError>
-  >([`access-api/withdrawals/${wid}`], fetcher, {
+  >([`withdrawals/${wid}`], fetcher, {
     refreshInterval: 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -248,15 +228,15 @@ export function useTransactionDetails(
   account: string,
   tid: string,
 ): HttpResponse<
-  SandboxBackend.Access.BankAccountTransactionInfo,
+  SandboxBackend.CoreBank.BankAccountTransactionInfo,
   SandboxBackend.SandboxError
 > {
-  const { fetcher } = useAuthenticatedBackend();
+  const { paginatedFetcher } = useAuthenticatedBackend();
 
   const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Access.BankAccountTransactionInfo>,
+    HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionInfo>,
     RequestError<SandboxBackend.SandboxError>
-  >([`access-api/accounts/${account}/transactions/${tid}`], fetcher, {
+  >([`accounts/${account}/transactions/${tid}`], paginatedFetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -281,7 +261,7 @@ interface PaginationFilter {
 export function usePublicAccounts(
   args?: PaginationFilter,
 ): HttpResponsePaginated<
-  SandboxBackend.Access.PublicAccountsResponse,
+  SandboxBackend.CoreBank.PublicAccountsResponse,
   SandboxBackend.SandboxError
 > {
   const { paginatedFetcher } = usePublicBackend();
@@ -293,13 +273,13 @@ export function usePublicAccounts(
     error: afterError,
     isValidating: loadingAfter,
   } = useSWR<
-    HttpResponseOk<SandboxBackend.Access.PublicAccountsResponse>,
+    HttpResponseOk<SandboxBackend.CoreBank.PublicAccountsResponse>,
     RequestError<SandboxBackend.SandboxError>
-  >([`access-api/public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
+  >([`public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
 
   const [lastAfter, setLastAfter] = useState<
     HttpResponse<
-      SandboxBackend.Access.PublicAccountsResponse,
+      SandboxBackend.CoreBank.PublicAccountsResponse,
       SandboxBackend.SandboxError
     >
   >({ loading: true });
@@ -312,7 +292,7 @@ export function usePublicAccounts(
 
   // if the query returns less that we ask, then we have reach the end or beginning
   const isReachingEnd =
-    afterData && afterData.data.publicAccounts.length < PAGE_SIZE;
+    afterData && afterData.data.public_accounts.length < PAGE_SIZE;
   const isReachingStart = false;
 
   const pagination = {
@@ -320,7 +300,7 @@ export function usePublicAccounts(
     isReachingStart,
     loadMore: () => {
       if (!afterData || isReachingEnd) return;
-      if (afterData.data.publicAccounts.length < MAX_RESULT_SIZE) {
+      if (afterData.data.public_accounts.length < MAX_RESULT_SIZE) {
         setPage(page + 1);
       }
     },
@@ -329,12 +309,12 @@ export function usePublicAccounts(
     },
   };
 
-  const publicAccounts = !afterData
+  const public_accounts = !afterData
     ? []
-    : (afterData || lastAfter).data.publicAccounts;
-  if (loadingAfter) return { loading: true, data: { publicAccounts } };
+    : (afterData || lastAfter).data.public_accounts;
+  if (loadingAfter) return { loading: true, data: { public_accounts } };
   if (afterData) {
-    return { ok: true, data: { publicAccounts }, ...pagination };
+    return { ok: true, data: { public_accounts }, ...pagination };
   }
   return { loading: true };
 }
@@ -349,7 +329,7 @@ export function useTransactions(
   account: string,
   args?: PaginationFilter,
 ): HttpResponsePaginated<
-  SandboxBackend.Access.BankAccountTransactionsResponse,
+  SandboxBackend.CoreBank.BankAccountTransactionsResponse,
   SandboxBackend.SandboxError
 > {
   const { paginatedFetcher } = useAuthenticatedBackend();
@@ -361,10 +341,10 @@ export function useTransactions(
     error: afterError,
     isValidating: loadingAfter,
   } = useSWR<
-    HttpResponseOk<SandboxBackend.Access.BankAccountTransactionsResponse>,
+    HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionsResponse>,
     RequestError<SandboxBackend.SandboxError>
   >(
-    [`access-api/accounts/${account}/transactions`, args?.page, PAGE_SIZE],
+    [`accounts/${account}/transactions`, args?.page, PAGE_SIZE],
     paginatedFetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
@@ -378,7 +358,7 @@ export function useTransactions(
 
   const [lastAfter, setLastAfter] = useState<
     HttpResponse<
-      SandboxBackend.Access.BankAccountTransactionsResponse,
+      SandboxBackend.CoreBank.BankAccountTransactionsResponse,
       SandboxBackend.SandboxError
     >
   >({ loading: true });
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
index 9d32db4b8..f2be90f08 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -310,7 +310,7 @@ export function useAuthenticatedBackend(): useBackendType {
     ]): Promise<HttpResponseOk<T>> {
       return requestHandler<T>(baseUrl, endpoint, {
         basicAuth: creds,
-        params: { page, size },
+        params: { delta: size, start: size * page },
       });
     },
     [baseUrl, creds],
diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
index b76754ffe..05954348f 100644
--- a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
+++ b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
@@ -9,20 +9,25 @@ export function useCredentialsChecker() {
   //while merchant backend doesn't have a login endpoint
   async function requestNewLoginToken(
     username: string,
-    password: AccessToken,
+    password: string,
   ): Promise<LoginResult> {
     const data: LoginTokenRequest = {
-      scope: "write",
+      scope: "readwrite" as "write", //FIX: different than merchant
       duration: {
-        d_us: "forever"
+        // d_us: "forever" //FIX: should return shortest
+        d_us: 1000 * 60 * 60 * 23
       },
       refreshable: true,
     }
     try {
       const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
         method: "POST",
-        token: password,
-        data
+        basicAuth: {
+          username: username,
+          password,
+        },
+        data,
+        contentType: "json"
       });
       return { valid: true, token: response.data.token, expiration: response.data.expiration };
     } catch (error) {
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index 81eeb4a03..ba9b85710 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -60,7 +60,7 @@ export namespace State {
 
   export interface InvalidIban {
     status: "invalid-iban",
-    error: HttpResponseOk<SandboxBackend.Access.BankAccountBalanceResponse>;
+    error: HttpResponseOk<SandboxBackend.CoreBank.AccountData>;
   }
 
   export interface UserNotFound {
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index 1a1475c0d..b12afbf9e 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -40,7 +40,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
       };
     }
     //logout if there is any error, not if loading
-    backend.logOut();
+    // backend.logOut();
     if (result.status === HttpStatusCode.NotFound) {
       notifyError(i18n.str`Username or account label "${account}" not found`, undefined);
       return {
@@ -55,9 +55,13 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
   }
 
   const { data } = result;
-  const balance = Amounts.parseOrThrow(data.balance.amount);
-  const debitThreshold = Amounts.parseOrThrow(data.debitThreshold);
-  const payto = parsePaytoUri(data.paytoUri);
+
+  // FIXME: balance
+  // const balance = Amounts.parseOrThrow(data.balance.amount);
+  const balance = Amounts.parseOrThrow(data.balance);
+
+  const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
+  const payto = parsePaytoUri(data.payto_uri);
 
   if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
     return {
@@ -66,7 +70,9 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
     };
   }
 
-  const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
+  // FIXME: balance
+  const balanceIsDebit = true;
+  // data.balance.credit_debit_indicator == "debit";
   const limit = balanceIsDebit
     ? Amounts.sub(debitThreshold, balance).amount
     : Amounts.add(balance, debitThreshold).amount;
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 23a815bd8..b3996c5fc 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -27,7 +27,7 @@ import { useSettings } from "../../hooks/settings.js";
 
 export function InvalidIbanView({ error }: State.InvalidIban) {
   return (
-    <div>Payto from server is not valid &quot;{error.data.paytoUri}&quot;</div>
+    <div>Payto from server is not valid &quot;{error.data.payto_uri}&quot;</div>
   );
 }
 
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 5bfaa63ec..a1414544e 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -458,15 +458,14 @@ function WelcomeAccount({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
   if (!result.ok) return <div />
 
-  // const account = "Sebastian"
-  const payto = parsePaytoUri(result.data.paytoUri)
+  const payto = parsePaytoUri(result.data.payto_uri)
   if (!payto) return <div />
 
   const accountNumber = !payto.isKnown ? undefined : payto.targetType === "iban" ? payto.iban : payto.targetType === "x-taler-bank" ? payto.account : undefined;
   return <i18n.Translate>
     Welcome,  {account} {accountNumber !== undefined ?
       <span>
-        (<a href={result.data.paytoUri}>{accountNumber}</a> <CopyButton getContent={() => result.data.paytoUri} />)
+        (<a href={result.data.payto_uri}>{accountNumber}</a> <CopyButton getContent={() => result.data.payto_uri} />)
       </span>
       : <Fragment />}!
   </i18n.Translate>
@@ -477,9 +476,12 @@ function AccountBalance({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
   if (!result.ok) return <div />
 
+  // FIXME: balance
   return <div>
-    {Amounts.currencyOf(result.data.balance.amount)}
+    {Amounts.currencyOf(result.data.balance)}
+    {Amounts.stringifyValue(result.data.balance)}
+    {/* {Amounts.currencyOf(result.data.balance.amount)}
     &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
-    {Amounts.stringifyValue(result.data.balance.amount)}
+    {Amounts.stringifyValue(result.data.balance.amount)} */}
   </div>
 }
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index 8d5e1f3b9..d80a57d21 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -120,8 +120,8 @@ export function handleNotOkResult(
 ) => VNode {
   return function handleNotOkResult2<T>(
     result:
-      | HttpResponsePaginated<T, SandboxBackend.SandboxError>
-      | HttpResponse<T, SandboxBackend.SandboxError>,
+      | HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
+      | HttpResponse<T, SandboxBackend.SandboxError| undefined>,
   ): VNode {
     if (result.loading) return <Loading />;
     if (!result.ok) {
@@ -139,7 +139,7 @@ export function handleNotOkResult(
           notify({
             type: "error",
             title: i18n.str`Could not load due to a client error`,
-            description: errorData.error.description as TranslatedString,
+            description: errorData?.error?.description as TranslatedString,
             debug: JSON.stringify(result),
           });
           break;
@@ -148,7 +148,7 @@ export function handleNotOkResult(
           notify({
             type: "error",
             title: i18n.str`Server returned with error`,
-            description: result.payload.error.description as TranslatedString,
+            description: result.payload?.error?.description as TranslatedString,
             debug: JSON.stringify(result.payload),
           });
           break;
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f6ea0e1d1..0d4bd1261 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -31,12 +31,13 @@ import { useCredentialsCheckerOld } from "../hooks/backend.js";
  */
 export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const backend = useBackendContext();
-  const [username, setUsername] = useState<string | undefined>();
+  const currentUser = backend.state.status === "loggedIn" ? backend.state.username : undefined
+  const [username, setUsername] = useState<string | undefined>(currentUser);
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
-  // const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
+  const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
 
-  const testLogin = useCredentialsCheckerOld();
+  // const testLogin = useCredentialsCheckerOld();
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
     ref.current?.focus();
@@ -46,8 +47,8 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const errors = undefinedIfEmpty({
     username: !username
       ? i18n.str`Missing username`
-      : !USERNAME_REGEX.test(username)
-        ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
+      // : !USERNAME_REGEX.test(username)
+      //   ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
         : undefined,
     password: !password ? i18n.str`Missing password` : undefined,
   }) ?? busy;
@@ -59,19 +60,18 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   async function doLogin() {
     if (!username || !password) return;
     setBusy({})
-    const testResult = await testLogin(username, password);
-    if (testResult.valid) {
+    const result = await requestNewLoginToken(username, password);
+    if (result.valid) {
       backend.logIn({ username, password });
     } else {
-      if (testResult.requestError) {
-        const { cause } = testResult;
-        switch (cause.type) {
-          case ErrorType.CLIENT: {
-            if (cause.status === HttpStatusCode.Unauthorized) {
-              saveError({
-                title: i18n.str`Wrong credentials for "${username}"`,
-              });
-            } else
+      const { cause } = result;
+      switch (cause.type) {
+        case ErrorType.CLIENT: {
+          if (cause.status === HttpStatusCode.Unauthorized) {
+            saveError({
+              title: i18n.str`Wrong credentials for "${username}"`,
+            });
+          } else
             if (cause.status === HttpStatusCode.NotFound) {
               saveError({
                 title: i18n.str`Account not found`,
@@ -79,50 +79,44 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
             } else {
               saveError({
                 title: i18n.str`Could not load due to a client error`,
-                description: cause.payload.error.description,
+                // description: cause.payload.error.description,
                 debug: JSON.stringify(cause.payload),
               });
             }
-            break;
-          }
-          case ErrorType.SERVER: {
-            saveError({
-              title: i18n.str`Server had a problem, try again later or report.`,
-              description: cause.payload.error.description,
-              debug: JSON.stringify(cause.payload),
-            });
-            break;
-          }
-          case ErrorType.TIMEOUT: {
-            saveError({
-              title: i18n.str`Request timeout, try again later.`,
-            });
-            break;
-          }
-          case ErrorType.UNREADABLE: {
-            saveError({
-              title: i18n.str`Unexpected error.`,
-              description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
-              debug: JSON.stringify(cause),
-            });
-            break;
-          }
-          default: {
-            saveError({
-              title: i18n.str`Unexpected error, please report.`,
-              description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
-              debug: JSON.stringify(cause),
-            });
-            break;
-          }
+          break;
+        }
+        case ErrorType.SERVER: {
+          saveError({
+            title: i18n.str`Server had a problem, try again later or report.`,
+            // description: cause.payload.error.description,
+            debug: JSON.stringify(cause.payload),
+          });
+          break;
+        }
+        case ErrorType.TIMEOUT: {
+          saveError({
+            title: i18n.str`Request timeout, try again later.`,
+          });
+          break;
+        }
+        case ErrorType.UNREADABLE: {
+          saveError({
+            title: i18n.str`Unexpected error.`,
+            description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
+            debug: JSON.stringify(cause),
+          });
+          break;
+        }
+        default: {
+          saveError({
+            title: i18n.str`Unexpected error, please report.`,
+            description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
+            debug: JSON.stringify(cause),
+          });
+          break;
         }
-      } else {
-        saveError({
-          title: i18n.str`Unexpected error, please report.`,
-          debug: JSON.stringify(testResult.error),
-        });
       }
-      backend.logOut();
+      // backend.logOut();
     }
     setPassword(undefined);
     setBusy(undefined)
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index 03bdb78b7..680368919 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -36,8 +36,8 @@ export function PublicHistoriesPage({}: Props): VNode {
   const result = usePublicAccounts();
 
   const [showAccount, setShowAccount] = useState(
-    result.ok && result.data.publicAccounts.length > 0
-      ? result.data.publicAccounts[0].accountLabel
+    result.ok && result.data.public_accounts.length > 0
+      ? result.data.public_accounts[0].account_name
       : undefined,
   );
 
@@ -51,9 +51,9 @@ export function PublicHistoriesPage({}: Props): VNode {
   const accountsBar = [];
 
   // Ask story of all the public accounts.
-  for (const account of data.publicAccounts) {
-    logger.trace("Asking transactions for", account.accountLabel);
-    const isSelected = account.accountLabel == showAccount;
+  for (const account of data.public_accounts) {
+    logger.trace("Asking transactions for", account.account_name);
+    const isSelected = account.account_name == showAccount;
     accountsBar.push(
       <li
         class={
@@ -65,13 +65,13 @@ export function PublicHistoriesPage({}: Props): VNode {
         <a
           href="#"
           class="pure-menu-link"
-          onClick={() => setShowAccount(account.accountLabel)}
+          onClick={() => setShowAccount(account.account_name)}
         >
-          {account.accountLabel}
+          {account.account_name}
         </a>
       </li>,
     );
-    txs[account.accountLabel] = <Transactions account={account.accountLabel} />;
+    txs[account.account_name] = <Transactions account={account.account_name} />;
   }
 
   return (
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index f972e0380..8221457bf 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -53,6 +53,7 @@ export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
 function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
   const backend = useBackendContext();
   const [username, setUsername] = useState<string | undefined>();
+  const [name, setName] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
   const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
 
@@ -60,6 +61,9 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
   const { i18n } = useTranslationContext();
 
   const errors = undefinedIfEmpty({
+    name: !name
+      ? i18n.str`Missing name`
+      : undefined,
     username: !username
       ? i18n.str`Missing username`
       : !USERNAME_REGEX.test(username)
@@ -74,9 +78,9 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
   });
 
   async function doRegistrationStep() {
-    if (!username || !password) return;
+    if (!username || !password || !name) return;
     try {
-      await register({ username, password });
+      await register({ name, username, password });
       setUsername(undefined);
       backend.logIn({ username, password });
       onComplete();
@@ -97,13 +101,13 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
             ? error.message
             : JSON.stringify(error)) as TranslatedString
         )
-}
+      }
     }
     setPassword(undefined);
     setRepeatPassword(undefined);
-}
+  }
 
-  async function delay(ms: number):Promise<void> {
+  async function delay(ms: number): Promise<void> {
     return new Promise((resolve) => {
       setTimeout(() => {
         resolve(undefined);
@@ -117,14 +121,15 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
       setUsername(undefined);
       setPassword(undefined);
       setRepeatPassword(undefined);
-      await register({ username: user, password: pass });
-      backend.logIn({ username: user, password: pass });
+      const username = `_${user.first}-${user.second}_`
+      await register({ username, name: `${user.first} ${user.second}`, password: pass });
+      backend.logIn({ username, password: pass });
       onComplete();
     } catch (error) {
       if (error instanceof RequestError) {
         if (tries > 0) {
           await delay(200)
-          await doRandomRegistration(tries-1)
+          await doRandomRegistration(tries - 1)
         } else {
           notify(
             buildRequestErrorMessage(i18n, error.cause, {
@@ -142,7 +147,7 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
             ? error.message
             : JSON.stringify(error)) as TranslatedString
         )
-}
+      }
     }
   }
 
@@ -257,17 +262,19 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
 
           </form>
 
-          <p class="mt-10 text-center text-sm text-gray-500 border-t">
-            <button type="submit"
-              class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
-              onClick={(e) => {
-                e.preventDefault()
-                doRandomRegistration()
-              }}
-            >
-              <i18n.Translate>Create a random user</i18n.Translate>
-            </button>
-          </p>
+          {bankUiSettings.allowRandomAccountCreation &&
+            <p class="mt-10 text-center text-sm text-gray-500 border-t">
+              <button type="submit"
+                class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
+                onClick={(e) => {
+                  e.preventDefault()
+                  doRandomRegistration()
+                }}
+              >
+                <i18n.Translate>Create a random user</i18n.Translate>
+              </button>
+            </p>
+          }
         </div>
       </div>
 
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 8dbdd9da6..e16825527 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -59,27 +59,41 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
   }, [focus]);
 
   if (!!settings.currentWithdrawalOperationId) {
-    return <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
-      <div class="flex">
-        <div class="flex-shrink-0">
-          <svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
-            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
-          </svg>
-        </div>
-        <div class="ml-3">
-          <h3 class="text-sm font-bold text-yellow-800">
-            <i18n.Translate>There is an operation already</i18n.Translate>
-          </h3>
-          <div class="mt-2 text-sm text-yellow-700">
-            <p>
-              <i18n.Translate>
-                To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
-              </i18n.Translate>
-            </p>
+    return <div>
+
+      <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
+        <div class="flex">
+          <div class="flex-shrink-0">
+            <svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
+            </svg>
+          </div>
+          <div class="ml-3">
+            <h3 class="text-sm font-bold text-yellow-800">
+              <i18n.Translate>There is an operation already</i18n.Translate>
+            </h3>
+            <div class="mt-2 text-sm text-yellow-700">
+              <p>
+                <i18n.Translate>
+                  To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
+                </i18n.Translate>
+              </p>
+            </div>
+
+          </div>
         </div>
+      </div >
+      <div class="flex justify-end gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8 " >
+        <button type="button" class="text-sm font-semibold leading-6 text-gray-900 bg-white p-2 rounded-sm"
+          onClick={() => {
+            updateSettings("currentWithdrawalOperationId", undefined)
+            onCancel()
+          }}
+        >
+          <i18n.Translate>Cancel</i18n.Translate>
+        </button>
       </div>
     </div>
-  </div >
   }
 
   const trimmedAmountStr = amountStr?.trim();
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
index 90ddd611d..d368c4319 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -16,9 +16,14 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode
     return handleNotOkResult(i18n, onRegister)(result);
   }
   const { data } = result;
-  const balance = Amounts.parseOrThrow(data.balance.amount);
-  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+
+  //FIXME: libeufin does not follow the spec
+  const balance = Amounts.parseOrThrow(data.balance);
+  const balanceIsDebit = true;
+  // const balance = Amounts.parseOrThrow(data.balance.amount);
+  // const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+  
+  const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
   const limit = balanceIsDebit
     ? Amounts.sub(debitThreshold, balance).amount
     : Amounts.add(balance, debitThreshold).amount;
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 1e5370afc..63a7c79f3 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -41,7 +41,9 @@ export function RemoveAccount({
     if (focus) ref.current?.focus();
   }, [focus]);
 
-  const balance = Amounts.parse(result.data.balance.amount);
+  //FIXME: libeufin does not follow the spec
+  const balance = Amounts.parse(result.data.balance);
+  // const balance = Amounts.parse(result.data.balance.amount);
   if (!balance) {
     return <div>there was an error reading the balance</div>;
   }
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index f5f77a3ea..427cfc656 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -201,13 +201,13 @@ function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
     (result.data.name !== oldResult.name ||
       result.data.version !== oldResult.version ||
       result.data.ratios_and_fees.buy_at_ratio !==
-        oldResult.ratios_and_fees.buy_at_ratio ||
+      oldResult.ratios_and_fees.buy_at_ratio ||
       result.data.ratios_and_fees.buy_in_fee !==
-        oldResult.ratios_and_fees.buy_in_fee ||
+      oldResult.ratios_and_fees.buy_in_fee ||
       result.data.ratios_and_fees.sell_at_ratio !==
-        oldResult.ratios_and_fees.sell_at_ratio ||
+      oldResult.ratios_and_fees.sell_at_ratio ||
       result.data.ratios_and_fees.sell_out_fee !==
-        oldResult.ratios_and_fees.sell_out_fee ||
+      oldResult.ratios_and_fees.sell_out_fee ||
       result.data.fiat_currency !== oldResult.fiat_currency);
 
   return {
@@ -236,10 +236,14 @@ function CreateCashout({
   if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
   const config = ratiosResult.data;
 
-  const balance = Amounts.parseOrThrow(result.data.balance.amount);
-  const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
+  //FIXME: libeufin does not follow the spec
+  const balance = Amounts.parseOrThrow(result.data.balance);
+  const balanceIsDebit = true;
+  // const balance = Amounts.parseOrThrow(result.data.balance.amount);
+  // const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+
+  const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
   const zero = Amounts.zeroOfCurrency(balance.currency);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
   const limit = balanceIsDebit
     ? Amounts.sub(debitThreshold, balance).amount
     : Amounts.add(balance, debitThreshold).amount;
@@ -250,15 +254,14 @@ function CreateCashout({
   const sellFee = !config.ratios_and_fees.sell_out_fee
     ? zero
     : Amounts.parseOrThrow(
-        `${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
-      );
+      `${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
+    );
   const fiatCurrency = config.fiat_currency;
 
   if (!sellRate || sellRate < 0) return <div>error rate</div>;
 
   const amount = Amounts.parseOrThrow(
-    `${!form.isDebit ? fiatCurrency : balance.currency}:${
-      !form.amount ? "0" : form.amount
+    `${!form.isDebit ? fiatCurrency : balance.currency}:${!form.amount ? "0" : form.amount
     }`,
   );
 
@@ -273,10 +276,10 @@ function CreateCashout({
             error instanceof RequestError
               ? buildRequestErrorMessage(i18n, error.cause)
               : {
-                  type: "error",
-                  title: i18n.str`Could not estimate the cashout`,
-                  description: error.message as TranslatedString
-                },
+                type: "error",
+                title: i18n.str`Could not estimate the cashout`,
+                description: error.message as TranslatedString
+              },
           );
         });
     } else {
@@ -289,10 +292,10 @@ function CreateCashout({
             error instanceof RequestError
               ? buildRequestErrorMessage(i18n, error.cause)
               : {
-                  type: "error",
-                  title: i18n.str`Could not estimate the cashout`,
-                  description: error.message,
-                },
+                type: "error",
+                title: i18n.str`Could not estimate the cashout`,
+                description: error.message,
+              },
           );
         });
     }
@@ -307,14 +310,14 @@ function CreateCashout({
     amount: !form.amount
       ? i18n.str`required`
       : !amount
-      ? i18n.str`could not be parsed`
-      : Amounts.cmp(limit, calc.debit) === -1
-      ? i18n.str`balance is not enough`
-      : Amounts.cmp(calc.beforeFee, sellFee) === -1
-      ? i18n.str`the total amount to transfer does not cover the fees`
-      : Amounts.isZero(calc.credit)
-      ? i18n.str`the total transfer at destination will be zero`
-      : undefined,
+        ? i18n.str`could not be parsed`
+        : Amounts.cmp(limit, calc.debit) === -1
+          ? i18n.str`balance is not enough`
+          : Amounts.cmp(calc.beforeFee, sellFee) === -1
+            ? i18n.str`the total amount to transfer does not cover the fees`
+            : Amounts.isZero(calc.credit)
+              ? i18n.str`the total transfer at destination will be zero`
+              : undefined,
     channel: !form.channel ? i18n.str`required` : undefined,
   });
 
@@ -341,7 +344,7 @@ function CreateCashout({
             {form.isDebit
               ? i18n.str`Amount to send`
               : i18n.str`Amount to receive`}
-            
+
           </label>
           <div style={{ display: "flex" }}>
             <Amount
@@ -520,12 +523,12 @@ function CreateCashout({
                         status === HttpStatusCode.BadRequest
                           ? i18n.str`The exchange rate was incorrectly applied`
                           : status === HttpStatusCode.Forbidden
-                          ? i18n.str`A institutional user tried the operation`
-                          : status === HttpStatusCode.Conflict
-                          ? i18n.str`Need a contact data where to send the TAN`
-                          : status === HttpStatusCode.PreconditionFailed
-                          ? i18n.str`The account does not have sufficient funds`
-                          : undefined,
+                            ? i18n.str`A institutional user tried the operation`
+                            : status === HttpStatusCode.Conflict
+                              ? i18n.str`Need a contact data where to send the TAN`
+                              : status === HttpStatusCode.PreconditionFailed
+                                ? i18n.str`The account does not have sufficient funds`
+                                : undefined,
                       onServerError: (status) =>
                         status === HttpStatusCode.ServiceUnavailable
                           ? i18n.str`The bank does not support the TAN channel for this operation`
@@ -539,7 +542,7 @@ function CreateCashout({
                       ? error.message
                       : JSON.stringify(error)) as TranslatedString
                   )
-            }
+                }
               }
             }}
           >
@@ -665,8 +668,8 @@ export function ShowCashoutDetails({
                           status === HttpStatusCode.NotFound
                             ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
                             : status === HttpStatusCode.PreconditionFailed
-                            ? i18n.str`Cashout was already confimed`
-                            : undefined,
+                              ? i18n.str`Cashout was already confimed`
+                              : undefined,
                       }),
                     );
                   } else {
@@ -676,7 +679,7 @@ export function ShowCashoutDetails({
                         ? error.message
                         : JSON.stringify(error)) as TranslatedString
                     )
-                }
+                  }
                 }
               }}
             >
@@ -702,12 +705,12 @@ export function ShowCashoutDetails({
                           status === HttpStatusCode.NotFound
                             ? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
                             : status === HttpStatusCode.PreconditionFailed
-                            ? i18n.str`Cashout was already confimed`
-                            : status === HttpStatusCode.Conflict
-                            ? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
-                            : status === HttpStatusCode.Forbidden
-                            ? i18n.str`Invalid code`
-                            : undefined,
+                              ? i18n.str`Cashout was already confimed`
+                              : status === HttpStatusCode.Conflict
+                                ? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
+                                : status === HttpStatusCode.Forbidden
+                                  ? i18n.str`Invalid code`
+                                  : undefined,
                       }),
                     );
                   } else {
@@ -717,7 +720,7 @@ export function ShowCashoutDetails({
                         ? error.message
                         : JSON.stringify(error)) as TranslatedString
                     )
-                }
+                  }
                 }
               }}
             >
diff --git a/packages/demobank-ui/src/pages/rnd.ts b/packages/demobank-ui/src/pages/rnd.ts
index 8c9bae875..32c3a934f 100644
--- a/packages/demobank-ui/src/pages/rnd.ts
+++ b/packages/demobank-ui/src/pages/rnd.ts
@@ -1,2890 +1,2895 @@
 import { createEddsaKeyPair, encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"
+import { bankUiSettings } from "../settings.js"
 
 
 const noun = [
-    "people",
-    "history",
-    "way",
-    "art",
-    "world",
-    "information",
-    "map",
-    "two",
-    "family",
-    "government",
-    "health",
-    "system",
-    "computer",
-    "meat",
-    "year",
-    "thanks",
-    "music",
-    "person",
-    "reading",
-    "method",
-    "data",
-    "food",
-    "understanding",
-    "theory",
-    "law",
-    "bird",
-    "literature",
-    "problem",
-    "software",
-    "control",
-    "knowledge",
-    "power",
-    "ability",
-    "economics",
-    "love",
-    "internet",
-    "television",
-    "science",
-    "library",
-    "nature",
-    "fact",
-    "product",
-    "idea",
-    "temperature",
-    "investment",
-    "area",
-    "society",
-    "activity",
-    "story",
-    "industry",
-    "media",
-    "thing",
-    "oven",
-    "community",
-    "definition",
-    "safety",
-    "quality",
-    "development",
-    "language",
-    "management",
-    "player",
-    "variety",
-    "video",
-    "week",
-    "security",
-    "country",
-    "exam",
-    "movie",
-    "organization",
-    "equipment",
-    "physics",
-    "analysis",
-    "policy",
-    "series",
-    "thought",
-    "basis",
-    "boyfriend",
-    "direction",
-    "strategy",
-    "technology",
-    "army",
-    "camera",
-    "freedom",
-    "paper",
-    "environment",
-    "child",
-    "instance",
-    "month",
-    "truth",
-    "marketing",
-    "university",
-    "writing",
-    "article",
-    "department",
-    "difference",
-    "goal",
-    "news",
-    "audience",
-    "fishing",
-    "growth",
-    "income",
-    "marriage",
-    "user",
-    "combination",
-    "failure",
-    "meaning",
-    "medicine",
-    "philosophy",
-    "teacher",
-    "communication",
-    "night",
-    "chemistry",
-    "disease",
-    "disk",
-    "energy",
-    "nation",
-    "road",
-    "role",
-    "soup",
-    "advertising",
-    "location",
-    "success",
-    "addition",
-    "apartment",
-    "education",
-    "math",
-    "moment",
-    "painting",
-    "politics",
-    "attention",
-    "decision",
-    "event",
-    "property",
-    "shopping",
-    "student",
-    "wood",
-    "competition",
-    "distribution",
-    "entertainment",
-    "office",
-    "population",
-    "president",
-    "unit",
-    "category",
-    "cigarette",
-    "context",
-    "introduction",
-    "opportunity",
-    "performance",
-    "driver",
-    "flight",
-    "length",
-    "magazine",
-    "newspaper",
-    "relationship",
-    "teaching",
-    "cell",
-    "dealer",
-    "finding",
-    "lake",
-    "member",
-    "message",
-    "phone",
-    "scene",
-    "appearance",
-    "association",
-    "concept",
-    "customer",
-    "death",
-    "discussion",
-    "housing",
-    "inflation",
-    "insurance",
-    "mood",
-    "woman",
-    "advice",
-    "blood",
-    "effort",
-    "expression",
-    "importance",
-    "opinion",
-    "payment",
-    "reality",
-    "responsibility",
-    "situation",
-    "skill",
-    "statement",
-    "wealth",
-    "application",
-    "city",
-    "county",
-    "depth",
-    "estate",
-    "foundation",
-    "grandmother",
-    "heart",
-    "perspective",
-    "photo",
-    "recipe",
-    "studio",
-    "topic",
-    "collection",
-    "depression",
-    "imagination",
-    "passion",
-    "percentage",
-    "resource",
-    "setting",
-    "ad",
-    "agency",
-    "college",
-    "connection",
-    "criticism",
-    "debt",
-    "description",
-    "memory",
-    "patience",
-    "secretary",
-    "solution",
-    "administration",
-    "aspect",
-    "attitude",
-    "director",
-    "personality",
-    "psychology",
-    "recommendation",
-    "response",
-    "selection",
-    "storage",
-    "version",
-    "alcohol",
-    "argument",
-    "complaint",
-    "contract",
-    "emphasis",
-    "highway",
-    "loss",
-    "membership",
-    "possession",
-    "preparation",
-    "steak",
-    "union",
-    "agreement",
-    "cancer",
-    "currency",
-    "employment",
-    "engineering",
-    "entry",
-    "interaction",
-    "mixture",
-    "preference",
-    "region",
-    "republic",
-    "tradition",
-    "virus",
-    "actor",
-    "classroom",
-    "delivery",
-    "device",
-    "difficulty",
-    "drama",
-    "election",
-    "engine",
-    "football",
-    "guidance",
-    "hotel",
-    "owner",
-    "priority",
-    "protection",
-    "suggestion",
-    "tension",
-    "variation",
-    "anxiety",
-    "atmosphere",
-    "awareness",
-    "bath",
-    "bread",
-    "candidate",
-    "climate",
-    "comparison",
-    "confusion",
-    "construction",
-    "elevator",
-    "emotion",
-    "employee",
-    "employer",
-    "guest",
-    "height",
-    "leadership",
-    "mall",
-    "manager",
-    "operation",
-    "recording",
-    "sample",
-    "transportation",
-    "charity",
-    "cousin",
-    "disaster",
-    "editor",
-    "efficiency",
-    "excitement",
-    "extent",
-    "feedback",
-    "guitar",
-    "homework",
-    "leader",
-    "mom",
-    "outcome",
-    "permission",
-    "presentation",
-    "promotion",
-    "reflection",
-    "refrigerator",
-    "resolution",
-    "revenue",
-    "session",
-    "singer",
-    "tennis",
-    "basket",
-    "bonus",
-    "cabinet",
-    "childhood",
-    "church",
-    "clothes",
-    "coffee",
-    "dinner",
-    "drawing",
-    "hair",
-    "hearing",
-    "initiative",
-    "judgment",
-    "lab",
-    "measurement",
-    "mode",
-    "mud",
-    "orange",
-    "poetry",
-    "police",
-    "possibility",
-    "procedure",
-    "queen",
-    "ratio",
-    "relation",
-    "restaurant",
-    "satisfaction",
-    "sector",
-    "signature",
-    "significance",
-    "song",
-    "tooth",
-    "town",
-    "vehicle",
-    "volume",
-    "wife",
-    "accident",
-    "airport",
-    "appointment",
-    "arrival",
-    "assumption",
-    "baseball",
-    "chapter",
-    "committee",
-    "conversation",
-    "database",
-    "enthusiasm",
-    "error",
-    "explanation",
-    "farmer",
-    "gate",
-    "girl",
-    "hall",
-    "historian",
-    "hospital",
-    "injury",
-    "instruction",
-    "maintenance",
-    "manufacturer",
-    "meal",
-    "perception",
-    "pie",
-    "poem",
-    "presence",
-    "proposal",
-    "reception",
-    "replacement",
-    "revolution",
-    "river",
-    "son",
-    "speech",
-    "tea",
-    "village",
-    "warning",
-    "winner",
-    "worker",
-    "writer",
-    "assistance",
-    "breath",
-    "buyer",
-    "chest",
-    "chocolate",
-    "conclusion",
-    "contribution",
-    "cookie",
-    "courage",
-    "dad",
-    "desk",
-    "drawer",
-    "establishment",
-    "examination",
-    "garbage",
-    "grocery",
-    "honey",
-    "impression",
-    "improvement",
-    "independence",
-    "insect",
-    "inspection",
-    "inspector",
-    "king",
-    "ladder",
-    "menu",
-    "penalty",
-    "piano",
-    "potato",
-    "profession",
-    "professor",
-    "quantity",
-    "reaction",
-    "requirement",
-    "salad",
-    "sister",
-    "supermarket",
-    "tongue",
-    "weakness",
-    "wedding",
-    "affair",
-    "ambition",
-    "analyst",
-    "apple",
-    "assignment",
-    "assistant",
-    "bathroom",
-    "bedroom",
-    "beer",
-    "birthday",
-    "celebration",
-    "championship",
-    "cheek",
-    "client",
-    "consequence",
-    "departure",
-    "diamond",
-    "dirt",
-    "ear",
-    "fortune",
-    "friendship",
-    "funeral",
-    "gene",
-    "girlfriend",
-    "hat",
-    "indication",
-    "intention",
-    "lady",
-    "midnight",
-    "negotiation",
-    "obligation",
-    "passenger",
-    "pizza",
-    "platform",
-    "poet",
-    "pollution",
-    "recognition",
-    "reputation",
-    "shirt",
-    "sir",
-    "speaker",
-    "stranger",
-    "surgery",
-    "sympathy",
-    "tale",
-    "throat",
-    "trainer",
-    "uncle",
-    "youth",
-    "time",
-    "work",
-    "film",
-    "water",
-    "money",
-    "example",
-    "while",
-    "business",
-    "study",
-    "game",
-    "life",
-    "form",
-    "air",
-    "day",
-    "place",
-    "number",
-    "part",
-    "field",
-    "fish",
-    "back",
-    "process",
-    "heat",
-    "hand",
-    "experience",
-    "job",
-    "book",
-    "end",
-    "point",
-    "type",
-    "home",
-    "economy",
-    "value",
-    "body",
-    "market",
-    "guide",
-    "interest",
-    "state",
-    "radio",
-    "course",
-    "company",
-    "price",
-    "size",
-    "card",
-    "list",
-    "mind",
-    "trade",
-    "line",
-    "care",
-    "group",
-    "risk",
-    "word",
-    "fat",
-    "force",
-    "key",
-    "light",
-    "training",
-    "name",
-    "school",
-    "top",
-    "amount",
-    "level",
-    "order",
-    "practice",
-    "research",
-    "sense",
-    "service",
-    "piece",
-    "web",
-    "boss",
-    "sport",
-    "fun",
-    "house",
-    "page",
-    "term",
-    "test",
-    "answer",
-    "sound",
-    "focus",
-    "matter",
-    "kind",
-    "soil",
-    "board",
-    "oil",
-    "picture",
-    "access",
-    "garden",
-    "range",
-    "rate",
-    "reason",
-    "future",
-    "site",
-    "demand",
-    "exercise",
-    "image",
-    "case",
-    "cause",
-    "coast",
-    "action",
-    "age",
-    "bad",
-    "boat",
-    "record",
-    "result",
-    "section",
-    "building",
-    "mouse",
-    "cash",
-    "class",
-    "nothing",
-    "period",
-    "plan",
-    "store",
-    "tax",
-    "side",
-    "subject",
-    "space",
-    "rule",
-    "stock",
-    "weather",
-    "chance",
-    "figure",
-    "man",
-    "model",
-    "source",
-    "beginning",
-    "earth",
-    "program",
-    "chicken",
-    "design",
-    "feature",
-    "head",
-    "material",
-    "purpose",
-    "question",
-    "rock",
-    "salt",
-    "act",
-    "birth",
-    "car",
-    "dog",
-    "object",
-    "scale",
-    "sun",
-    "note",
-    "profit",
-    "rent",
-    "speed",
-    "style",
-    "war",
-    "bank",
-    "craft",
-    "half",
-    "inside",
-    "outside",
-    "standard",
-    "bus",
-    "exchange",
-    "eye",
-    "fire",
-    "position",
-    "pressure",
-    "stress",
-    "advantage",
-    "benefit",
-    "box",
-    "frame",
-    "issue",
-    "step",
-    "cycle",
-    "face",
-    "item",
-    "metal",
-    "paint",
-    "review",
-    "room",
-    "screen",
-    "structure",
-    "view",
-    "account",
-    "ball",
-    "discipline",
-    "medium",
-    "share",
-    "balance",
-    "bit",
-    "black",
-    "bottom",
-    "choice",
-    "gift",
-    "impact",
-    "machine",
-    "shape",
-    "tool",
-    "wind",
-    "address",
-    "average",
-    "career",
-    "culture",
-    "morning",
-    "pot",
-    "sign",
-    "table",
-    "task",
-    "condition",
-    "contact",
-    "credit",
-    "egg",
-    "hope",
-    "ice",
-    "network",
-    "north",
-    "square",
-    "attempt",
-    "date",
-    "effect",
-    "link",
-    "post",
-    "star",
-    "voice",
-    "capital",
-    "challenge",
-    "friend",
-    "self",
-    "shot",
-    "brush",
-    "couple",
-    "debate",
-    "exit",
-    "front",
-    "function",
-    "lack",
-    "living",
-    "plant",
-    "plastic",
-    "spot",
-    "summer",
-    "taste",
-    "theme",
-    "track",
-    "wing",
-    "brain",
-    "button",
-    "click",
-    "desire",
-    "foot",
-    "gas",
-    "influence",
-    "notice",
-    "rain",
-    "wall",
-    "base",
-    "damage",
-    "distance",
-    "feeling",
-    "pair",
-    "savings",
-    "staff",
-    "sugar",
-    "target",
-    "text",
-    "animal",
-    "author",
-    "budget",
-    "discount",
-    "file",
-    "ground",
-    "lesson",
-    "minute",
-    "officer",
-    "phase",
-    "reference",
-    "register",
-    "sky",
-    "stage",
-    "stick",
-    "title",
-    "trouble",
-    "bowl",
-    "bridge",
-    "campaign",
-    "character",
-    "club",
-    "edge",
-    "evidence",
-    "fan",
-    "letter",
-    "lock",
-    "maximum",
-    "novel",
-    "option",
-    "pack",
-    "park",
-    "plenty",
-    "quarter",
-    "skin",
-    "sort",
-    "weight",
-    "baby",
-    "background",
-    "carry",
-    "dish",
-    "factor",
-    "fruit",
-    "glass",
-    "joint",
-    "master",
-    "muscle",
-    "red",
-    "strength",
-    "traffic",
-    "trip",
-    "vegetable",
-    "appeal",
-    "chart",
-    "gear",
-    "ideal",
-    "kitchen",
-    "land",
-    "log",
-    "mother",
-    "net",
-    "party",
-    "principle",
-    "relative",
-    "sale",
-    "season",
-    "signal",
-    "spirit",
-    "street",
-    "tree",
-    "wave",
-    "belt",
-    "bench",
-    "commission",
-    "copy",
-    "drop",
-    "minimum",
-    "path",
-    "progress",
-    "project",
-    "sea",
-    "south",
-    "status",
-    "stuff",
-    "ticket",
-    "tour",
-    "angle",
-    "blue",
-    "breakfast",
-    "confidence",
-    "daughter",
-    "degree",
-    "doctor",
-    "dot",
-    "dream",
-    "duty",
-    "essay",
-    "father",
-    "fee",
-    "finance",
-    "hour",
-    "juice",
-    "limit",
-    "luck",
-    "milk",
-    "mouth",
-    "peace",
-    "pipe",
-    "seat",
-    "stable",
-    "storm",
-    "substance",
-    "team",
-    "trick",
-    "afternoon",
-    "bat",
-    "beach",
-    "blank",
-    "catch",
-    "chain",
-    "consideration",
-    "cream",
-    "crew",
-    "detail",
-    "gold",
-    "interview",
-    "kid",
-    "mark",
-    "match",
-    "mission",
-    "pain",
-    "pleasure",
-    "score",
-    "screw",
-    "sex",
-    "shop",
-    "shower",
-    "suit",
-    "tone",
-    "window",
-    "agent",
-    "band",
-    "block",
-    "bone",
-    "calendar",
-    "cap",
-    "coat",
-    "contest",
-    "corner",
-    "court",
-    "cup",
-    "district",
-    "door",
-    "east",
-    "finger",
-    "garage",
-    "guarantee",
-    "hole",
-    "hook",
-    "implement",
-    "layer",
-    "lecture",
-    "lie",
-    "manner",
-    "meeting",
-    "nose",
-    "parking",
-    "partner",
-    "profile",
-    "respect",
-    "rice",
-    "routine",
-    "schedule",
-    "swimming",
-    "telephone",
-    "tip",
-    "winter",
-    "airline",
-    "bag",
-    "battle",
-    "bed",
-    "bill",
-    "bother",
-    "cake",
-    "code",
-    "curve",
-    "designer",
-    "dimension",
-    "dress",
-    "ease",
-    "emergency",
-    "evening",
-    "extension",
-    "farm",
-    "fight",
-    "gap",
-    "grade",
-    "holiday",
-    "horror",
-    "horse",
-    "host",
-    "husband",
-    "loan",
-    "mistake",
-    "mountain",
-    "nail",
-    "noise",
-    "occasion",
-    "package",
-    "patient",
-    "pause",
-    "phrase",
-    "proof",
-    "race",
-    "relief",
-    "sand",
-    "sentence",
-    "shoulder",
-    "smoke",
-    "stomach",
-    "string",
-    "tourist",
-    "towel",
-    "vacation",
-    "west",
-    "wheel",
-    "wine",
-    "arm",
-    "aside",
-    "associate",
-    "bet",
-    "blow",
-    "border",
-    "branch",
-    "breast",
-    "brother",
-    "buddy",
-    "bunch",
-    "chip",
-    "coach",
-    "cross",
-    "document",
-    "draft",
-    "dust",
-    "expert",
-    "floor",
-    "god",
-    "golf",
-    "habit",
-    "iron",
-    "judge",
-    "knife",
-    "landscape",
-    "league",
-    "mail",
-    "mess",
-    "native",
-    "opening",
-    "parent",
-    "pattern",
-    "pin",
-    "pool",
-    "pound",
-    "request",
-    "salary",
-    "shame",
-    "shelter",
-    "shoe",
-    "silver",
-    "tackle",
-    "tank",
-    "trust",
-    "assist",
-    "bake",
-    "bar",
-    "bell",
-    "bike",
-    "blame",
-    "boy",
-    "brick",
-    "chair",
-    "closet",
-    "clue",
-    "collar",
-    "comment",
-    "conference",
-    "devil",
-    "diet",
-    "fear",
-    "fuel",
-    "glove",
-    "jacket",
-    "lunch",
-    "monitor",
-    "mortgage",
-    "nurse",
-    "pace",
-    "panic",
-    "peak",
-    "plane",
-    "reward",
-    "row",
-    "sandwich",
-    "shock",
-    "spite",
-    "spray",
-    "surprise",
-    "till",
-    "transition",
-    "weekend",
-    "welcome",
-    "yard",
-    "alarm",
-    "bend",
-    "bicycle",
-    "bite",
-    "blind",
-    "bottle",
-    "cable",
-    "candle",
-    "clerk",
-    "cloud",
-    "concert",
-    "counter",
-    "flower",
-    "grandfather",
-    "harm",
-    "knee",
-    "lawyer",
-    "leather",
-    "load",
-    "mirror",
-    "neck",
-    "pension",
-    "plate",
-    "purple",
-    "ruin",
-    "ship",
-    "skirt",
-    "slice",
-    "snow",
-    "specialist",
-    "stroke",
-    "switch",
-    "trash",
-    "tune",
-    "zone",
-    "anger",
-    "award",
-    "bid",
-    "bitter",
-    "boot",
-    "bug",
-    "camp",
-    "candy",
-    "carpet",
-    "cat",
-    "champion",
-    "channel",
-    "clock",
-    "comfort",
-    "cow",
-    "crack",
-    "engineer",
-    "entrance",
-    "fault",
-    "grass",
-    "guy",
-    "hell",
-    "highlight",
-    "incident",
-    "island",
-    "joke",
-    "jury",
-    "leg",
-    "lip",
-    "mate",
-    "motor",
-    "nerve",
-    "passage",
-    "pen",
-    "pride",
-    "priest",
-    "prize",
-    "promise",
-    "resident",
-    "resort",
-    "ring",
-    "roof",
-    "rope",
-    "sail",
-    "scheme",
-    "script",
-    "sock",
-    "station",
-    "toe",
-    "tower",
-    "truck",
-    "witness",
-    "a",
-    "you",
-    "it",
-    "can",
-    "will",
-    "if",
-    "one",
-    "many",
-    "most",
-    "other",
-    "use",
-    "make",
-    "good",
-    "look",
-    "help",
-    "go",
-    "great",
-    "being",
-    "few",
-    "might",
-    "still",
-    "public",
-    "read",
-    "keep",
-    "start",
-    "give",
-    "human",
-    "local",
-    "general",
-    "she",
-    "specific",
-    "long",
-    "play",
-    "feel",
-    "high",
-    "tonight",
-    "put",
-    "common",
-    "set",
-    "change",
-    "simple",
-    "past",
-    "big",
-    "possible",
-    "particular",
-    "today",
-    "major",
-    "personal",
-    "current",
-    "national",
-    "cut",
-    "natural",
-    "physical",
-    "show",
-    "try",
-    "check",
-    "second",
-    "call",
-    "move",
-    "pay",
-    "let",
-    "increase",
-    "single",
-    "individual",
-    "turn",
-    "ask",
-    "buy",
-    "guard",
-    "hold",
-    "main",
-    "offer",
-    "potential",
-    "professional",
-    "international",
-    "travel",
-    "cook",
-    "alternative",
-    "following",
-    "special",
-    "working",
-    "whole",
-    "dance",
-    "excuse",
-    "cold",
-    "commercial",
-    "low",
-    "purchase",
-    "deal",
-    "primary",
-    "worth",
-    "fall",
-    "necessary",
-    "positive",
-    "produce",
-    "search",
-    "present",
-    "spend",
-    "talk",
-    "creative",
-    "tell",
-    "cost",
-    "drive",
-    "green",
-    "support",
-    "glad",
-    "remove",
-    "return",
-    "run",
-    "complex",
-    "due",
-    "effective",
-    "middle",
-    "regular",
-    "reserve",
-    "independent",
-    "leave",
-    "original",
-    "reach",
-    "rest",
-    "serve",
-    "watch",
-    "beautiful",
-    "charge",
-    "active",
-    "break",
-    "negative",
-    "safe",
-    "stay",
-    "visit",
-    "visual",
-    "affect",
-    "cover",
-    "report",
-    "rise",
-    "walk",
-    "white",
-    "beyond",
-    "junior",
-    "pick",
-    "unique",
-    "anything",
-    "classic",
-    "final",
-    "lift",
-    "mix",
-    "private",
-    "stop",
-    "teach",
-    "western",
-    "concern",
-    "familiar",
-    "fly",
-    "official",
-    "broad",
-    "comfortable",
-    "gain",
-    "maybe",
-    "rich",
-    "save",
-    "stand",
-    "young",
-    "fail",
-    "heavy",
-    "hello",
-    "lead",
-    "listen",
-    "valuable",
-    "worry",
-    "handle",
-    "leading",
-    "meet",
-    "release",
-    "sell",
-    "finish",
-    "normal",
-    "press",
-    "ride",
-    "secret",
-    "spread",
-    "spring",
-    "tough",
-    "wait",
-    "brown",
-    "deep",
-    "display",
-    "flow",
-    "hit",
-    "objective",
-    "shoot",
-    "touch",
-    "cancel",
-    "chemical",
-    "cry",
-    "dump",
-    "extreme",
-    "push",
-    "conflict",
-    "eat",
-    "fill",
-    "formal",
-    "jump",
-    "kick",
-    "opposite",
-    "pass",
-    "pitch",
-    "remote",
-    "total",
-    "treat",
-    "vast",
-    "abuse",
-    "beat",
-    "burn",
-    "deposit",
-    "print",
-    "raise",
-    "sleep",
-    "somewhere",
-    "advance",
-    "anywhere",
-    "consist",
-    "dark",
-    "double",
-    "draw",
-    "equal",
-    "fix",
-    "hire",
-    "internal",
-    "join",
-    "kill",
-    "sensitive",
-    "tap",
-    "win",
-    "attack",
-    "claim",
-    "constant",
-    "drag",
-    "drink",
-    "guess",
-    "minor",
-    "pull",
-    "raw",
-    "soft",
-    "solid",
-    "wear",
-    "weird",
-    "wonder",
-    "annual",
-    "count",
-    "dead",
-    "doubt",
-    "feed",
-    "forever",
-    "impress",
-    "nobody",
-    "repeat",
-    "round",
-    "sing",
-    "slide",
-    "strip",
-    "whereas",
-    "wish",
-    "combine",
-    "command",
-    "dig",
-    "divide",
-    "equivalent",
-    "hang",
-    "hunt",
-    "initial",
-    "march",
-    "mention",
-    "smell",
-    "spiritual",
-    "survey",
-    "tie",
-    "adult",
-    "brief",
-    "crazy",
-    "escape",
-    "gather",
-    "hate",
-    "prior",
-    "repair",
-    "rough",
-    "sad",
-    "scratch",
-    "sick",
-    "strike",
-    "employ",
-    "external",
-    "hurt",
-    "illegal",
-    "laugh",
-    "lay",
-    "mobile",
-    "nasty",
-    "ordinary",
-    "respond",
-    "royal",
-    "senior",
-    "split",
-    "strain",
-    "struggle",
-    "swim",
-    "train",
-    "upper",
-    "wash",
-    "yellow",
-    "convert",
-    "crash",
-    "dependent",
-    "fold",
-    "funny",
-    "grab",
-    "hide",
-    "miss",
-    "permit",
-    "quote",
-    "recover",
-    "resolve",
-    "roll",
-    "sink",
-    "slip",
-    "spare",
-    "suspect",
-    "sweet",
-    "swing",
-    "twist",
-    "upstairs",
-    "usual",
-    "abroad",
-    "brave",
-    "calm",
-    "concentrate",
-    "estimate",
-    "grand",
-    "male",
-    "mine",
-    "prompt",
-    "quiet",
-    "refuse",
-    "regret",
-    "reveal",
-    "rush",
-    "shake",
-    "shift",
-    "shine",
-    "steal",
-    "suck",
-    "surround",
-    "anybody",
-    "bear",
-    "brilliant",
-    "dare",
-    "dear",
-    "delay",
-    "drunk",
-    "female",
-    "hurry",
-    "inevitable",
-    "invite",
-    "kiss",
-    "neat",
-    "pop",
-    "punch",
-    "quit",
-    "reply",
-    "representative",
-    "resist",
-    "rip",
-    "rub",
-    "silly",
-    "smile",
-    "spell",
-    "stretch",
-    "stupid",
-    "tear",
-    "temporary",
-    "tomorrow",
-    "wake",
-    "wrap",
-    "yesterday"
+  "people",
+  "history",
+  "way",
+  "art",
+  "world",
+  "information",
+  "map",
+  "two",
+  "family",
+  "government",
+  "health",
+  "system",
+  "computer",
+  "meat",
+  "year",
+  "thanks",
+  "music",
+  "person",
+  "reading",
+  "method",
+  "data",
+  "food",
+  "understanding",
+  "theory",
+  "law",
+  "bird",
+  "literature",
+  "problem",
+  "software",
+  "control",
+  "knowledge",
+  "power",
+  "ability",
+  "economics",
+  "love",
+  "internet",
+  "television",
+  "science",
+  "library",
+  "nature",
+  "fact",
+  "product",
+  "idea",
+  "temperature",
+  "investment",
+  "area",
+  "society",
+  "activity",
+  "story",
+  "industry",
+  "media",
+  "thing",
+  "oven",
+  "community",
+  "definition",
+  "safety",
+  "quality",
+  "development",
+  "language",
+  "management",
+  "player",
+  "variety",
+  "video",
+  "week",
+  "security",
+  "country",
+  "exam",
+  "movie",
+  "organization",
+  "equipment",
+  "physics",
+  "analysis",
+  "policy",
+  "series",
+  "thought",
+  "basis",
+  "boyfriend",
+  "direction",
+  "strategy",
+  "technology",
+  "army",
+  "camera",
+  "freedom",
+  "paper",
+  "environment",
+  "child",
+  "instance",
+  "month",
+  "truth",
+  "marketing",
+  "university",
+  "writing",
+  "article",
+  "department",
+  "difference",
+  "goal",
+  "news",
+  "audience",
+  "fishing",
+  "growth",
+  "income",
+  "marriage",
+  "user",
+  "combination",
+  "failure",
+  "meaning",
+  "medicine",
+  "philosophy",
+  "teacher",
+  "communication",
+  "night",
+  "chemistry",
+  "disease",
+  "disk",
+  "energy",
+  "nation",
+  "road",
+  "role",
+  "soup",
+  "advertising",
+  "location",
+  "success",
+  "addition",
+  "apartment",
+  "education",
+  "math",
+  "moment",
+  "painting",
+  "politics",
+  "attention",
+  "decision",
+  "event",
+  "property",
+  "shopping",
+  "student",
+  "wood",
+  "competition",
+  "distribution",
+  "entertainment",
+  "office",
+  "population",
+  "president",
+  "unit",
+  "category",
+  "cigarette",
+  "context",
+  "introduction",
+  "opportunity",
+  "performance",
+  "driver",
+  "flight",
+  "length",
+  "magazine",
+  "newspaper",
+  "relationship",
+  "teaching",
+  "cell",
+  "dealer",
+  "finding",
+  "lake",
+  "member",
+  "message",
+  "phone",
+  "scene",
+  "appearance",
+  "association",
+  "concept",
+  "customer",
+  "death",
+  "discussion",
+  "housing",
+  "inflation",
+  "insurance",
+  "mood",
+  "woman",
+  "advice",
+  "blood",
+  "effort",
+  "expression",
+  "importance",
+  "opinion",
+  "payment",
+  "reality",
+  "responsibility",
+  "situation",
+  "skill",
+  "statement",
+  "wealth",
+  "application",
+  "city",
+  "county",
+  "depth",
+  "estate",
+  "foundation",
+  "grandmother",
+  "heart",
+  "perspective",
+  "photo",
+  "recipe",
+  "studio",
+  "topic",
+  "collection",
+  "depression",
+  "imagination",
+  "passion",
+  "percentage",
+  "resource",
+  "setting",
+  "ad",
+  "agency",
+  "college",
+  "connection",
+  "criticism",
+  "debt",
+  "description",
+  "memory",
+  "patience",
+  "secretary",
+  "solution",
+  "administration",
+  "aspect",
+  "attitude",
+  "director",
+  "personality",
+  "psychology",
+  "recommendation",
+  "response",
+  "selection",
+  "storage",
+  "version",
+  "alcohol",
+  "argument",
+  "complaint",
+  "contract",
+  "emphasis",
+  "highway",
+  "loss",
+  "membership",
+  "possession",
+  "preparation",
+  "steak",
+  "union",
+  "agreement",
+  "cancer",
+  "currency",
+  "employment",
+  "engineering",
+  "entry",
+  "interaction",
+  "mixture",
+  "preference",
+  "region",
+  "republic",
+  "tradition",
+  "virus",
+  "actor",
+  "classroom",
+  "delivery",
+  "device",
+  "difficulty",
+  "drama",
+  "election",
+  "engine",
+  "football",
+  "guidance",
+  "hotel",
+  "owner",
+  "priority",
+  "protection",
+  "suggestion",
+  "tension",
+  "variation",
+  "anxiety",
+  "atmosphere",
+  "awareness",
+  "bath",
+  "bread",
+  "candidate",
+  "climate",
+  "comparison",
+  "confusion",
+  "construction",
+  "elevator",
+  "emotion",
+  "employee",
+  "employer",
+  "guest",
+  "height",
+  "leadership",
+  "mall",
+  "manager",
+  "operation",
+  "recording",
+  "sample",
+  "transportation",
+  "charity",
+  "cousin",
+  "disaster",
+  "editor",
+  "efficiency",
+  "excitement",
+  "extent",
+  "feedback",
+  "guitar",
+  "homework",
+  "leader",
+  "mom",
+  "outcome",
+  "permission",
+  "presentation",
+  "promotion",
+  "reflection",
+  "refrigerator",
+  "resolution",
+  "revenue",
+  "session",
+  "singer",
+  "tennis",
+  "basket",
+  "bonus",
+  "cabinet",
+  "childhood",
+  "church",
+  "clothes",
+  "coffee",
+  "dinner",
+  "drawing",
+  "hair",
+  "hearing",
+  "initiative",
+  "judgment",
+  "lab",
+  "measurement",
+  "mode",
+  "mud",
+  "orange",
+  "poetry",
+  "police",
+  "possibility",
+  "procedure",
+  "queen",
+  "ratio",
+  "relation",
+  "restaurant",
+  "satisfaction",
+  "sector",
+  "signature",
+  "significance",
+  "song",
+  "tooth",
+  "town",
+  "vehicle",
+  "volume",
+  "wife",
+  "accident",
+  "airport",
+  "appointment",
+  "arrival",
+  "assumption",
+  "baseball",
+  "chapter",
+  "committee",
+  "conversation",
+  "database",
+  "enthusiasm",
+  "error",
+  "explanation",
+  "farmer",
+  "gate",
+  "girl",
+  "hall",
+  "historian",
+  "hospital",
+  "injury",
+  "instruction",
+  "maintenance",
+  "manufacturer",
+  "meal",
+  "perception",
+  "pie",
+  "poem",
+  "presence",
+  "proposal",
+  "reception",
+  "replacement",
+  "revolution",
+  "river",
+  "son",
+  "speech",
+  "tea",
+  "village",
+  "warning",
+  "winner",
+  "worker",
+  "writer",
+  "assistance",
+  "breath",
+  "buyer",
+  "chest",
+  "chocolate",
+  "conclusion",
+  "contribution",
+  "cookie",
+  "courage",
+  "dad",
+  "desk",
+  "drawer",
+  "establishment",
+  "examination",
+  "garbage",
+  "grocery",
+  "honey",
+  "impression",
+  "improvement",
+  "independence",
+  "insect",
+  "inspection",
+  "inspector",
+  "king",
+  "ladder",
+  "menu",
+  "penalty",
+  "piano",
+  "potato",
+  "profession",
+  "professor",
+  "quantity",
+  "reaction",
+  "requirement",
+  "salad",
+  "sister",
+  "supermarket",
+  "tongue",
+  "weakness",
+  "wedding",
+  "affair",
+  "ambition",
+  "analyst",
+  "apple",
+  "assignment",
+  "assistant",
+  "bathroom",
+  "bedroom",
+  "beer",
+  "birthday",
+  "celebration",
+  "championship",
+  "cheek",
+  "client",
+  "consequence",
+  "departure",
+  "diamond",
+  "dirt",
+  "ear",
+  "fortune",
+  "friendship",
+  "funeral",
+  "gene",
+  "girlfriend",
+  "hat",
+  "indication",
+  "intention",
+  "lady",
+  "midnight",
+  "negotiation",
+  "obligation",
+  "passenger",
+  "pizza",
+  "platform",
+  "poet",
+  "pollution",
+  "recognition",
+  "reputation",
+  "shirt",
+  "sir",
+  "speaker",
+  "stranger",
+  "surgery",
+  "sympathy",
+  "tale",
+  "throat",
+  "trainer",
+  "uncle",
+  "youth",
+  "time",
+  "work",
+  "film",
+  "water",
+  "money",
+  "example",
+  "while",
+  "business",
+  "study",
+  "game",
+  "life",
+  "form",
+  "air",
+  "day",
+  "place",
+  "number",
+  "part",
+  "field",
+  "fish",
+  "back",
+  "process",
+  "heat",
+  "hand",
+  "experience",
+  "job",
+  "book",
+  "end",
+  "point",
+  "type",
+  "home",
+  "economy",
+  "value",
+  "body",
+  "market",
+  "guide",
+  "interest",
+  "state",
+  "radio",
+  "course",
+  "company",
+  "price",
+  "size",
+  "card",
+  "list",
+  "mind",
+  "trade",
+  "line",
+  "care",
+  "group",
+  "risk",
+  "word",
+  "fat",
+  "force",
+  "key",
+  "light",
+  "training",
+  "name",
+  "school",
+  "top",
+  "amount",
+  "level",
+  "order",
+  "practice",
+  "research",
+  "sense",
+  "service",
+  "piece",
+  "web",
+  "boss",
+  "sport",
+  "fun",
+  "house",
+  "page",
+  "term",
+  "test",
+  "answer",
+  "sound",
+  "focus",
+  "matter",
+  "kind",
+  "soil",
+  "board",
+  "oil",
+  "picture",
+  "access",
+  "garden",
+  "range",
+  "rate",
+  "reason",
+  "future",
+  "site",
+  "demand",
+  "exercise",
+  "image",
+  "case",
+  "cause",
+  "coast",
+  "action",
+  "age",
+  "bad",
+  "boat",
+  "record",
+  "result",
+  "section",
+  "building",
+  "mouse",
+  "cash",
+  "class",
+  "nothing",
+  "period",
+  "plan",
+  "store",
+  "tax",
+  "side",
+  "subject",
+  "space",
+  "rule",
+  "stock",
+  "weather",
+  "chance",
+  "figure",
+  "man",
+  "model",
+  "source",
+  "beginning",
+  "earth",
+  "program",
+  "chicken",
+  "design",
+  "feature",
+  "head",
+  "material",
+  "purpose",
+  "question",
+  "rock",
+  "salt",
+  "act",
+  "birth",
+  "car",
+  "dog",
+  "object",
+  "scale",
+  "sun",
+  "note",
+  "profit",
+  "rent",
+  "speed",
+  "style",
+  "war",
+  "bank",
+  "craft",
+  "half",
+  "inside",
+  "outside",
+  "standard",
+  "bus",
+  "exchange",
+  "eye",
+  "fire",
+  "position",
+  "pressure",
+  "stress",
+  "advantage",
+  "benefit",
+  "box",
+  "frame",
+  "issue",
+  "step",
+  "cycle",
+  "face",
+  "item",
+  "metal",
+  "paint",
+  "review",
+  "room",
+  "screen",
+  "structure",
+  "view",
+  "account",
+  "ball",
+  "discipline",
+  "medium",
+  "share",
+  "balance",
+  "bit",
+  "black",
+  "bottom",
+  "choice",
+  "gift",
+  "impact",
+  "machine",
+  "shape",
+  "tool",
+  "wind",
+  "address",
+  "average",
+  "career",
+  "culture",
+  "morning",
+  "pot",
+  "sign",
+  "table",
+  "task",
+  "condition",
+  "contact",
+  "credit",
+  "egg",
+  "hope",
+  "ice",
+  "network",
+  "north",
+  "square",
+  "attempt",
+  "date",
+  "effect",
+  "link",
+  "post",
+  "star",
+  "voice",
+  "capital",
+  "challenge",
+  "friend",
+  "self",
+  "shot",
+  "brush",
+  "couple",
+  "debate",
+  "exit",
+  "front",
+  "function",
+  "lack",
+  "living",
+  "plant",
+  "plastic",
+  "spot",
+  "summer",
+  "taste",
+  "theme",
+  "track",
+  "wing",
+  "brain",
+  "button",
+  "click",
+  "desire",
+  "foot",
+  "gas",
+  "influence",
+  "notice",
+  "rain",
+  "wall",
+  "base",
+  "damage",
+  "distance",
+  "feeling",
+  "pair",
+  "savings",
+  "staff",
+  "sugar",
+  "target",
+  "text",
+  "animal",
+  "author",
+  "budget",
+  "discount",
+  "file",
+  "ground",
+  "lesson",
+  "minute",
+  "officer",
+  "phase",
+  "reference",
+  "register",
+  "sky",
+  "stage",
+  "stick",
+  "title",
+  "trouble",
+  "bowl",
+  "bridge",
+  "campaign",
+  "character",
+  "club",
+  "edge",
+  "evidence",
+  "fan",
+  "letter",
+  "lock",
+  "maximum",
+  "novel",
+  "option",
+  "pack",
+  "park",
+  "plenty",
+  "quarter",
+  "skin",
+  "sort",
+  "weight",
+  "baby",
+  "background",
+  "carry",
+  "dish",
+  "factor",
+  "fruit",
+  "glass",
+  "joint",
+  "master",
+  "muscle",
+  "red",
+  "strength",
+  "traffic",
+  "trip",
+  "vegetable",
+  "appeal",
+  "chart",
+  "gear",
+  "ideal",
+  "kitchen",
+  "land",
+  "log",
+  "mother",
+  "net",
+  "party",
+  "principle",
+  "relative",
+  "sale",
+  "season",
+  "signal",
+  "spirit",
+  "street",
+  "tree",
+  "wave",
+  "belt",
+  "bench",
+  "commission",
+  "copy",
+  "drop",
+  "minimum",
+  "path",
+  "progress",
+  "project",
+  "sea",
+  "south",
+  "status",
+  "stuff",
+  "ticket",
+  "tour",
+  "angle",
+  "blue",
+  "breakfast",
+  "confidence",
+  "daughter",
+  "degree",
+  "doctor",
+  "dot",
+  "dream",
+  "duty",
+  "essay",
+  "father",
+  "fee",
+  "finance",
+  "hour",
+  "juice",
+  "limit",
+  "luck",
+  "milk",
+  "mouth",
+  "peace",
+  "pipe",
+  "seat",
+  "stable",
+  "storm",
+  "substance",
+  "team",
+  "trick",
+  "afternoon",
+  "bat",
+  "beach",
+  "blank",
+  "catch",
+  "chain",
+  "consideration",
+  "cream",
+  "crew",
+  "detail",
+  "gold",
+  "interview",
+  "kid",
+  "mark",
+  "match",
+  "mission",
+  "pain",
+  "pleasure",
+  "score",
+  "screw",
+  "sex",
+  "shop",
+  "shower",
+  "suit",
+  "tone",
+  "window",
+  "agent",
+  "band",
+  "block",
+  "bone",
+  "calendar",
+  "cap",
+  "coat",
+  "contest",
+  "corner",
+  "court",
+  "cup",
+  "district",
+  "door",
+  "east",
+  "finger",
+  "garage",
+  "guarantee",
+  "hole",
+  "hook",
+  "implement",
+  "layer",
+  "lecture",
+  "lie",
+  "manner",
+  "meeting",
+  "nose",
+  "parking",
+  "partner",
+  "profile",
+  "respect",
+  "rice",
+  "routine",
+  "schedule",
+  "swimming",
+  "telephone",
+  "tip",
+  "winter",
+  "airline",
+  "bag",
+  "battle",
+  "bed",
+  "bill",
+  "bother",
+  "cake",
+  "code",
+  "curve",
+  "designer",
+  "dimension",
+  "dress",
+  "ease",
+  "emergency",
+  "evening",
+  "extension",
+  "farm",
+  "fight",
+  "gap",
+  "grade",
+  "holiday",
+  "horror",
+  "horse",
+  "host",
+  "husband",
+  "loan",
+  "mistake",
+  "mountain",
+  "nail",
+  "noise",
+  "occasion",
+  "package",
+  "patient",
+  "pause",
+  "phrase",
+  "proof",
+  "race",
+  "relief",
+  "sand",
+  "sentence",
+  "shoulder",
+  "smoke",
+  "stomach",
+  "string",
+  "tourist",
+  "towel",
+  "vacation",
+  "west",
+  "wheel",
+  "wine",
+  "arm",
+  "aside",
+  "associate",
+  "bet",
+  "blow",
+  "border",
+  "branch",
+  "breast",
+  "brother",
+  "buddy",
+  "bunch",
+  "chip",
+  "coach",
+  "cross",
+  "document",
+  "draft",
+  "dust",
+  "expert",
+  "floor",
+  "god",
+  "golf",
+  "habit",
+  "iron",
+  "judge",
+  "knife",
+  "landscape",
+  "league",
+  "mail",
+  "mess",
+  "native",
+  "opening",
+  "parent",
+  "pattern",
+  "pin",
+  "pool",
+  "pound",
+  "request",
+  "salary",
+  "shame",
+  "shelter",
+  "shoe",
+  "silver",
+  "tackle",
+  "tank",
+  "trust",
+  "assist",
+  "bake",
+  "bar",
+  "bell",
+  "bike",
+  "blame",
+  "boy",
+  "brick",
+  "chair",
+  "closet",
+  "clue",
+  "collar",
+  "comment",
+  "conference",
+  "devil",
+  "diet",
+  "fear",
+  "fuel",
+  "glove",
+  "jacket",
+  "lunch",
+  "monitor",
+  "mortgage",
+  "nurse",
+  "pace",
+  "panic",
+  "peak",
+  "plane",
+  "reward",
+  "row",
+  "sandwich",
+  "shock",
+  "spite",
+  "spray",
+  "surprise",
+  "till",
+  "transition",
+  "weekend",
+  "welcome",
+  "yard",
+  "alarm",
+  "bend",
+  "bicycle",
+  "bite",
+  "blind",
+  "bottle",
+  "cable",
+  "candle",
+  "clerk",
+  "cloud",
+  "concert",
+  "counter",
+  "flower",
+  "grandfather",
+  "harm",
+  "knee",
+  "lawyer",
+  "leather",
+  "load",
+  "mirror",
+  "neck",
+  "pension",
+  "plate",
+  "purple",
+  "ruin",
+  "ship",
+  "skirt",
+  "slice",
+  "snow",
+  "specialist",
+  "stroke",
+  "switch",
+  "trash",
+  "tune",
+  "zone",
+  "anger",
+  "award",
+  "bid",
+  "bitter",
+  "boot",
+  "bug",
+  "camp",
+  "candy",
+  "carpet",
+  "cat",
+  "champion",
+  "channel",
+  "clock",
+  "comfort",
+  "cow",
+  "crack",
+  "engineer",
+  "entrance",
+  "fault",
+  "grass",
+  "guy",
+  "hell",
+  "highlight",
+  "incident",
+  "island",
+  "joke",
+  "jury",
+  "leg",
+  "lip",
+  "mate",
+  "motor",
+  "nerve",
+  "passage",
+  "pen",
+  "pride",
+  "priest",
+  "prize",
+  "promise",
+  "resident",
+  "resort",
+  "ring",
+  "roof",
+  "rope",
+  "sail",
+  "scheme",
+  "script",
+  "sock",
+  "station",
+  "toe",
+  "tower",
+  "truck",
+  "witness",
+  "a",
+  "you",
+  "it",
+  "can",
+  "will",
+  "if",
+  "one",
+  "many",
+  "most",
+  "other",
+  "use",
+  "make",
+  "good",
+  "look",
+  "help",
+  "go",
+  "great",
+  "being",
+  "few",
+  "might",
+  "still",
+  "public",
+  "read",
+  "keep",
+  "start",
+  "give",
+  "human",
+  "local",
+  "general",
+  "she",
+  "specific",
+  "long",
+  "play",
+  "feel",
+  "high",
+  "tonight",
+  "put",
+  "common",
+  "set",
+  "change",
+  "simple",
+  "past",
+  "big",
+  "possible",
+  "particular",
+  "today",
+  "major",
+  "personal",
+  "current",
+  "national",
+  "cut",
+  "natural",
+  "physical",
+  "show",
+  "try",
+  "check",
+  "second",
+  "call",
+  "move",
+  "pay",
+  "let",
+  "increase",
+  "single",
+  "individual",
+  "turn",
+  "ask",
+  "buy",
+  "guard",
+  "hold",
+  "main",
+  "offer",
+  "potential",
+  "professional",
+  "international",
+  "travel",
+  "cook",
+  "alternative",
+  "following",
+  "special",
+  "working",
+  "whole",
+  "dance",
+  "excuse",
+  "cold",
+  "commercial",
+  "low",
+  "purchase",
+  "deal",
+  "primary",
+  "worth",
+  "fall",
+  "necessary",
+  "positive",
+  "produce",
+  "search",
+  "present",
+  "spend",
+  "talk",
+  "creative",
+  "tell",
+  "cost",
+  "drive",
+  "green",
+  "support",
+  "glad",
+  "remove",
+  "return",
+  "run",
+  "complex",
+  "due",
+  "effective",
+  "middle",
+  "regular",
+  "reserve",
+  "independent",
+  "leave",
+  "original",
+  "reach",
+  "rest",
+  "serve",
+  "watch",
+  "beautiful",
+  "charge",
+  "active",
+  "break",
+  "negative",
+  "safe",
+  "stay",
+  "visit",
+  "visual",
+  "affect",
+  "cover",
+  "report",
+  "rise",
+  "walk",
+  "white",
+  "beyond",
+  "junior",
+  "pick",
+  "unique",
+  "anything",
+  "classic",
+  "final",
+  "lift",
+  "mix",
+  "private",
+  "stop",
+  "teach",
+  "western",
+  "concern",
+  "familiar",
+  "fly",
+  "official",
+  "broad",
+  "comfortable",
+  "gain",
+  "maybe",
+  "rich",
+  "save",
+  "stand",
+  "young",
+  "fail",
+  "heavy",
+  "hello",
+  "lead",
+  "listen",
+  "valuable",
+  "worry",
+  "handle",
+  "leading",
+  "meet",
+  "release",
+  "sell",
+  "finish",
+  "normal",
+  "press",
+  "ride",
+  "secret",
+  "spread",
+  "spring",
+  "tough",
+  "wait",
+  "brown",
+  "deep",
+  "display",
+  "flow",
+  "hit",
+  "objective",
+  "shoot",
+  "touch",
+  "cancel",
+  "chemical",
+  "cry",
+  "dump",
+  "extreme",
+  "push",
+  "conflict",
+  "eat",
+  "fill",
+  "formal",
+  "jump",
+  "kick",
+  "opposite",
+  "pass",
+  "pitch",
+  "remote",
+  "total",
+  "treat",
+  "vast",
+  "abuse",
+  "beat",
+  "burn",
+  "deposit",
+  "print",
+  "raise",
+  "sleep",
+  "somewhere",
+  "advance",
+  "anywhere",
+  "consist",
+  "dark",
+  "double",
+  "draw",
+  "equal",
+  "fix",
+  "hire",
+  "internal",
+  "join",
+  "kill",
+  "sensitive",
+  "tap",
+  "win",
+  "attack",
+  "claim",
+  "constant",
+  "drag",
+  "drink",
+  "guess",
+  "minor",
+  "pull",
+  "raw",
+  "soft",
+  "solid",
+  "wear",
+  "weird",
+  "wonder",
+  "annual",
+  "count",
+  "dead",
+  "doubt",
+  "feed",
+  "forever",
+  "impress",
+  "nobody",
+  "repeat",
+  "round",
+  "sing",
+  "slide",
+  "strip",
+  "whereas",
+  "wish",
+  "combine",
+  "command",
+  "dig",
+  "divide",
+  "equivalent",
+  "hang",
+  "hunt",
+  "initial",
+  "march",
+  "mention",
+  "smell",
+  "spiritual",
+  "survey",
+  "tie",
+  "adult",
+  "brief",
+  "crazy",
+  "escape",
+  "gather",
+  "hate",
+  "prior",
+  "repair",
+  "rough",
+  "sad",
+  "scratch",
+  "sick",
+  "strike",
+  "employ",
+  "external",
+  "hurt",
+  "illegal",
+  "laugh",
+  "lay",
+  "mobile",
+  "nasty",
+  "ordinary",
+  "respond",
+  "royal",
+  "senior",
+  "split",
+  "strain",
+  "struggle",
+  "swim",
+  "train",
+  "upper",
+  "wash",
+  "yellow",
+  "convert",
+  "crash",
+  "dependent",
+  "fold",
+  "funny",
+  "grab",
+  "hide",
+  "miss",
+  "permit",
+  "quote",
+  "recover",
+  "resolve",
+  "roll",
+  "sink",
+  "slip",
+  "spare",
+  "suspect",
+  "sweet",
+  "swing",
+  "twist",
+  "upstairs",
+  "usual",
+  "abroad",
+  "brave",
+  "calm",
+  "concentrate",
+  "estimate",
+  "grand",
+  "male",
+  "mine",
+  "prompt",
+  "quiet",
+  "refuse",
+  "regret",
+  "reveal",
+  "rush",
+  "shake",
+  "shift",
+  "shine",
+  "steal",
+  "suck",
+  "surround",
+  "anybody",
+  "bear",
+  "brilliant",
+  "dare",
+  "dear",
+  "delay",
+  "drunk",
+  "female",
+  "hurry",
+  "inevitable",
+  "invite",
+  "kiss",
+  "neat",
+  "pop",
+  "punch",
+  "quit",
+  "reply",
+  "representative",
+  "resist",
+  "rip",
+  "rub",
+  "silly",
+  "smile",
+  "spell",
+  "stretch",
+  "stupid",
+  "tear",
+  "temporary",
+  "tomorrow",
+  "wake",
+  "wrap",
+  "yesterday"
 ]
 
 const adj = [
-    "abandoned",
-    "able",
-    "absolute",
-    "adorable",
-    "adventurous",
-    "academic",
-    "acceptable",
-    "acclaimed",
-    "accomplished",
-    "accurate",
-    "aching",
-    "acidic",
-    "acrobatic",
-    "active",
-    "actual",
-    "adept",
-    "admirable",
-    "admired",
-    "adolescent",
-    "adorable",
-    "adored",
-    "advanced",
-    "afraid",
-    "affectionate",
-    "aged",
-    "aggravating",
-    "aggressive",
-    "agile",
-    "agitated",
-    "agonizing",
-    "agreeable",
-    "ajar",
-    "alarmed",
-    "alarming",
-    "alert",
-    "alienated",
-    "alive",
-    "all",
-    "altruistic",
-    "amazing",
-    "ambitious",
-    "ample",
-    "amused",
-    "amusing",
-    "anchored",
-    "ancient",
-    "angelic",
-    "angry",
-    "anguished",
-    "animated",
-    "annual",
-    "another",
-    "antique",
-    "anxious",
-    "any",
-    "apprehensive",
-    "appropriate",
-    "apt",
-    "arctic",
-    "arid",
-    "aromatic",
-    "artistic",
-    "ashamed",
-    "assured",
-    "astonishing",
-    "athletic",
-    "attached",
-    "attentive",
-    "attractive",
-    "austere",
-    "authentic",
-    "authorized",
-    "automatic",
-    "avaricious",
-    "average",
-    "aware",
-    "awesome",
-    "awful",
-    "awkward",
-    "babyish",
-    "bad",
-    "back",
-    "baggy",
-    "bare",
-    "barren",
-    "basic",
-    "beautiful",
-    "belated",
-    "beloved",
-    "beneficial",
-    "better",
-    "best",
-    "bewitched",
-    "big",
-    "big-hearted",
-    "biodegradable",
-    "bite-sized",
-    "bitter",
-    "black",
-    "black-and-white",
-    "bland",
-    "blank",
-    "blaring",
-    "bleak",
-    "blind",
-    "blissful",
-    "blond",
-    "blue",
-    "blushing",
-    "bogus",
-    "boiling",
-    "bold",
-    "bony",
-    "boring",
-    "bossy",
-    "both",
-    "bouncy",
-    "bountiful",
-    "bowed",
-    "brave",
-    "breakable",
-    "brief",
-    "bright",
-    "brilliant",
-    "brisk",
-    "broken",
-    "bronze",
-    "brown",
-    "bruised",
-    "bubbly",
-    "bulky",
-    "bumpy",
-    "buoyant",
-    "burdensome",
-    "burly",
-    "bustling",
-    "busy",
-    "buttery",
-    "buzzing",
-    "calculating",
-    "calm",
-    "candid",
-    "canine",
-    "capital",
-    "carefree",
-    "careful",
-    "careless",
-    "caring",
-    "cautious",
-    "cavernous",
-    "celebrated",
-    "charming",
-    "cheap",
-    "cheerful",
-    "cheery",
-    "chief",
-    "chilly",
-    "chubby",
-    "circular",
-    "classic",
-    "clean",
-    "clear",
-    "clear-cut",
-    "clever",
-    "close",
-    "closed",
-    "cloudy",
-    "clueless",
-    "clumsy",
-    "cluttered",
-    "coarse",
-    "cold",
-    "colorful",
-    "colorless",
-    "colossal",
-    "comfortable",
-    "common",
-    "compassionate",
-    "competent",
-    "complete",
-    "complex",
-    "complicated",
-    "composed",
-    "concerned",
-    "concrete",
-    "confused",
-    "conscious",
-    "considerate",
-    "constant",
-    "content",
-    "conventional",
-    "cooked",
-    "cool",
-    "cooperative",
-    "coordinated",
-    "corny",
-    "corrupt",
-    "costly",
-    "courageous",
-    "courteous",
-    "crafty",
-    "crazy",
-    "creamy",
-    "creative",
-    "creepy",
-    "criminal",
-    "crisp",
-    "critical",
-    "crooked",
-    "crowded",
-    "cruel",
-    "crushing",
-    "cuddly",
-    "cultivated",
-    "cultured",
-    "cumbersome",
-    "curly",
-    "curvy",
-    "cute",
-    "cylindrical",
-    "damaged",
-    "damp",
-    "dangerous",
-    "dapper",
-    "daring",
-    "darling",
-    "dark",
-    "dazzling",
-    "dead",
-    "deadly",
-    "deafening",
-    "dear",
-    "dearest",
-    "decent",
-    "decimal",
-    "decisive",
-    "deep",
-    "defenseless",
-    "defensive",
-    "defiant",
-    "deficient",
-    "definite",
-    "definitive",
-    "delayed",
-    "delectable",
-    "delicious",
-    "delightful",
-    "delirious",
-    "demanding",
-    "dense",
-    "dental",
-    "dependable",
-    "dependent",
-    "descriptive",
-    "deserted",
-    "detailed",
-    "determined",
-    "devoted",
-    "different",
-    "difficult",
-    "digital",
-    "diligent",
-    "dim",
-    "dimpled",
-    "dimwitted",
-    "direct",
-    "disastrous",
-    "discrete",
-    "disfigured",
-    "disgusting",
-    "disloyal",
-    "dismal",
-    "distant",
-    "downright",
-    "dreary",
-    "dirty",
-    "disguised",
-    "dishonest",
-    "dismal",
-    "distant",
-    "distinct",
-    "distorted",
-    "dizzy",
-    "dopey",
-    "doting",
-    "double",
-    "downright",
-    "drab",
-    "drafty",
-    "dramatic",
-    "dreary",
-    "droopy",
-    "dry",
-    "dual",
-    "dull",
-    "dutiful",
-    "each",
-    "eager",
-    "earnest",
-    "early",
-    "easy",
-    "easy-going",
-    "ecstatic",
-    "edible",
-    "educated",
-    "elaborate",
-    "elastic",
-    "elated",
-    "elderly",
-    "electric",
-    "elegant",
-    "elementary",
-    "elliptical",
-    "embarrassed",
-    "embellished",
-    "eminent",
-    "emotional",
-    "empty",
-    "enchanted",
-    "enchanting",
-    "energetic",
-    "enlightened",
-    "enormous",
-    "enraged",
-    "entire",
-    "envious",
-    "equal",
-    "equatorial",
-    "essential",
-    "esteemed",
-    "ethical",
-    "euphoric",
-    "even",
-    "evergreen",
-    "everlasting",
-    "every",
-    "evil",
-    "exalted",
-    "excellent",
-    "exemplary",
-    "exhausted",
-    "excitable",
-    "excited",
-    "exciting",
-    "exotic",
-    "expensive",
-    "experienced",
-    "expert",
-    "extraneous",
-    "extroverted",
-    "extra-large",
-    "extra-small",
-    "fabulous",
-    "failing",
-    "faint",
-    "fair",
-    "faithful",
-    "fake",
-    "false",
-    "familiar",
-    "famous",
-    "fancy",
-    "fantastic",
-    "far",
-    "faraway",
-    "far-flung",
-    "far-off",
-    "fast",
-    "fat",
-    "fatal",
-    "fatherly",
-    "favorable",
-    "favorite",
-    "fearful",
-    "fearless",
-    "feisty",
-    "feline",
-    "female",
-    "feminine",
-    "few",
-    "fickle",
-    "filthy",
-    "fine",
-    "finished",
-    "firm",
-    "first",
-    "firsthand",
-    "fitting",
-    "fixed",
-    "flaky",
-    "flamboyant",
-    "flashy",
-    "flat",
-    "flawed",
-    "flawless",
-    "flickering",
-    "flimsy",
-    "flippant",
-    "flowery",
-    "fluffy",
-    "fluid",
-    "flustered",
-    "focused",
-    "fond",
-    "foolhardy",
-    "foolish",
-    "forceful",
-    "forked",
-    "formal",
-    "forsaken",
-    "forthright",
-    "fortunate",
-    "fragrant",
-    "frail",
-    "frank",
-    "frayed",
-    "free",
-    "French",
-    "fresh",
-    "frequent",
-    "friendly",
-    "frightened",
-    "frightening",
-    "frigid",
-    "frilly",
-    "frizzy",
-    "frivolous",
-    "front",
-    "frosty",
-    "frozen",
-    "frugal",
-    "fruitful",
-    "full",
-    "fumbling",
-    "functional",
-    "funny",
-    "fussy",
-    "fuzzy",
-    "gargantuan",
-    "gaseous",
-    "general",
-    "generous",
-    "gentle",
-    "genuine",
-    "giant",
-    "giddy",
-    "gigantic",
-    "gifted",
-    "giving",
-    "glamorous",
-    "glaring",
-    "glass",
-    "gleaming",
-    "gleeful",
-    "glistening",
-    "glittering",
-    "gloomy",
-    "glorious",
-    "glossy",
-    "glum",
-    "golden",
-    "good",
-    "good-natured",
-    "gorgeous",
-    "graceful",
-    "gracious",
-    "grand",
-    "grandiose",
-    "granular",
-    "grateful",
-    "grave",
-    "gray",
-    "great",
-    "greedy",
-    "green",
-    "gregarious",
-    "grim",
-    "grimy",
-    "gripping",
-    "grizzled",
-    "gross",
-    "grotesque",
-    "grouchy",
-    "grounded",
-    "growing",
-    "growling",
-    "grown",
-    "grubby",
-    "gruesome",
-    "grumpy",
-    "guilty",
-    "gullible",
-    "gummy",
-    "hairy",
-    "half",
-    "handmade",
-    "handsome",
-    "handy",
-    "happy",
-    "happy-go-lucky",
-    "hard",
-    "hard-to-find",
-    "harmful",
-    "harmless",
-    "harmonious",
-    "harsh",
-    "hasty",
-    "hateful",
-    "haunting",
-    "healthy",
-    "heartfelt",
-    "hearty",
-    "heavenly",
-    "heavy",
-    "hefty",
-    "helpful",
-    "helpless",
-    "hidden",
-    "hideous",
-    "high",
-    "high-level",
-    "hilarious",
-    "hoarse",
-    "hollow",
-    "homely",
-    "honest",
-    "honorable",
-    "honored",
-    "hopeful",
-    "horrible",
-    "hospitable",
-    "hot",
-    "huge",
-    "humble",
-    "humiliating",
-    "humming",
-    "humongous",
-    "hungry",
-    "hurtful",
-    "husky",
-    "icky",
-    "icy",
-    "ideal",
-    "idealistic",
-    "identical",
-    "idle",
-    "idiotic",
-    "idolized",
-    "ignorant",
-    "ill",
-    "illegal",
-    "ill-fated",
-    "ill-informed",
-    "illiterate",
-    "illustrious",
-    "imaginary",
-    "imaginative",
-    "immaculate",
-    "immaterial",
-    "immediate",
-    "immense",
-    "impassioned",
-    "impeccable",
-    "impartial",
-    "imperfect",
-    "imperturbable",
-    "impish",
-    "impolite",
-    "important",
-    "impossible",
-    "impractical",
-    "impressionable",
-    "impressive",
-    "improbable",
-    "impure",
-    "inborn",
-    "incomparable",
-    "incompatible",
-    "incomplete",
-    "inconsequential",
-    "incredible",
-    "indelible",
-    "inexperienced",
-    "indolent",
-    "infamous",
-    "infantile",
-    "infatuated",
-    "inferior",
-    "infinite",
-    "informal",
-    "innocent",
-    "insecure",
-    "insidious",
-    "insignificant",
-    "insistent",
-    "instructive",
-    "insubstantial",
-    "intelligent",
-    "intent",
-    "intentional",
-    "interesting",
-    "internal",
-    "international",
-    "intrepid",
-    "ironclad",
-    "irresponsible",
-    "irritating",
-    "itchy",
-    "jaded",
-    "jagged",
-    "jam-packed",
-    "jaunty",
-    "jealous",
-    "jittery",
-    "joint",
-    "jolly",
-    "jovial",
-    "joyful",
-    "joyous",
-    "jubilant",
-    "judicious",
-    "juicy",
-    "jumbo",
-    "junior",
-    "jumpy",
-    "juvenile",
-    "kaleidoscopic",
-    "keen",
-    "key",
-    "kind",
-    "kindhearted",
-    "kindly",
-    "klutzy",
-    "knobby",
-    "knotty",
-    "knowledgeable",
-    "knowing",
-    "known",
-    "kooky",
-    "kosher",
-    "lame",
-    "lanky",
-    "large",
-    "last",
-    "lasting",
-    "late",
-    "lavish",
-    "lawful",
-    "lazy",
-    "leading",
-    "lean",
-    "leafy",
-    "left",
-    "legal",
-    "legitimate",
-    "light",
-    "lighthearted",
-    "likable",
-    "likely",
-    "limited",
-    "limp",
-    "limping",
-    "linear",
-    "lined",
-    "liquid",
-    "little",
-    "live",
-    "lively",
-    "livid",
-    "loathsome",
-    "lone",
-    "lonely",
-    "long",
-    "long-term",
-    "loose",
-    "lopsided",
-    "lost",
-    "loud",
-    "lovable",
-    "lovely",
-    "loving",
-    "low",
-    "loyal",
-    "lucky",
-    "lumbering",
-    "luminous",
-    "lumpy",
-    "lustrous",
-    "luxurious",
-    "mad",
-    "made-up",
-    "magnificent",
-    "majestic",
-    "major",
-    "male",
-    "mammoth",
-    "married",
-    "marvelous",
-    "masculine",
-    "massive",
-    "mature",
-    "meager",
-    "mealy",
-    "mean",
-    "measly",
-    "meaty",
-    "medical",
-    "mediocre",
-    "medium",
-    "meek",
-    "mellow",
-    "melodic",
-    "memorable",
-    "menacing",
-    "merry",
-    "messy",
-    "metallic",
-    "mild",
-    "milky",
-    "mindless",
-    "miniature",
-    "minor",
-    "minty",
-    "miserable",
-    "miserly",
-    "misguided",
-    "misty",
-    "mixed",
-    "modern",
-    "modest",
-    "moist",
-    "monstrous",
-    "monthly",
-    "monumental",
-    "moral",
-    "mortified",
-    "motherly",
-    "motionless",
-    "mountainous",
-    "muddy",
-    "muffled",
-    "multicolored",
-    "mundane",
-    "murky",
-    "mushy",
-    "musty",
-    "muted",
-    "mysterious",
-    "naive",
-    "narrow",
-    "nasty",
-    "natural",
-    "naughty",
-    "nautical",
-    "near",
-    "neat",
-    "necessary",
-    "needy",
-    "negative",
-    "neglected",
-    "negligible",
-    "neighboring",
-    "nervous",
-    "new",
-    "next",
-    "nice",
-    "nifty",
-    "nimble",
-    "nippy",
-    "nocturnal",
-    "noisy",
-    "nonstop",
-    "normal",
-    "notable",
-    "noted",
-    "noteworthy",
-    "novel",
-    "noxious",
-    "numb",
-    "nutritious",
-    "nutty",
-    "obedient",
-    "obese",
-    "oblong",
-    "oily",
-    "oblong",
-    "obvious",
-    "occasional",
-    "odd",
-    "oddball",
-    "offbeat",
-    "offensive",
-    "official",
-    "old",
-    "old-fashioned",
-    "only",
-    "open",
-    "optimal",
-    "optimistic",
-    "opulent",
-    "orange",
-    "orderly",
-    "organic",
-    "ornate",
-    "ornery",
-    "ordinary",
-    "original",
-    "other",
-    "our",
-    "outlying",
-    "outgoing",
-    "outlandish",
-    "outrageous",
-    "outstanding",
-    "oval",
-    "overcooked",
-    "overdue",
-    "overjoyed",
-    "overlooked",
-    "palatable",
-    "pale",
-    "paltry",
-    "parallel",
-    "parched",
-    "partial",
-    "passionate",
-    "past",
-    "pastel",
-    "peaceful",
-    "peppery",
-    "perfect",
-    "perfumed",
-    "periodic",
-    "perky",
-    "personal",
-    "pertinent",
-    "pesky",
-    "pessimistic",
-    "petty",
-    "phony",
-    "physical",
-    "piercing",
-    "pink",
-    "pitiful",
-    "plain",
-    "plaintive",
-    "plastic",
-    "playful",
-    "pleasant",
-    "pleased",
-    "pleasing",
-    "plump",
-    "plush",
-    "polished",
-    "polite",
-    "political",
-    "pointed",
-    "pointless",
-    "poised",
-    "poor",
-    "popular",
-    "portly",
-    "posh",
-    "positive",
-    "possible",
-    "potable",
-    "powerful",
-    "powerless",
-    "practical",
-    "precious",
-    "present",
-    "prestigious",
-    "pretty",
-    "precious",
-    "previous",
-    "pricey",
-    "prickly",
-    "primary",
-    "prime",
-    "pristine",
-    "private",
-    "prize",
-    "probable",
-    "productive",
-    "profitable",
-    "profuse",
-    "proper",
-    "proud",
-    "prudent",
-    "punctual",
-    "pungent",
-    "puny",
-    "pure",
-    "purple",
-    "pushy",
-    "putrid",
-    "puzzled",
-    "puzzling",
-    "quaint",
-    "qualified",
-    "quarrelsome",
-    "quarterly",
-    "queasy",
-    "querulous",
-    "questionable",
-    "quick",
-    "quick-witted",
-    "quiet",
-    "quintessential",
-    "quirky",
-    "quixotic",
-    "quizzical",
-    "radiant",
-    "ragged",
-    "rapid",
-    "rare",
-    "rash",
-    "raw",
-    "recent",
-    "reckless",
-    "rectangular",
-    "ready",
-    "real",
-    "realistic",
-    "reasonable",
-    "red",
-    "reflecting",
-    "regal",
-    "regular",
-    "reliable",
-    "relieved",
-    "remarkable",
-    "remorseful",
-    "remote",
-    "repentant",
-    "required",
-    "respectful",
-    "responsible",
-    "repulsive",
-    "revolving",
-    "rewarding",
-    "rich",
-    "rigid",
-    "right",
-    "ringed",
-    "ripe",
-    "roasted",
-    "robust",
-    "rosy",
-    "rotating",
-    "rotten",
-    "rough",
-    "round",
-    "rowdy",
-    "royal",
-    "rubbery",
-    "rundown",
-    "ruddy",
-    "rude",
-    "runny",
-    "rural",
-    "rusty",
-    "sad",
-    "safe",
-    "salty",
-    "same",
-    "sandy",
-    "sane",
-    "sarcastic",
-    "sardonic",
-    "satisfied",
-    "scaly",
-    "scarce",
-    "scared",
-    "scary",
-    "scented",
-    "scholarly",
-    "scientific",
-    "scornful",
-    "scratchy",
-    "scrawny",
-    "second",
-    "secondary",
-    "second-hand",
-    "secret",
-    "self-assured",
-    "self-reliant",
-    "selfish",
-    "sentimental",
-    "separate",
-    "serene",
-    "serious",
-    "serpentine",
-    "several",
-    "severe",
-    "shabby",
-    "shadowy",
-    "shady",
-    "shallow",
-    "shameful",
-    "shameless",
-    "sharp",
-    "shimmering",
-    "shiny",
-    "shocked",
-    "shocking",
-    "shoddy",
-    "short",
-    "short-term",
-    "showy",
-    "shrill",
-    "shy",
-    "sick",
-    "silent",
-    "silky",
-    "silly",
-    "silver",
-    "similar",
-    "simple",
-    "simplistic",
-    "sinful",
-    "single",
-    "sizzling",
-    "skeletal",
-    "skinny",
-    "sleepy",
-    "slight",
-    "slim",
-    "slimy",
-    "slippery",
-    "slow",
-    "slushy",
-    "small",
-    "smart",
-    "smoggy",
-    "smooth",
-    "smug",
-    "snappy",
-    "snarling",
-    "sneaky",
-    "sniveling",
-    "snoopy",
-    "sociable",
-    "soft",
-    "soggy",
-    "solid",
-    "somber",
-    "some",
-    "spherical",
-    "sophisticated",
-    "sore",
-    "sorrowful",
-    "soulful",
-    "soupy",
-    "sour",
-    "Spanish",
-    "sparkling",
-    "sparse",
-    "specific",
-    "spectacular",
-    "speedy",
-    "spicy",
-    "spiffy",
-    "spirited",
-    "spiteful",
-    "splendid",
-    "spotless",
-    "spotted",
-    "spry",
-    "square",
-    "squeaky",
-    "squiggly",
-    "stable",
-    "staid",
-    "stained",
-    "stale",
-    "standard",
-    "starchy",
-    "stark",
-    "starry",
-    "steep",
-    "sticky",
-    "stiff",
-    "stimulating",
-    "stingy",
-    "stormy",
-    "straight",
-    "strange",
-    "steel",
-    "strict",
-    "strident",
-    "striking",
-    "striped",
-    "strong",
-    "studious",
-    "stunning",
-    "stupendous",
-    "stupid",
-    "sturdy",
-    "stylish",
-    "subdued",
-    "submissive",
-    "substantial",
-    "subtle",
-    "suburban",
-    "sudden",
-    "sugary",
-    "sunny",
-    "super",
-    "superb",
-    "superficial",
-    "superior",
-    "supportive",
-    "sure-footed",
-    "surprised",
-    "suspicious",
-    "svelte",
-    "sweaty",
-    "sweet",
-    "sweltering",
-    "swift",
-    "sympathetic",
-    "tall",
-    "talkative",
-    "tame",
-    "tan",
-    "tangible",
-    "tart",
-    "tasty",
-    "tattered",
-    "taut",
-    "tedious",
-    "teeming",
-    "tempting",
-    "tender",
-    "tense",
-    "tepid",
-    "terrible",
-    "terrific",
-    "testy",
-    "thankful",
-    "that",
-    "these",
-    "thick",
-    "thin",
-    "third",
-    "thirsty",
-    "this",
-    "thorough",
-    "thorny",
-    "those",
-    "thoughtful",
-    "threadbare",
-    "thrifty",
-    "thunderous",
-    "tidy",
-    "tight",
-    "timely",
-    "tinted",
-    "tiny",
-    "tired",
-    "torn",
-    "total",
-    "tough",
-    "traumatic",
-    "treasured",
-    "tremendous",
-    "tragic",
-    "trained",
-    "tremendous",
-    "triangular",
-    "tricky",
-    "trifling",
-    "trim",
-    "trivial",
-    "troubled",
-    "true",
-    "trusting",
-    "trustworthy",
-    "trusty",
-    "truthful",
-    "tubby",
-    "turbulent",
-    "twin",
-    "ugly",
-    "ultimate",
-    "unacceptable",
-    "unaware",
-    "uncomfortable",
-    "uncommon",
-    "unconscious",
-    "understated",
-    "unequaled",
-    "uneven",
-    "unfinished",
-    "unfit",
-    "unfolded",
-    "unfortunate",
-    "unhappy",
-    "unhealthy",
-    "uniform",
-    "unimportant",
-    "unique",
-    "united",
-    "unkempt",
-    "unknown",
-    "unlawful",
-    "unlined",
-    "unlucky",
-    "unnatural",
-    "unpleasant",
-    "unrealistic",
-    "unripe",
-    "unruly",
-    "unselfish",
-    "unsightly",
-    "unsteady",
-    "unsung",
-    "untidy",
-    "untimely",
-    "untried",
-    "untrue",
-    "unused",
-    "unusual",
-    "unwelcome",
-    "unwieldy",
-    "unwilling",
-    "unwitting",
-    "unwritten",
-    "upbeat",
-    "upright",
-    "upset",
-    "urban",
-    "usable",
-    "used",
-    "useful",
-    "useless",
-    "utilized",
-    "utter",
-    "vacant",
-    "vague",
-    "vain",
-    "valid",
-    "valuable",
-    "vapid",
-    "variable",
-    "vast",
-    "velvety",
-    "venerated",
-    "vengeful",
-    "verifiable",
-    "vibrant",
-    "vicious",
-    "victorious",
-    "vigilant",
-    "vigorous",
-    "villainous",
-    "violet",
-    "violent",
-    "virtual",
-    "virtuous",
-    "visible",
-    "vital",
-    "vivacious",
-    "vivid",
-    "voluminous",
-    "wan",
-    "warlike",
-    "warm",
-    "warmhearted",
-    "warped",
-    "wary",
-    "wasteful",
-    "watchful",
-    "waterlogged",
-    "watery",
-    "wavy",
-    "wealthy",
-    "weak",
-    "weary",
-    "webbed",
-    "wee",
-    "weekly",
-    "weepy",
-    "weighty",
-    "weird",
-    "welcome",
-    "well-documented",
-    "well-groomed",
-    "well-informed",
-    "well-lit",
-    "well-made",
-    "well-off",
-    "well-to-do",
-    "well-worn",
-    "wet",
-    "which",
-    "whimsical",
-    "whirlwind",
-    "whispered",
-    "white",
-    "whole",
-    "whopping",
-    "wicked",
-    "wide",
-    "wide-eyed",
-    "wiggly",
-    "wild",
-    "willing",
-    "wilted",
-    "winding",
-    "windy",
-    "winged",
-    "wiry",
-    "wise",
-    "witty",
-    "wobbly",
-    "woeful",
-    "wonderful",
-    "wooden",
-    "woozy",
-    "wordy",
-    "worldly",
-    "worn",
-    "worried",
-    "worrisome",
-    "worse",
-    "worst",
-    "worthless",
-    "worthwhile",
-    "worthy",
-    "wrathful",
-    "wretched",
-    "writhing",
-    "wrong",
-    "wry",
-    "yawning",
-    "yearly",
-    "yellow",
-    "yellowish",
-    "young",
-    "youthful",
-    "yummy",
-    "zany",
-    "zealous",
-    "zesty",
-    "zigzag",
+  "abandoned",
+  "able",
+  "absolute",
+  "adorable",
+  "adventurous",
+  "academic",
+  "acceptable",
+  "acclaimed",
+  "accomplished",
+  "accurate",
+  "aching",
+  "acidic",
+  "acrobatic",
+  "active",
+  "actual",
+  "adept",
+  "admirable",
+  "admired",
+  "adolescent",
+  "adorable",
+  "adored",
+  "advanced",
+  "afraid",
+  "affectionate",
+  "aged",
+  "aggravating",
+  "aggressive",
+  "agile",
+  "agitated",
+  "agonizing",
+  "agreeable",
+  "ajar",
+  "alarmed",
+  "alarming",
+  "alert",
+  "alienated",
+  "alive",
+  "all",
+  "altruistic",
+  "amazing",
+  "ambitious",
+  "ample",
+  "amused",
+  "amusing",
+  "anchored",
+  "ancient",
+  "angelic",
+  "angry",
+  "anguished",
+  "animated",
+  "annual",
+  "another",
+  "antique",
+  "anxious",
+  "any",
+  "apprehensive",
+  "appropriate",
+  "apt",
+  "arctic",
+  "arid",
+  "aromatic",
+  "artistic",
+  "ashamed",
+  "assured",
+  "astonishing",
+  "athletic",
+  "attached",
+  "attentive",
+  "attractive",
+  "austere",
+  "authentic",
+  "authorized",
+  "automatic",
+  "avaricious",
+  "average",
+  "aware",
+  "awesome",
+  "awful",
+  "awkward",
+  "babyish",
+  "bad",
+  "back",
+  "baggy",
+  "bare",
+  "barren",
+  "basic",
+  "beautiful",
+  "belated",
+  "beloved",
+  "beneficial",
+  "better",
+  "best",
+  "bewitched",
+  "big",
+  "big-hearted",
+  "biodegradable",
+  "bite-sized",
+  "bitter",
+  "black",
+  "black-and-white",
+  "bland",
+  "blank",
+  "blaring",
+  "bleak",
+  "blind",
+  "blissful",
+  "blond",
+  "blue",
+  "blushing",
+  "bogus",
+  "boiling",
+  "bold",
+  "bony",
+  "boring",
+  "bossy",
+  "both",
+  "bouncy",
+  "bountiful",
+  "bowed",
+  "brave",
+  "breakable",
+  "brief",
+  "bright",
+  "brilliant",
+  "brisk",
+  "broken",
+  "bronze",
+  "brown",
+  "bruised",
+  "bubbly",
+  "bulky",
+  "bumpy",
+  "buoyant",
+  "burdensome",
+  "burly",
+  "bustling",
+  "busy",
+  "buttery",
+  "buzzing",
+  "calculating",
+  "calm",
+  "candid",
+  "canine",
+  "capital",
+  "carefree",
+  "careful",
+  "careless",
+  "caring",
+  "cautious",
+  "cavernous",
+  "celebrated",
+  "charming",
+  "cheap",
+  "cheerful",
+  "cheery",
+  "chief",
+  "chilly",
+  "chubby",
+  "circular",
+  "classic",
+  "clean",
+  "clear",
+  "clear-cut",
+  "clever",
+  "close",
+  "closed",
+  "cloudy",
+  "clueless",
+  "clumsy",
+  "cluttered",
+  "coarse",
+  "cold",
+  "colorful",
+  "colorless",
+  "colossal",
+  "comfortable",
+  "common",
+  "compassionate",
+  "competent",
+  "complete",
+  "complex",
+  "complicated",
+  "composed",
+  "concerned",
+  "concrete",
+  "confused",
+  "conscious",
+  "considerate",
+  "constant",
+  "content",
+  "conventional",
+  "cooked",
+  "cool",
+  "cooperative",
+  "coordinated",
+  "corny",
+  "corrupt",
+  "costly",
+  "courageous",
+  "courteous",
+  "crafty",
+  "crazy",
+  "creamy",
+  "creative",
+  "creepy",
+  "criminal",
+  "crisp",
+  "critical",
+  "crooked",
+  "crowded",
+  "cruel",
+  "crushing",
+  "cuddly",
+  "cultivated",
+  "cultured",
+  "cumbersome",
+  "curly",
+  "curvy",
+  "cute",
+  "cylindrical",
+  "damaged",
+  "damp",
+  "dangerous",
+  "dapper",
+  "daring",
+  "darling",
+  "dark",
+  "dazzling",
+  "dead",
+  "deadly",
+  "deafening",
+  "dear",
+  "dearest",
+  "decent",
+  "decimal",
+  "decisive",
+  "deep",
+  "defenseless",
+  "defensive",
+  "defiant",
+  "deficient",
+  "definite",
+  "definitive",
+  "delayed",
+  "delectable",
+  "delicious",
+  "delightful",
+  "delirious",
+  "demanding",
+  "dense",
+  "dental",
+  "dependable",
+  "dependent",
+  "descriptive",
+  "deserted",
+  "detailed",
+  "determined",
+  "devoted",
+  "different",
+  "difficult",
+  "digital",
+  "diligent",
+  "dim",
+  "dimpled",
+  "dimwitted",
+  "direct",
+  "disastrous",
+  "discrete",
+  "disfigured",
+  "disgusting",
+  "disloyal",
+  "dismal",
+  "distant",
+  "downright",
+  "dreary",
+  "dirty",
+  "disguised",
+  "dishonest",
+  "dismal",
+  "distant",
+  "distinct",
+  "distorted",
+  "dizzy",
+  "dopey",
+  "doting",
+  "double",
+  "downright",
+  "drab",
+  "drafty",
+  "dramatic",
+  "dreary",
+  "droopy",
+  "dry",
+  "dual",
+  "dull",
+  "dutiful",
+  "each",
+  "eager",
+  "earnest",
+  "early",
+  "easy",
+  "easy-going",
+  "ecstatic",
+  "edible",
+  "educated",
+  "elaborate",
+  "elastic",
+  "elated",
+  "elderly",
+  "electric",
+  "elegant",
+  "elementary",
+  "elliptical",
+  "embarrassed",
+  "embellished",
+  "eminent",
+  "emotional",
+  "empty",
+  "enchanted",
+  "enchanting",
+  "energetic",
+  "enlightened",
+  "enormous",
+  "enraged",
+  "entire",
+  "envious",
+  "equal",
+  "equatorial",
+  "essential",
+  "esteemed",
+  "ethical",
+  "euphoric",
+  "even",
+  "evergreen",
+  "everlasting",
+  "every",
+  "evil",
+  "exalted",
+  "excellent",
+  "exemplary",
+  "exhausted",
+  "excitable",
+  "excited",
+  "exciting",
+  "exotic",
+  "expensive",
+  "experienced",
+  "expert",
+  "extraneous",
+  "extroverted",
+  "extra-large",
+  "extra-small",
+  "fabulous",
+  "failing",
+  "faint",
+  "fair",
+  "faithful",
+  "fake",
+  "false",
+  "familiar",
+  "famous",
+  "fancy",
+  "fantastic",
+  "far",
+  "faraway",
+  "far-flung",
+  "far-off",
+  "fast",
+  "fat",
+  "fatal",
+  "fatherly",
+  "favorable",
+  "favorite",
+  "fearful",
+  "fearless",
+  "feisty",
+  "feline",
+  "female",
+  "feminine",
+  "few",
+  "fickle",
+  "filthy",
+  "fine",
+  "finished",
+  "firm",
+  "first",
+  "firsthand",
+  "fitting",
+  "fixed",
+  "flaky",
+  "flamboyant",
+  "flashy",
+  "flat",
+  "flawed",
+  "flawless",
+  "flickering",
+  "flimsy",
+  "flippant",
+  "flowery",
+  "fluffy",
+  "fluid",
+  "flustered",
+  "focused",
+  "fond",
+  "foolhardy",
+  "foolish",
+  "forceful",
+  "forked",
+  "formal",
+  "forsaken",
+  "forthright",
+  "fortunate",
+  "fragrant",
+  "frail",
+  "frank",
+  "frayed",
+  "free",
+  "French",
+  "fresh",
+  "frequent",
+  "friendly",
+  "frightened",
+  "frightening",
+  "frigid",
+  "frilly",
+  "frizzy",
+  "frivolous",
+  "front",
+  "frosty",
+  "frozen",
+  "frugal",
+  "fruitful",
+  "full",
+  "fumbling",
+  "functional",
+  "funny",
+  "fussy",
+  "fuzzy",
+  "gargantuan",
+  "gaseous",
+  "general",
+  "generous",
+  "gentle",
+  "genuine",
+  "giant",
+  "giddy",
+  "gigantic",
+  "gifted",
+  "giving",
+  "glamorous",
+  "glaring",
+  "glass",
+  "gleaming",
+  "gleeful",
+  "glistening",
+  "glittering",
+  "gloomy",
+  "glorious",
+  "glossy",
+  "glum",
+  "golden",
+  "good",
+  "good-natured",
+  "gorgeous",
+  "graceful",
+  "gracious",
+  "grand",
+  "grandiose",
+  "granular",
+  "grateful",
+  "grave",
+  "gray",
+  "great",
+  "greedy",
+  "green",
+  "gregarious",
+  "grim",
+  "grimy",
+  "gripping",
+  "grizzled",
+  "gross",
+  "grotesque",
+  "grouchy",
+  "grounded",
+  "growing",
+  "growling",
+  "grown",
+  "grubby",
+  "gruesome",
+  "grumpy",
+  "guilty",
+  "gullible",
+  "gummy",
+  "hairy",
+  "half",
+  "handmade",
+  "handsome",
+  "handy",
+  "happy",
+  "happy-go-lucky",
+  "hard",
+  "hard-to-find",
+  "harmful",
+  "harmless",
+  "harmonious",
+  "harsh",
+  "hasty",
+  "hateful",
+  "haunting",
+  "healthy",
+  "heartfelt",
+  "hearty",
+  "heavenly",
+  "heavy",
+  "hefty",
+  "helpful",
+  "helpless",
+  "hidden",
+  "hideous",
+  "high",
+  "high-level",
+  "hilarious",
+  "hoarse",
+  "hollow",
+  "homely",
+  "honest",
+  "honorable",
+  "honored",
+  "hopeful",
+  "horrible",
+  "hospitable",
+  "hot",
+  "huge",
+  "humble",
+  "humiliating",
+  "humming",
+  "humongous",
+  "hungry",
+  "hurtful",
+  "husky",
+  "icky",
+  "icy",
+  "ideal",
+  "idealistic",
+  "identical",
+  "idle",
+  "idiotic",
+  "idolized",
+  "ignorant",
+  "ill",
+  "illegal",
+  "ill-fated",
+  "ill-informed",
+  "illiterate",
+  "illustrious",
+  "imaginary",
+  "imaginative",
+  "immaculate",
+  "immaterial",
+  "immediate",
+  "immense",
+  "impassioned",
+  "impeccable",
+  "impartial",
+  "imperfect",
+  "imperturbable",
+  "impish",
+  "impolite",
+  "important",
+  "impossible",
+  "impractical",
+  "impressionable",
+  "impressive",
+  "improbable",
+  "impure",
+  "inborn",
+  "incomparable",
+  "incompatible",
+  "incomplete",
+  "inconsequential",
+  "incredible",
+  "indelible",
+  "inexperienced",
+  "indolent",
+  "infamous",
+  "infantile",
+  "infatuated",
+  "inferior",
+  "infinite",
+  "informal",
+  "innocent",
+  "insecure",
+  "insidious",
+  "insignificant",
+  "insistent",
+  "instructive",
+  "insubstantial",
+  "intelligent",
+  "intent",
+  "intentional",
+  "interesting",
+  "internal",
+  "international",
+  "intrepid",
+  "ironclad",
+  "irresponsible",
+  "irritating",
+  "itchy",
+  "jaded",
+  "jagged",
+  "jam-packed",
+  "jaunty",
+  "jealous",
+  "jittery",
+  "joint",
+  "jolly",
+  "jovial",
+  "joyful",
+  "joyous",
+  "jubilant",
+  "judicious",
+  "juicy",
+  "jumbo",
+  "junior",
+  "jumpy",
+  "juvenile",
+  "kaleidoscopic",
+  "keen",
+  "key",
+  "kind",
+  "kindhearted",
+  "kindly",
+  "klutzy",
+  "knobby",
+  "knotty",
+  "knowledgeable",
+  "knowing",
+  "known",
+  "kooky",
+  "kosher",
+  "lame",
+  "lanky",
+  "large",
+  "last",
+  "lasting",
+  "late",
+  "lavish",
+  "lawful",
+  "lazy",
+  "leading",
+  "lean",
+  "leafy",
+  "left",
+  "legal",
+  "legitimate",
+  "light",
+  "lighthearted",
+  "likable",
+  "likely",
+  "limited",
+  "limp",
+  "limping",
+  "linear",
+  "lined",
+  "liquid",
+  "little",
+  "live",
+  "lively",
+  "livid",
+  "loathsome",
+  "lone",
+  "lonely",
+  "long",
+  "long-term",
+  "loose",
+  "lopsided",
+  "lost",
+  "loud",
+  "lovable",
+  "lovely",
+  "loving",
+  "low",
+  "loyal",
+  "lucky",
+  "lumbering",
+  "luminous",
+  "lumpy",
+  "lustrous",
+  "luxurious",
+  "mad",
+  "made-up",
+  "magnificent",
+  "majestic",
+  "major",
+  "male",
+  "mammoth",
+  "married",
+  "marvelous",
+  "masculine",
+  "massive",
+  "mature",
+  "meager",
+  "mealy",
+  "mean",
+  "measly",
+  "meaty",
+  "medical",
+  "mediocre",
+  "medium",
+  "meek",
+  "mellow",
+  "melodic",
+  "memorable",
+  "menacing",
+  "merry",
+  "messy",
+  "metallic",
+  "mild",
+  "milky",
+  "mindless",
+  "miniature",
+  "minor",
+  "minty",
+  "miserable",
+  "miserly",
+  "misguided",
+  "misty",
+  "mixed",
+  "modern",
+  "modest",
+  "moist",
+  "monstrous",
+  "monthly",
+  "monumental",
+  "moral",
+  "mortified",
+  "motherly",
+  "motionless",
+  "mountainous",
+  "muddy",
+  "muffled",
+  "multicolored",
+  "mundane",
+  "murky",
+  "mushy",
+  "musty",
+  "muted",
+  "mysterious",
+  "naive",
+  "narrow",
+  "nasty",
+  "natural",
+  "naughty",
+  "nautical",
+  "near",
+  "neat",
+  "necessary",
+  "needy",
+  "negative",
+  "neglected",
+  "negligible",
+  "neighboring",
+  "nervous",
+  "new",
+  "next",
+  "nice",
+  "nifty",
+  "nimble",
+  "nippy",
+  "nocturnal",
+  "noisy",
+  "nonstop",
+  "normal",
+  "notable",
+  "noted",
+  "noteworthy",
+  "novel",
+  "noxious",
+  "numb",
+  "nutritious",
+  "nutty",
+  "obedient",
+  "obese",
+  "oblong",
+  "oily",
+  "oblong",
+  "obvious",
+  "occasional",
+  "odd",
+  "oddball",
+  "offbeat",
+  "offensive",
+  "official",
+  "old",
+  "old-fashioned",
+  "only",
+  "open",
+  "optimal",
+  "optimistic",
+  "opulent",
+  "orange",
+  "orderly",
+  "organic",
+  "ornate",
+  "ornery",
+  "ordinary",
+  "original",
+  "other",
+  "our",
+  "outlying",
+  "outgoing",
+  "outlandish",
+  "outrageous",
+  "outstanding",
+  "oval",
+  "overcooked",
+  "overdue",
+  "overjoyed",
+  "overlooked",
+  "palatable",
+  "pale",
+  "paltry",
+  "parallel",
+  "parched",
+  "partial",
+  "passionate",
+  "past",
+  "pastel",
+  "peaceful",
+  "peppery",
+  "perfect",
+  "perfumed",
+  "periodic",
+  "perky",
+  "personal",
+  "pertinent",
+  "pesky",
+  "pessimistic",
+  "petty",
+  "phony",
+  "physical",
+  "piercing",
+  "pink",
+  "pitiful",
+  "plain",
+  "plaintive",
+  "plastic",
+  "playful",
+  "pleasant",
+  "pleased",
+  "pleasing",
+  "plump",
+  "plush",
+  "polished",
+  "polite",
+  "political",
+  "pointed",
+  "pointless",
+  "poised",
+  "poor",
+  "popular",
+  "portly",
+  "posh",
+  "positive",
+  "possible",
+  "potable",
+  "powerful",
+  "powerless",
+  "practical",
+  "precious",
+  "present",
+  "prestigious",
+  "pretty",
+  "precious",
+  "previous",
+  "pricey",
+  "prickly",
+  "primary",
+  "prime",
+  "pristine",
+  "private",
+  "prize",
+  "probable",
+  "productive",
+  "profitable",
+  "profuse",
+  "proper",
+  "proud",
+  "prudent",
+  "punctual",
+  "pungent",
+  "puny",
+  "pure",
+  "purple",
+  "pushy",
+  "putrid",
+  "puzzled",
+  "puzzling",
+  "quaint",
+  "qualified",
+  "quarrelsome",
+  "quarterly",
+  "queasy",
+  "querulous",
+  "questionable",
+  "quick",
+  "quick-witted",
+  "quiet",
+  "quintessential",
+  "quirky",
+  "quixotic",
+  "quizzical",
+  "radiant",
+  "ragged",
+  "rapid",
+  "rare",
+  "rash",
+  "raw",
+  "recent",
+  "reckless",
+  "rectangular",
+  "ready",
+  "real",
+  "realistic",
+  "reasonable",
+  "red",
+  "reflecting",
+  "regal",
+  "regular",
+  "reliable",
+  "relieved",
+  "remarkable",
+  "remorseful",
+  "remote",
+  "repentant",
+  "required",
+  "respectful",
+  "responsible",
+  "repulsive",
+  "revolving",
+  "rewarding",
+  "rich",
+  "rigid",
+  "right",
+  "ringed",
+  "ripe",
+  "roasted",
+  "robust",
+  "rosy",
+  "rotating",
+  "rotten",
+  "rough",
+  "round",
+  "rowdy",
+  "royal",
+  "rubbery",
+  "rundown",
+  "ruddy",
+  "rude",
+  "runny",
+  "rural",
+  "rusty",
+  "sad",
+  "safe",
+  "salty",
+  "same",
+  "sandy",
+  "sane",
+  "sarcastic",
+  "sardonic",
+  "satisfied",
+  "scaly",
+  "scarce",
+  "scared",
+  "scary",
+  "scented",
+  "scholarly",
+  "scientific",
+  "scornful",
+  "scratchy",
+  "scrawny",
+  "second",
+  "secondary",
+  "second-hand",
+  "secret",
+  "self-assured",
+  "self-reliant",
+  "selfish",
+  "sentimental",
+  "separate",
+  "serene",
+  "serious",
+  "serpentine",
+  "several",
+  "severe",
+  "shabby",
+  "shadowy",
+  "shady",
+  "shallow",
+  "shameful",
+  "shameless",
+  "sharp",
+  "shimmering",
+  "shiny",
+  "shocked",
+  "shocking",
+  "shoddy",
+  "short",
+  "short-term",
+  "showy",
+  "shrill",
+  "shy",
+  "sick",
+  "silent",
+  "silky",
+  "silly",
+  "silver",
+  "similar",
+  "simple",
+  "simplistic",
+  "sinful",
+  "single",
+  "sizzling",
+  "skeletal",
+  "skinny",
+  "sleepy",
+  "slight",
+  "slim",
+  "slimy",
+  "slippery",
+  "slow",
+  "slushy",
+  "small",
+  "smart",
+  "smoggy",
+  "smooth",
+  "smug",
+  "snappy",
+  "snarling",
+  "sneaky",
+  "sniveling",
+  "snoopy",
+  "sociable",
+  "soft",
+  "soggy",
+  "solid",
+  "somber",
+  "some",
+  "spherical",
+  "sophisticated",
+  "sore",
+  "sorrowful",
+  "soulful",
+  "soupy",
+  "sour",
+  "Spanish",
+  "sparkling",
+  "sparse",
+  "specific",
+  "spectacular",
+  "speedy",
+  "spicy",
+  "spiffy",
+  "spirited",
+  "spiteful",
+  "splendid",
+  "spotless",
+  "spotted",
+  "spry",
+  "square",
+  "squeaky",
+  "squiggly",
+  "stable",
+  "staid",
+  "stained",
+  "stale",
+  "standard",
+  "starchy",
+  "stark",
+  "starry",
+  "steep",
+  "sticky",
+  "stiff",
+  "stimulating",
+  "stingy",
+  "stormy",
+  "straight",
+  "strange",
+  "steel",
+  "strict",
+  "strident",
+  "striking",
+  "striped",
+  "strong",
+  "studious",
+  "stunning",
+  "stupendous",
+  "stupid",
+  "sturdy",
+  "stylish",
+  "subdued",
+  "submissive",
+  "substantial",
+  "subtle",
+  "suburban",
+  "sudden",
+  "sugary",
+  "sunny",
+  "super",
+  "superb",
+  "superficial",
+  "superior",
+  "supportive",
+  "sure-footed",
+  "surprised",
+  "suspicious",
+  "svelte",
+  "sweaty",
+  "sweet",
+  "sweltering",
+  "swift",
+  "sympathetic",
+  "tall",
+  "talkative",
+  "tame",
+  "tan",
+  "tangible",
+  "tart",
+  "tasty",
+  "tattered",
+  "taut",
+  "tedious",
+  "teeming",
+  "tempting",
+  "tender",
+  "tense",
+  "tepid",
+  "terrible",
+  "terrific",
+  "testy",
+  "thankful",
+  "that",
+  "these",
+  "thick",
+  "thin",
+  "third",
+  "thirsty",
+  "this",
+  "thorough",
+  "thorny",
+  "those",
+  "thoughtful",
+  "threadbare",
+  "thrifty",
+  "thunderous",
+  "tidy",
+  "tight",
+  "timely",
+  "tinted",
+  "tiny",
+  "tired",
+  "torn",
+  "total",
+  "tough",
+  "traumatic",
+  "treasured",
+  "tremendous",
+  "tragic",
+  "trained",
+  "tremendous",
+  "triangular",
+  "tricky",
+  "trifling",
+  "trim",
+  "trivial",
+  "troubled",
+  "true",
+  "trusting",
+  "trustworthy",
+  "trusty",
+  "truthful",
+  "tubby",
+  "turbulent",
+  "twin",
+  "ugly",
+  "ultimate",
+  "unacceptable",
+  "unaware",
+  "uncomfortable",
+  "uncommon",
+  "unconscious",
+  "understated",
+  "unequaled",
+  "uneven",
+  "unfinished",
+  "unfit",
+  "unfolded",
+  "unfortunate",
+  "unhappy",
+  "unhealthy",
+  "uniform",
+  "unimportant",
+  "unique",
+  "united",
+  "unkempt",
+  "unknown",
+  "unlawful",
+  "unlined",
+  "unlucky",
+  "unnatural",
+  "unpleasant",
+  "unrealistic",
+  "unripe",
+  "unruly",
+  "unselfish",
+  "unsightly",
+  "unsteady",
+  "unsung",
+  "untidy",
+  "untimely",
+  "untried",
+  "untrue",
+  "unused",
+  "unusual",
+  "unwelcome",
+  "unwieldy",
+  "unwilling",
+  "unwitting",
+  "unwritten",
+  "upbeat",
+  "upright",
+  "upset",
+  "urban",
+  "usable",
+  "used",
+  "useful",
+  "useless",
+  "utilized",
+  "utter",
+  "vacant",
+  "vague",
+  "vain",
+  "valid",
+  "valuable",
+  "vapid",
+  "variable",
+  "vast",
+  "velvety",
+  "venerated",
+  "vengeful",
+  "verifiable",
+  "vibrant",
+  "vicious",
+  "victorious",
+  "vigilant",
+  "vigorous",
+  "villainous",
+  "violet",
+  "violent",
+  "virtual",
+  "virtuous",
+  "visible",
+  "vital",
+  "vivacious",
+  "vivid",
+  "voluminous",
+  "wan",
+  "warlike",
+  "warm",
+  "warmhearted",
+  "warped",
+  "wary",
+  "wasteful",
+  "watchful",
+  "waterlogged",
+  "watery",
+  "wavy",
+  "wealthy",
+  "weak",
+  "weary",
+  "webbed",
+  "wee",
+  "weekly",
+  "weepy",
+  "weighty",
+  "weird",
+  "welcome",
+  "well-documented",
+  "well-groomed",
+  "well-informed",
+  "well-lit",
+  "well-made",
+  "well-off",
+  "well-to-do",
+  "well-worn",
+  "wet",
+  "which",
+  "whimsical",
+  "whirlwind",
+  "whispered",
+  "white",
+  "whole",
+  "whopping",
+  "wicked",
+  "wide",
+  "wide-eyed",
+  "wiggly",
+  "wild",
+  "willing",
+  "wilted",
+  "winding",
+  "windy",
+  "winged",
+  "wiry",
+  "wise",
+  "witty",
+  "wobbly",
+  "woeful",
+  "wonderful",
+  "wooden",
+  "woozy",
+  "wordy",
+  "worldly",
+  "worn",
+  "worried",
+  "worrisome",
+  "worse",
+  "worst",
+  "worthless",
+  "worthwhile",
+  "worthy",
+  "wrathful",
+  "wretched",
+  "writhing",
+  "wrong",
+  "wry",
+  "yawning",
+  "yearly",
+  "yellow",
+  "yellowish",
+  "young",
+  "youthful",
+  "yummy",
+  "zany",
+  "zealous",
+  "zesty",
+  "zigzag",
 ]
 
-export function getRandomUsername(): string {
-    const n = Math.random() * noun.length
-    const a = Math.random() * adj.length
-    return `tmp-${a}-${n}`
+export function getRandomUsername(): { first: string, second: string } {
+  const n = Math.floor(Math.random() * noun.length)
+  const a = Math.floor(Math.random() * adj.length)
+  return {
+    first: adj[a],
+    second: noun[n]
+  }
 }
 
 export function getRandomPassword(): string {
-    return encodeCrock(getRandomBytes(16))
+  if (bankUiSettings.simplePasswordForRandomAccounts) return "123"
+  return encodeCrock(getRandomBytes(16))
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/settings.ts b/packages/demobank-ui/src/settings.ts
index 6c78d287b..f33bf07e2 100644
--- a/packages/demobank-ui/src/settings.ts
+++ b/packages/demobank-ui/src/settings.ts
@@ -18,6 +18,8 @@ export interface BankUiSettings {
   backendBaseURL: string;
   allowRegistrations: boolean;
   showDemoNav: boolean;
+  simplePasswordForRandomAccounts: boolean;
+  allowRandomAccountCreation: boolean;
   bankName: string;
   demoSites: [string, string][];
 }
@@ -30,6 +32,8 @@ const defaultSettings: BankUiSettings = {
   allowRegistrations: true,
   bankName: "Taler Bank",
   showDemoNav: true,
+  simplePasswordForRandomAccounts: true,
+  allowRandomAccountCreation: true,
   demoSites: [
     ["Landing", "https://demo.taler.net/"],
     ["Bank", "https://bank.demo.taler.net/"],
-- 
cgit v1.2.3


From ae49194d4271f1108ec9b8318ea3b7977314cb85 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 08:40:18 -0300
Subject: more ui

---
 .../demobank-ui/src/components/ErrorLoading.tsx    | 19 +++++--
 packages/demobank-ui/src/components/Routing.tsx    |  4 +-
 .../src/components/Transactions/views.tsx          |  1 +
 packages/demobank-ui/src/hooks/access.ts           |  2 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     | 10 ++--
 .../demobank-ui/src/pages/AccountPage/state.ts     |  7 +++
 packages/demobank-ui/src/pages/BankFrame.tsx       |  5 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |  8 +--
 packages/demobank-ui/src/pages/LoginForm.tsx       |  4 +-
 .../demobank-ui/src/pages/OperationState/index.ts  | 11 ++--
 .../demobank-ui/src/pages/OperationState/state.ts  | 36 ++++++++++----
 .../demobank-ui/src/pages/OperationState/views.tsx | 44 ++++++----------
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  4 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     | 58 +++++++++++++++++++---
 packages/demobank-ui/src/pages/Test.tsx            |  5 --
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  1 -
 packages/taler-util/src/errors.ts                  |  2 +-
 .../taler-wallet-core/src/operations/withdraw.ts   | 17 +++----
 packages/taler-wallet-core/src/versions.ts         |  2 +-
 19 files changed, 145 insertions(+), 95 deletions(-)
 delete mode 100644 packages/demobank-ui/src/pages/Test.tsx

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx b/packages/demobank-ui/src/components/ErrorLoading.tsx
index fbc4ffceb..a4faa4d5d 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -15,15 +15,24 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
 
-export function ErrorLoading(): VNode {
+export function ErrorLoading({ error }: { error: HttpError<SandboxBackend.SandboxError> }): VNode {
   const { i18n } = useTranslationContext()
   return (
-    <div><i18n.Translate>
-      Could not complete the request
-    </i18n.Translate>
+    <div><div class="rounded-md bg-red-50 p-4">
+      <div class="flex">
+        <div class="flex-shrink-0">
+          <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
+            <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
+          </svg>
+        </div>
+        <div class="ml-3 flex-1 md:flex md:justify-between">
+          <p class="text-sm font-medium text-red-800">{error.message}</p>
+        </div>
+      </div>
+    </div>
     </div>
   );
 }
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index d51bd01eb..2710069c2 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -23,7 +23,6 @@ import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
-import { Test } from "../pages/Test.js";
 import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
@@ -69,6 +68,9 @@ export function Routing(): VNode {
                 onComplete={() => {
                   route("/account");
                 }}
+                onCancel={() => {
+                  route("/account");
+                }}
               />
             )}
           />
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index b11888320..e34120e34 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -32,6 +32,7 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
 
 export function ReadyView({ transactions }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
+  if (!transactions.length) return <div />
   const txByDate = transactions.reduce((prev, cur) => {
     const d = cur.when.t_ms === "never"
       ? ""
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 13fee71f0..2f6848af8 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -94,7 +94,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
   const { request } = useAuthenticatedBackend();
 
   const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`accounts/withdrawals/${id}/abort`, {
+    const res = await request<void>(`withdrawals/${id}/abort`, {
       method: "POST",
       contentType: "json",
     });
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts
index ba9b85710..9230fb6b1 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -18,7 +18,7 @@ import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-ta
 import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank } from "@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { useComponentState } from "./state.js";
-import { ReadyView, InvalidIbanView} from "./views.js";
+import { ReadyView, InvalidIbanView } from "./views.js";
 import { VNode } from "preact";
 import { LoginForm } from "../LoginForm.js";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
@@ -29,7 +29,7 @@ export interface Props {
     error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
   ) => VNode;
   goToBusinessAccount: () => void;
-  goToConfirmOperation: (id:string) => void;
+  goToConfirmOperation: (id: string) => void;
 }
 
 export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound;
@@ -48,14 +48,14 @@ export namespace State {
   export interface BaseInfo {
     error: undefined;
   }
-  
+
   export interface Ready extends BaseInfo {
     status: "ready";
     error: undefined;
-    account: string, 
+    account: string,
     limit: AmountJson,
     goToBusinessAccount: () => void;
-    goToConfirmOperation: (id:string) => void;
+    goToConfirmOperation: (id: string) => void;
   }
 
   export interface InvalidIban {
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index b12afbf9e..3df463f4e 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -48,6 +48,13 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
         error: result,
       };
     }
+    if (result.status === HttpStatusCode.Unauthorized) {
+      notifyError(i18n.str`Require login`, undefined);
+      return {
+        status: "error-user-not-found",
+        error: result,
+      };
+    }
     return {
       status: "loading-error",
       error: result,
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index a1414544e..214aac063 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -344,8 +344,6 @@ function StatusBanner(): VNode {
     class="fixed top-10 z-20 ml-4 mr-4"
   > {
       notifs.map(n => {
-        const info = n.message.type === "info" ? n.message : undefined
-        const error = n.message.type === "error" ? n.message : undefined
         switch (n.message.type) {
           case "error":
             return <div class="rounded-md bg-red-50 p-4">
@@ -478,8 +476,7 @@ function AccountBalance({ account }: { account: string }): VNode {
 
   // FIXME: balance
   return <div>
-    {Amounts.currencyOf(result.data.balance)}
-    {Amounts.stringifyValue(result.data.balance)}
+    {Amounts.currencyOf(result.data.balance)} {Amounts.stringifyValue(result.data.balance)}
     {/* {Amounts.currencyOf(result.data.balance.amount)}
     &nbsp;{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
     {Amounts.stringifyValue(result.data.balance.amount)} */}
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index d80a57d21..fb30c1218 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -59,7 +59,7 @@ export function HomePage({
   account: string,
   onRegister: () => void;
   goToBusinessAccount: () => void;
-  goToConfirmOperation: (id:string) => void;
+  goToConfirmOperation: (id: string) => void;
 }): VNode {
   const { i18n } = useTranslationContext();
 
@@ -84,7 +84,7 @@ export function WithdrawalOperationPage({
   //or return withdrawal uri from response
   const baseUrl = getInitialBackendBaseURL()
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`,
+    bankIntegrationApiBaseUrl: `${baseUrl}/taler-integration`,
     withdrawalOperationId: operationId,
   });
   const parsedUri = parseWithdrawUri(uri);
@@ -98,7 +98,7 @@ export function WithdrawalOperationPage({
     );
     return <Loading />;
   }
-  
+
   return (
     <WithdrawalQRCode
       withdrawUri={parsedUri}
@@ -121,7 +121,7 @@ export function handleNotOkResult(
   return function handleNotOkResult2<T>(
     result:
       | HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
-      | HttpResponse<T, SandboxBackend.SandboxError| undefined>,
+      | HttpResponse<T, SandboxBackend.SandboxError | undefined>,
   ): VNode {
     if (result.loading) return <Loading />;
     if (!result.ok) {
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index 0d4bd1261..cfef71b9a 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -49,7 +49,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
       ? i18n.str`Missing username`
       // : !USERNAME_REGEX.test(username)
       //   ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
-        : undefined,
+      : undefined,
     password: !password ? i18n.str`Missing password` : undefined,
   }) ?? busy;
 
@@ -213,7 +213,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
           {bankUiSettings.allowRegistrations && onRegister &&
             <p class="mt-10 text-center text-sm text-gray-500 border-t">
               <button type="submit"
-                class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
+                class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
                 onClick={(e) => {
                   e.preventDefault()
                   onRegister()
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts
index 32302f272..b347fd942 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -14,19 +14,16 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser";
-import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank, WithdrawUriResult } from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson, WithdrawUriResult } from "@gnu-taler/taler-util";
+import { HttpError, utils } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { useComponentState } from "./state.js";
-import { ReadyView, AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView } from "./views.js";
-import { VNode } from "preact";
-import { LoginForm } from "../LoginForm.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { AbortedView, ConfirmedView, InvalidPaytoView, InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } from "./views.js";
 
 export interface Props {
   currency: string;
   onClose: () => void;
-  goToConfirmOperation: (id: string) => void;
 }
 
 export type State = State.Loading |
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts
index ae03ed529..56e79f9ab 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -15,16 +15,15 @@
  */
 
 import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
-import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
-import { useBackendContext } from "../../context/backend.js";
-import { useAccessAPI, useAccessAnonAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js";
-import { Props, State } from "./index.js";
-import { useSettings } from "../../hooks/settings.js";
-import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
-import { useEffect, useMemo, useState } from "preact/hooks";
+import { RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { useAccessAPI, useAccessAnonAPI, useWithdrawalDetails } from "../../hooks/access.js";
 import { getInitialBackendBaseURL } from "../../hooks/backend.js";
+import { useSettings } from "../../hooks/settings.js";
+import { buildRequestErrorMessage } from "../../utils.js";
+import { Props, State } from "./index.js";
 
-export function useComponentState({ currency, onClose,goToConfirmOperation }: Props): utils.RecursiveState<State> {
+export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
   const { createWithdrawal } = useAccessAPI();
@@ -142,8 +141,8 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
       }
     }
     setBusy(undefined)
-  }  
-  const bankIntegrationApiBaseUrl = `${baseUrl}/integration-api`
+  }
+  const bankIntegrationApiBaseUrl = `${baseUrl}/taler-integration`
   const uri = stringifyWithdrawUri({
     bankIntegrationApiBaseUrl,
     withdrawalOperationId,
@@ -160,7 +159,13 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
 
   return (): utils.RecursiveState<State> => {
     const result = useWithdrawalDetails(withdrawalOperationId);
+    const shouldCreateNewOperation = !result.ok && !result.loading && result.info.status === HttpStatusCode.NotFound
 
+    useEffect(() => {
+      if (shouldCreateNewOperation) {
+        doSilentStart()
+      }
+    }, [])
     if (!result.ok) {
       if (result.loading) {
         return {
@@ -168,6 +173,12 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
           error: undefined
         }
       }
+      if (result.info.status === HttpStatusCode.NotFound) {
+        return {
+          status: "loading",
+          error: undefined,
+        }
+      }
       return {
         status: "loading-error",
         error: result
@@ -178,7 +189,10 @@ export function useComponentState({ currency, onClose,goToConfirmOperation }: Pr
       return {
         status: "aborted",
         error: undefined,
-        onClose,
+        onClose: async () => {
+          updateSettings("currentWithdrawalOperationId", undefined)
+          onClose()
+        },
       }
     }
 
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 17f1d8457..681a3b94d 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -256,6 +256,7 @@ export function AbortedView({ error, onClose }: State.Aborted) {
     <div>aborted</div>
   );
 }
+
 export function ConfirmedView({ error, onClose }: State.Confirmed) {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
@@ -325,8 +326,17 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
     document.title = `${document.title} ${uri.withdrawalOperationId}`;
   }, []);
   const talerWithdrawUri = stringifyWithdrawUri(uri);
-  const [show, setShow] = useState(false)
   return <Fragment>
+    <div class="flex justify-end mt-4">
+      <button type="button"
+        class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
+        onClick={() => {
+          onClose()
+        }}
+      >
+        Cancel
+      </button>
+    </div>
 
     <div class="bg-white shadow sm:rounded-lg mt-4">
       <div class="p-4">
@@ -360,39 +370,13 @@ export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> {
               <i18n.Translate>Scan the QR code with your mobile device.</i18n.Translate>
             </p>
           </div>
-          <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
-            <button type="button"
-              class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500"
-              onClick={() => {
-                setShow(!show)
-              }}
-            >
-              {!show ?
-                <i18n.Translate>Show QR</i18n.Translate>
-                :
-                <i18n.Translate>Hide QR</i18n.Translate>
-              }
-            </button>
-          </div>
         </div>
-        {show &&
-          <div class="mt-2 max-w-md ml-auto mr-auto">
-            <QR text={talerWithdrawUri} />
-          </div>
-        }
+        <div class="mt-2 max-w-md ml-auto mr-auto">
+          <QR text={talerWithdrawUri} />
+        </div>
       </div>
     </div>
 
-    <div class="flex justify-end mt-4">
-      <button type="button"
-        class="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
-        onClick={() => {
-          onClose()
-        }}
-      >
-        Cancel
-      </button>
-    </div>
   </Fragment>
 
 }
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 2830f5c1e..49419d0dc 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -56,11 +56,11 @@ export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJ
                   <i18n.Translate>Withdraw digital money into your mobile wallet or browser extension</i18n.Translate>
                 </span>
                 {!!settings.currentWithdrawalOperationId &&
-                  <span class="inline-flex items-center gap-x-1.5 rounded-full bg-green-100 px-1.5 py-0.5 text-xs font-medium text-green-700">
+                  <span class="inline-flex items-center gap-x-1.5 w-fit rounded-full bg-green-100 px-2 py-1 text-xs font-medium text-green-700">
                     <svg class="h-1.5 w-1.5 fill-green-500" viewBox="0 0 6 6" aria-hidden="true">
                       <circle cx="3" cy="3" r="3" />
                     </svg>
-                    <i18n.Translate>Operation in progress</i18n.Translate>
+                    <i18n.Translate>operation ready</i18n.Translate>
                   </span>
                 }
               </span>
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 8221457bf..5325f43ab 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -33,8 +33,10 @@ const logger = new Logger("RegistrationPage");
 
 export function RegistrationPage({
   onComplete,
+  onCancel
 }: {
   onComplete: () => void;
+  onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
   if (!bankUiSettings.allowRegistrations) {
@@ -42,7 +44,7 @@ export function RegistrationPage({
       <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
     );
   }
-  return <RegistrationForm onComplete={onComplete} />;
+  return <RegistrationForm onComplete={onComplete} onCancel={onCancel} />;
 }
 
 export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
@@ -50,7 +52,7 @@ export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
 /**
  * Collect and submit registration data.
  */
-function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
+function RegistrationForm({ onComplete, onCancel }: { onComplete: () => void, onCancel: () => void }): VNode {
   const backend = useBackendContext();
   const [username, setUsername] = useState<string | undefined>();
   const [name, setName] = useState<string | undefined>();
@@ -168,9 +170,38 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
             autoCapitalize="none"
             autoCorrect="off"
           >
+            <div>
+              <label for="name" class="block text-sm font-medium leading-6 text-gray-900">
+                <i18n.Translate>Name</i18n.Translate>
+                <b style={{ color: "red" }}> *</b>
+              </label>
+              <div class="mt-2">
+                <input
+                  autoFocus
+                  type="text"
+                  name="name"
+                  id="name"
+                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  value={name ?? ""}
+                  enterkeyhint="next"
+                  placeholder="your name"
+                  autocomplete="name"
+                  required
+                  onInput={(e): void => {
+                    setName(e.currentTarget.value);
+                  }}
+                />
+                <ShowInputErrorLabel
+                  message={errors?.name}
+                  isDirty={name !== undefined}
+                />
+              </div>
+            </div>
+
             <div>
               <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
                 <i18n.Translate>Username</i18n.Translate>
+                <b style={{ color: "red" }}> *</b>
               </label>
               <div class="mt-2">
                 <input
@@ -197,7 +228,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
 
             <div>
               <div class="flex items-center justify-between">
-                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
+                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">
+                  Password
+                  <b style={{ color: "red" }}> *</b>
+                </label>
               </div>
               <div class="mt-2">
                 <input
@@ -223,7 +257,10 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
 
             <div>
               <div class="flex items-center justify-between">
-                <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">Repeat assword</label>
+                <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">
+                  Repeat assword
+                  <b style={{ color: "red" }}> *</b>
+                </label>
               </div>
               <div class="mt-2">
                 <input
@@ -247,9 +284,18 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
               </div>
             </div>
 
-            <div>
+            <div class="flex w-full justify-between">
+              <button type="submit"
+                class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
+                onClick={(e) => {
+                  e.preventDefault()
+                  onCancel()
+                }}
+              >
+                <i18n.Translate>Cancel</i18n.Translate>
+              </button>
               <button type="submit"
-                class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+                class=" rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
                 disabled={!!errors}
                 onClick={(e) => {
                   e.preventDefault()
diff --git a/packages/demobank-ui/src/pages/Test.tsx b/packages/demobank-ui/src/pages/Test.tsx
deleted file mode 100644
index 874f7fe68..000000000
--- a/packages/demobank-ui/src/pages/Test.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { VNode, h } from "preact";
-
-export function Test(): VNode {
-    return <div >hola</div>
-}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index e16825527..3c5ee34fd 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -307,7 +307,6 @@ export function WalletWithdrawForm({
         <OperationState
           currency={limit.currency}
           onClose={onCancel}
-          goToConfirmOperation={goToConfirmOperation}
         />
       }
     </div>
diff --git a/packages/taler-util/src/errors.ts b/packages/taler-util/src/errors.ts
index 4399dbcf2..07a402413 100644
--- a/packages/taler-util/src/errors.ts
+++ b/packages/taler-util/src/errors.ts
@@ -78,7 +78,7 @@ export interface DetailsMap {
     stack?: string;
   };
   [TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE]: {
-    exchangeProtocolVersion: string;
+    bankProtocolVersion: string;
     walletProtocolVersion: string;
   };
   [TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN]: {
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index fb503d75f..2c9c95d4c 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -570,7 +570,7 @@ export async function getBankWithdrawalInfo(
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE,
       {
-        exchangeProtocolVersion: config.version,
+        bankProtocolVersion: config.version,
         walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
       },
       "bank integration protocol version not compatible with wallet",
@@ -813,10 +813,10 @@ async function handleKycRequired(
             amlStatus === AmlStatus.normal || amlStatus === undefined
               ? WithdrawalGroupStatus.PendingKyc
               : amlStatus === AmlStatus.pending
-              ? WithdrawalGroupStatus.PendingAml
-              : amlStatus === AmlStatus.fronzen
-              ? WithdrawalGroupStatus.SuspendedAml
-              : assertUnreachable(amlStatus);
+                ? WithdrawalGroupStatus.PendingAml
+                : amlStatus === AmlStatus.fronzen
+                  ? WithdrawalGroupStatus.SuspendedAml
+                  : assertUnreachable(amlStatus);
 
           await tx.withdrawalGroups.put(wg2);
           const newTxState = computeWithdrawalTransactionStatus(wg2);
@@ -1145,8 +1145,7 @@ export async function updateWithdrawalDenoms(
         denom.verificationStatus === DenominationVerificationStatus.Unverified
       ) {
         logger.trace(
-          `Validating denomination (${current + 1}/${
-            denominations.length
+          `Validating denomination (${current + 1}/${denominations.length
           }) signature of ${denom.denomPubHash}`,
         );
         let valid = false;
@@ -1240,7 +1239,7 @@ async function queryReserve(
     if (
       resp.status === 404 &&
       result.talerErrorResponse.code ===
-        TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
+      TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
     ) {
       return { ready: false };
     } else {
@@ -1775,7 +1774,7 @@ export async function getExchangeWithdrawalInfo(
     ) {
       logger.warn(
         `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
-          `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`,
+        `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`,
       );
     }
   }
diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts
index 8b9177bc3..022f4900d 100644
--- a/packages/taler-wallet-core/src/versions.ts
+++ b/packages/taler-wallet-core/src/versions.ts
@@ -29,7 +29,7 @@ export const WALLET_EXCHANGE_PROTOCOL_VERSION = "17:0:0";
 export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1";
 
 /**
- * Protocol version spoken with the merchant.
+ * Protocol version spoken with the bank.
  *
  * Uses libtool's current:revision:age versioning.
  */
-- 
cgit v1.2.3


From 4041a76a58503572c6fe8edc87658afc946a11e0 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 11:58:17 -0300
Subject: more ui: pagination

---
 packages/demobank-ui/src/components/Routing.tsx    |  3 +-
 .../src/components/Transactions/index.ts           |  2 +
 .../src/components/Transactions/state.ts           | 52 ++++++----------------
 .../src/components/Transactions/views.tsx          | 25 ++++++++++-
 packages/demobank-ui/src/declaration.d.ts          | 41 ++++++++---------
 packages/demobank-ui/src/hooks/access.ts           | 23 ++++++----
 packages/demobank-ui/src/hooks/backend.ts          | 18 +++++---
 .../demobank-ui/src/pages/AccountPage/state.ts     |  2 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       | 17 +++++--
 packages/demobank-ui/src/pages/HomePage.tsx        |  5 +--
 packages/demobank-ui/src/pages/LoginForm.tsx       | 41 ++++++++++++++---
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  5 ++-
 packages/demobank-ui/src/pages/admin/Account.tsx   |  2 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  5 +--
 packages/demobank-ui/src/pages/admin/Home.tsx      |  8 ++--
 packages/demobank-ui/src/pages/business/Home.tsx   |  8 ++--
 16 files changed, 151 insertions(+), 106 deletions(-)

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 2710069c2..05af1719b 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -17,7 +17,7 @@
 import { createHashHistory } from "history";
 import { VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
-import { useEffect } from "preact/hooks";
+import { useEffect, useErrorBoundary } from "preact/hooks";
 import { BankFrame } from "../pages/BankFrame.js";
 import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
@@ -27,6 +27,7 @@ import { useBackendContext } from "../context/backend.js";
 import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
 import { bankUiSettings } from "../settings.js";
+import { notifyError } from "@gnu-taler/web-util/browser";
 
 export function Routing(): VNode {
   const history = createHashHistory();
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts
index 46b38ce74..9df1a70e5 100644
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -46,6 +46,8 @@ export namespace State {
     status: "ready";
     error: undefined;
     transactions: Transaction[];
+    onPrev?: () => void;
+    onNext?: () => void;
   }
 }
 
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
index d2a512b08..30c48aa45 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, Amounts } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
 import { useTransactions } from "../../hooks/access.js";
 import { Props, State, Transaction } from "./index.js";
 
@@ -34,45 +34,19 @@ export function useComponentState({ account }: Props): State {
   }
 
   const transactions = result.data.transactions
-    .map((item: unknown) => {
-      if (
-        !item ||
-        typeof item !== "object" ||
-        !("direction" in item) ||
-        !("creditorIban" in item) ||
-        !("debtorIban" in item) ||
-        !("date" in item) ||
-        !("subject" in item) ||
-        !("currency" in item) ||
-        !("amount" in item)
-      ) {
-        //not valid
-        return;
-      }
-      const anyItem = item as any;
-      if (
-        !(typeof anyItem.creditorIban === "string") ||
-        !(typeof anyItem.debtorIban === "string") ||
-        !(typeof anyItem.date === "string") ||
-        !(typeof anyItem.subject === "string") ||
-        !(typeof anyItem.currency === "string") ||
-        !(typeof anyItem.amount === "string")
-      ) {
-        return;
-      }
+    .map((tx) => {
 
-      const negative = anyItem.direction === "DEBIT";
-      const counterpart = negative ? anyItem.creditorIban : anyItem.debtorIban;
+      const negative = tx.direction === "debit";
+      const cp = parsePaytoUri(negative ? tx.creditor_payto_uri : tx.debtor_payto_uri);
+      const counterpart = (cp === undefined || !cp.isKnown ? undefined :
+        cp.targetType === "iban" ? cp.iban : 
+        cp.targetType === "x-taler-bank" ? cp.account :
+        cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 6)}...` : undefined) ??
+        "unkown";
 
-      let date = anyItem.date ? parseInt(anyItem.date, 10) : 0;
-      if (isNaN(date) || !isFinite(date)) {
-        date = 0;
-      }
-      const when: AbsoluteTime = !date
-        ? AbsoluteTime.never()
-        : AbsoluteTime.fromMilliseconds(date);
-      const amount = Amounts.parse(`${anyItem.currency}:${anyItem.amount}`);
-      const subject = anyItem.subject;
+      const when = AbsoluteTime.fromMilliseconds(tx.date / 1000);
+      const amount = Amounts.parse(tx.amount);
+      const subject = tx.subject;
       return {
         negative,
         counterpart,
@@ -87,5 +61,7 @@ export function useComponentState({ account }: Props): State {
     status: "ready",
     error: undefined,
     transactions,
+    onNext: result.isReachingEnd ? undefined : result.loadMore,
+    onPrev: result.isReachingStart ? undefined : result.loadMorePrev,
   };
 }
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index e34120e34..f8b2e3113 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -30,7 +30,7 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   );
 }
 
-export function ReadyView({ transactions }: State.Ready): VNode {
+export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   if (!transactions.length) return <div />
   const txByDate = transactions.reduce((prev, cur) => {
@@ -50,7 +50,7 @@ export function ReadyView({ transactions }: State.Ready): VNode {
           <h1 class="text-base font-semibold leading-6 text-gray-900"><i18n.Translate>Latest transactions</i18n.Translate></h1>
         </div>
       </div>
-      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 sm:rounded-lg min-w-fit bg-white">
+      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
         <table class="min-w-full divide-y divide-gray-300">
           <thead>
             <tr>
@@ -89,9 +89,30 @@ export function ReadyView({ transactions }: State.Ready): VNode {
                   </tr>)
                 })}
               </Fragment>
+
             })}
           </tbody>
+
         </table>
+       
+       <nav class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" aria-label="Pagination">
+          <div class="flex flex-1 justify-between sm:justify-end">
+            <button
+              class="relative disabled:bg-gray-100 disabled:text-gray-500 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
+              disabled={!onPrev}
+              onClick={onPrev}
+            >
+              <i18n.Translate>First page</i18n.Translate>
+            </button>
+            <button
+              class="relative disabled:bg-gray-100 disabled:text-gray-500 ml-3 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0"
+              disabled={!onNext}
+              onClick={onNext}
+            >
+              <i18n.Translate>Next</i18n.Translate>
+            </button>
+          </div>
+        </nav>
       </div>
     </div>
   );
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index 0c083a939..8d729c1f7 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -194,28 +194,25 @@ namespace SandboxBackend {
     }
 
     interface BankAccountTransactionInfo {
-      creditorIban: string;
-      creditorBic: string; // Optional
-      creditorName: string;
+      creditor_payto_uri: string;
+      debtor_payto_uri: string;
 
-      debtorIban: string;
-      debtorBic: string;
-      debtorName: string;
+      amount: Amount;
+      direction: "debit" | "credit";
 
-      amount: number;
-      currency: string;
       subject: string;
 
       // Transaction unique ID.  Matches
       // $transaction_id from the URI.
-      uid: string;
-      direction: "DBIT" | "CRDT";
-      date: string; // milliseconds since the Unix epoch
+      row_id: number;
+      date: number;
+      // date: Timestamp;
     }
+
     interface CreateBankAccountTransactionCreate {
       // Address in the Payto format of the wire transfer receiver.
       // It needs at least the 'message' query string parameter.
-      paytoUri: string;
+      payto_uri: string;
 
       // Transaction amount (in the $currency:x.y format), optional.
       // However, when not given, its value must occupy the 'amount'
@@ -326,32 +323,32 @@ namespace SandboxBackend {
     interface AccountMinimalData {
       // Username
       username: string;
-    
+
       // Legal name of the account owner.
       name: string;
-    
+
       // current balance of the account
       balance: Balance;
-    
+
       // Number indicating the max debit allowed for the requesting user.
       debit_threshold: Amount;
     }
-    
+
     interface AccountData {
       // Legal name of the account owner.
       name: string;
-    
+
       // Available balance on the account.
       balance: Balance;
-    
+
       // payto://-URI of the account.
       payto_uri: string;
-    
+
       // Number indicating the max debit allowed for the requesting user.
       debit_threshold: Amount;
-    
+
       contact_data?: ChallengeContactData;
-    
+
       // 'payto' address pointing the bank account
       // where to send cashouts.  This field is optional
       // because not all the accounts are required to participate
@@ -360,7 +357,7 @@ namespace SandboxBackend {
       // be done via the access API.
       cashout_payto_uri?: string;
     }
-    
+
   }
 
   namespace Circuit {
diff --git a/packages/demobank-ui/src/hooks/access.ts b/packages/demobank-ui/src/hooks/access.ts
index 2f6848af8..20fd64bfa 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -255,7 +255,7 @@ export function useTransactionDetails(
 }
 
 interface PaginationFilter {
-  page: number;
+  // page: number;
 }
 
 export function usePublicAccounts(
@@ -275,7 +275,7 @@ export function usePublicAccounts(
   } = useSWR<
     HttpResponseOk<SandboxBackend.CoreBank.PublicAccountsResponse>,
     RequestError<SandboxBackend.SandboxError>
-  >([`public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
+  >([`public-accounts`, page, PAGE_SIZE], paginatedFetcher);
 
   const [lastAfter, setLastAfter] = useState<
     HttpResponse<
@@ -334,7 +334,7 @@ export function useTransactions(
 > {
   const { paginatedFetcher } = useAuthenticatedBackend();
 
-  const [page, setPage] = useState(1);
+  const [start, setStart] = useState<string>();
 
   const {
     data: afterData,
@@ -344,7 +344,7 @@ export function useTransactions(
     HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionsResponse>,
     RequestError<SandboxBackend.SandboxError>
   >(
-    [`accounts/${account}/transactions`, args?.page, PAGE_SIZE],
+    [`accounts/${account}/transactions`, start, PAGE_SIZE],
     paginatedFetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
@@ -374,19 +374,24 @@ export function useTransactions(
   // if the query returns less that we ask, then we have reach the end or beginning
   const isReachingEnd =
     afterData && afterData.data.transactions.length < PAGE_SIZE;
-  const isReachingStart = false;
+  const isReachingStart = start == undefined;
 
   const pagination = {
     isReachingEnd,
     isReachingStart,
     loadMore: () => {
       if (!afterData || isReachingEnd) return;
-      if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
-        setPage(page + 1);
-      }
+      // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
+      // console.log("load more", page)
+      const l = afterData.data.transactions[afterData.data.transactions.length-1]
+      setStart(String(l.row_id));
+      // }
     },
     loadMorePrev: () => {
-      null;
+      if (!afterData || isReachingStart) return;
+      // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
+      setStart(undefined)
+      // }
     },
   };
 
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
index e6a3a1c0c..3d5bfa360 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -159,7 +159,7 @@ interface useBackendType {
   fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
   multiFetcher: <T>(endpoint: string[][]) => Promise<HttpResponseOk<T>[]>;
   paginatedFetcher: <T>(
-    args: [string, number, number],
+    args: [string, string | undefined, number],
   ) => Promise<HttpResponseOk<T>>;
   sandboxAccountsFetcher: <T>(
     args: [string, number, number, string],
@@ -188,13 +188,15 @@ export function usePublicBackend(): useBackendType {
     [baseUrl],
   );
   const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page, size]: [
+    function fetcherImpl<T>([endpoint, start, size]: [
       string,
-      number,
+      string | undefined,
       number,
     ]): Promise<HttpResponseOk<T>> {
+      const delta = -1 * size //descending order
+      const params = start ? { delta, start } : {delta}
       return requestHandler<T>(baseUrl, endpoint, {
-        params: { page: page || 1, size },
+        params,
       });
     },
     [baseUrl],
@@ -280,14 +282,16 @@ export function useAuthenticatedBackend(): useBackendType {
     [baseUrl, creds],
   );
   const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page = 1, size]: [
+    function fetcherImpl<T>([endpoint, start, size]: [
       string,
-      number,
+      string | undefined,
       number,
     ]): Promise<HttpResponseOk<T>> {
+      const delta = -1 * size //descending order
+      const params = start ? { delta, start } : {delta}
       return requestHandler<T>(baseUrl, endpoint, {
         token: creds,
-        params: { delta: size, start: size * page },
+        params,
       });
     },
     [baseUrl, creds],
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts
index a23d3b569..c212e7484 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -49,7 +49,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
       };
     }
     if (result.status === HttpStatusCode.Unauthorized) {
-      notifyError(i18n.str`Require login`, undefined);
+      notifyError(i18n.str`Authorization denied`, i18n.str`Maybe the session has expired, login again.`);
       return {
         status: "error-user-not-found",
         error: result,
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx
index 39ca09f1b..c4f872679 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -15,9 +15,9 @@
  */
 
 import { Amounts, Logger, PaytoUriIBAN, TranslatedString, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { notifyError, useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
+import { StateUpdater, useEffect, useErrorBoundary, useState } from "preact/hooks";
 import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js";
 import { useBackendContext } from "../context/backend.js";
 import { useBusinessAccountDetails } from "../hooks/circuit.js";
@@ -50,6 +50,15 @@ export function BankFrame({
   const [settings, updateSettings] = useSettings();
   const [open, setOpen] = useState(false)
 
+  const [error, resetError] = useErrorBoundary();
+
+  useEffect(() => {
+    if (error) {
+      notifyError(i18n.str`Internal error, please report.`, (error instanceof Error ? error.message : String(error)) as TranslatedString)
+      resetError()
+    }
+  }, [error])
+
   const demo_sites = [];
   for (const i in bankUiSettings.demoSites)
     demo_sites.push(
@@ -355,7 +364,9 @@ function StatusBanner(): VNode {
                 </div>
                 <div class="ml-3 flex-1 md:flex md:justify-between">
                   <p class="text-sm font-medium text-red-800">{n.message.title}</p>
-                  <p class="mt-3 text-sm md:ml-6 md:mt-0">
+                </div>
+                <div>
+                  <p class="text-sm">
                     <button type="button" class="inline-flex font-semibold items-center rounded bg-white px-2 py-1 text-xs text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
                       onClick={(e) => {
                         e.preventDefault();
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx
index fb30c1218..d945d80d1 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -68,7 +68,7 @@ export function HomePage({
       account={account}
       goToConfirmOperation={goToConfirmOperation}
       goToBusinessAccount={goToBusinessAccount}
-      onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+      onLoadNotOk={handleNotOkResult(i18n)}
     />
   );
 }
@@ -112,7 +112,6 @@ export function WithdrawalOperationPage({
 
 export function handleNotOkResult(
   i18n: ReturnType<typeof useTranslationContext>["i18n"],
-  onRegister?: () => void,
 ): <T>(
   result:
     | HttpResponsePaginated<T, SandboxBackend.SandboxError>
@@ -133,7 +132,7 @@ export function handleNotOkResult(
         case ErrorType.CLIENT: {
           if (result.status === HttpStatusCode.Unauthorized) {
             notifyError(i18n.str`Wrong credentials`, undefined);
-            return <LoginForm onRegister={onRegister} />;
+            return <LoginForm />;
           }
           const errorData = result.payload;
           notify({
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f1ba439ac..f579678f2 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -24,6 +24,7 @@ import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
 
+
 /**
  * Collect and submit login data.
  */
@@ -35,7 +36,6 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const { i18n } = useTranslationContext();
   const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
 
-  // const testLogin = useCredentialsCheckerOld();
   const ref = useRef<HTMLInputElement>(null);
   useEffect(function focusInput() {
     ref.current?.focus();
@@ -120,6 +120,13 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
     setBusy(undefined)
   }
 
+  /**
+   * Register form may be shown in the initialization step.
+   * If this is an error when usgin the app the registration
+   * callback is not set
+   */
+  const isSessionExpired = !onRegister
+
   return (
     <Fragment>
       <h1 class="nav"></h1>
@@ -128,9 +135,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
       )} */}
 
       <div class="flex min-h-full flex-col justify-center">
-        <div class="sm:mx-auto sm:w-full sm:max-w-sm">
+        {/* <div class="sm:mx-auto sm:w-full sm:max-w-sm">
           <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2>
-        </div>
+        </div> */}
 
         <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
           <form class="space-y-6" noValidate
@@ -151,8 +158,9 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
                   type="text"
                   name="username"
                   id="username"
-                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                  class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                   value={username ?? ""}
+                  disabled={isSessionExpired}
                   enterkeyhint="next"
                   placeholder="identification"
                   autocomplete="username"
@@ -194,7 +202,28 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
               </div>
             </div>
 
-            <div>
+            {isSessionExpired ? <div class="flex justify-between">
+              <button type="submit"
+                class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
+                onClick={(e) => {
+                  e.preventDefault()
+                  doLogin()
+                }}
+              >
+                <i18n.Translate>Cancel</i18n.Translate>
+              </button>
+              
+              <button type="submit"
+                class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+                disabled={!!errors}
+                onClick={(e) => {
+                  e.preventDefault()
+                  doLogin()
+                }}
+              >
+                <i18n.Translate>Renew session</i18n.Translate>
+              </button>
+            </div> : <div>
               <button type="submit"
                 class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
                 disabled={!!errors}
@@ -205,7 +234,7 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
               >
                 <i18n.Translate>Log in</i18n.Translate>
               </button>
-            </div>
+            </div>}
           </form>
 
           {bankUiSettings.allowRegistrations && onRegister &&
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 30fcbdff7..d160a88b3 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -317,8 +317,9 @@ export function WithdrawalConfirmationQuestion({
                   </div>
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                     <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
-                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">To be added</dd>
-                    {/* Amounts.stringifyValue(details.amount) */}
+                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
+                      {Amounts.stringifyValue(details.amount)}
+                    </dd>
                   </div>
                 </dl>
               </div>
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx
index 8ea59cdcb..521bc8eb3 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -13,7 +13,7 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode
   const result = useAccountDetails(account);
 
   if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
+    return handleNotOkResult(i18n)(result);
   }
   const { data } = result;
 
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index eb5765533..f99b320a4 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -8,17 +8,16 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
 interface Props {
   onAction: (type: AccountAction, account: string) => void;
   account: string | undefined;
-  onRegister: () => void;
   onCreateAccount: () => void;
 }
 
-export function AccountList({ account, onAction, onCreateAccount, onRegister }: Props): VNode {
+export function AccountList({ account, onAction, onCreateAccount }: Props): VNode {
   const result = useBusinessAccounts({ account });
   const { i18n } = useTranslationContext();
 
   if (result.loading) return <div />;
   if (!result.ok) {
-    return handleNotOkResult(i18n, onRegister)(result);
+    return handleNotOkResult(i18n)(result);
   }
 
   const { customers } = result.data;
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx b/packages/demobank-ui/src/pages/admin/Home.tsx
index 5033b7fdc..725820ada 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -37,7 +37,7 @@ export function AdminHome({ onRegister }: Props): VNode {
     switch (action.type) {
       case "show-cashouts-details": return <ShowCashoutDetails
         id={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setAction(undefined);
         }}
@@ -73,7 +73,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       )
       case "update-password": return <UpdateAccountPassword
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setAction(undefined);
@@ -84,7 +84,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "remove-account": return <RemoveAccount
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account removed`);
           setAction(undefined);
@@ -95,7 +95,7 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "show-details": return <ShowAccountDetails
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onChangePassword={() => {
           setAction({
             type: "update-password",
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx b/packages/demobank-ui/src/pages/business/Home.tsx
index 628ae328d..c9d798082 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -75,7 +75,7 @@ export function BusinessAccount({
     return (
       <CreateCashout
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setNewcashout(false);
         }}
@@ -93,7 +93,7 @@ export function BusinessAccount({
     return (
       <ShowCashoutDetails
         id={showCashoutDetails}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setShowCashoutDetails(undefined);
         }}
@@ -104,7 +104,7 @@ export function BusinessAccount({
     return (
       <UpdateAccountPassword
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setUpdatePassword(false);
@@ -119,7 +119,7 @@ export function BusinessAccount({
     <div>
       <ShowAccountDetails
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n, onRegister)}
+        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account updated`);
         }}
-- 
cgit v1.2.3


From 820f953b96b2b2852c32dc16a2fa920c6c717788 Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Sep 2023 12:18:59 -0300
Subject: check config number

---
 packages/demobank-ui/src/components/Routing.tsx |  31 ++--
 packages/demobank-ui/src/components/app.tsx     |  47 +++--
 packages/demobank-ui/src/hooks/config.ts        |  51 ++++++
 packages/demobank-ui/src/pages/LoginForm.tsx    | 232 +++++++++++-------------
 packages/demobank-ui/src/utils.ts               |  22 ---
 5 files changed, 214 insertions(+), 169 deletions(-)
 create mode 100644 packages/demobank-ui/src/hooks/config.ts

(limited to 'packages/demobank-ui/src/components/Routing.tsx')

diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx
index 05af1719b..aafc95687 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -14,36 +14,43 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { createHashHistory } from "history";
-import { VNode, h } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
-import { useEffect, useErrorBoundary } from "preact/hooks";
+import { useEffect } from "preact/hooks";
+import { useBackendContext } from "../context/backend.js";
 import { BankFrame } from "../pages/BankFrame.js";
-import { BusinessAccount } from "../pages/business/Home.js";
 import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js";
+import { LoginForm } from "../pages/LoginForm.js";
 import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "../pages/RegistrationPage.js";
-import { useBackendContext } from "../context/backend.js";
-import { LoginForm } from "../pages/LoginForm.js";
 import { AdminHome } from "../pages/admin/Home.js";
+import { BusinessAccount } from "../pages/business/Home.js";
 import { bankUiSettings } from "../settings.js";
-import { notifyError } from "@gnu-taler/web-util/browser";
 
 export function Routing(): VNode {
   const history = createHashHistory();
   const backend = useBackendContext();
-
+  const {i18n} = useTranslationContext();
+  
   if (backend.state.status === "loggedOut") {
     return <BankFrame >
       <Router history={history}>
         <Route
           path="/login"
           component={() => (
-            <LoginForm
-              onRegister={() => {
-                route("/register");
-              }}
-            />
+            <Fragment>
+              <div class="sm:mx-auto sm:w-full sm:max-w-sm">
+                <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2>
+              </div>
+
+              <LoginForm
+                onRegister={() => {
+                  route("/register");
+                }}
+              />
+            </Fragment>
           )}
         />
         <Route
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index 22752ab78..ebda31035 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -15,15 +15,20 @@
  */
 
 import {
+  LibtoolVersion,
   getGlobalLogLevel,
   setGlobalLogLevelFromString,
 } from "@gnu-taler/taler-util";
-import { TranslationProvider } from "@gnu-taler/web-util/browser";
-import { FunctionalComponent, h } from "preact";
+import { TranslationProvider, useApiContext } from "@gnu-taler/web-util/browser";
+import { ComponentChildren, Fragment, FunctionalComponent, VNode, h } from "preact";
 import { SWRConfig } from "swr";
-import { BackendStateProvider } from "../context/backend.js";
+import { BackendStateProvider, useBackendContext } from "../context/backend.js";
 import { strings } from "../i18n/strings.js";
 import { Routing } from "./Routing.js";
+import { useEffect, useState } from "preact/hooks";
+import { Loading } from "./Loading.js";
+import { getInitialBackendBaseURL } from "../hooks/backend.js";
+import { BANK_INTEGRATION_PROTOCOL_VERSION, useConfigState } from "../hooks/config.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 /**
@@ -47,22 +52,38 @@ const App: FunctionalComponent = () => {
   return (
     <TranslationProvider source={strings}>
       <BackendStateProvider>
-        <SWRConfig
-          value={{
-            provider: WITH_LOCAL_STORAGE_CACHE
-              ? localStorageProvider
-              : undefined,
-          }}
-        >
-          <Routing />
-        </SWRConfig>
+        <VersionCheck>
+          <SWRConfig
+            value={{
+              provider: WITH_LOCAL_STORAGE_CACHE
+                ? localStorageProvider
+                : undefined,
+            }}
+          >
+            <Routing />
+          </SWRConfig>
+        </VersionCheck>
       </BackendStateProvider>
-    </TranslationProvider>
+    </TranslationProvider >
   );
 };
 (window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString;
 (window as any).getGlobalLevel = getGlobalLogLevel;
 
+function VersionCheck({ children }: { children: ComponentChildren }): VNode {
+  const checked = useConfigState()
+
+  if (checked === undefined) {
+    return <Loading />
+  }
+  if (checked === false) {
+    return <div>
+      the bank backend is not supported. supported version "{BANK_INTEGRATION_PROTOCOL_VERSION}"
+    </div>
+  }
+  return <Fragment>{children}</Fragment>
+}
+
 function localStorageProvider(): Map<unknown, unknown> {
   const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]"));
 
diff --git a/packages/demobank-ui/src/hooks/config.ts b/packages/demobank-ui/src/hooks/config.ts
new file mode 100644
index 000000000..4b22e8ad3
--- /dev/null
+++ b/packages/demobank-ui/src/hooks/config.ts
@@ -0,0 +1,51 @@
+import { LibtoolVersion } from "@gnu-taler/taler-util";
+import { useApiContext } from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { getInitialBackendBaseURL } from "./backend.js";
+
+/**
+ * Protocol version spoken with the bank.
+ *
+ * Uses libtool's current:revision:age versioning.
+ */
+export const BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
+
+async function getConfigState(
+  request: ReturnType<typeof useApiContext>["request"],
+): Promise<SandboxBackend.Config | undefined> {
+  try {
+    const url = getInitialBackendBaseURL();
+    const result = await request<SandboxBackend.Config>(
+      url,
+      `config`,
+    );
+    return result.data;
+  } catch (error) {
+    return undefined;
+  }
+}
+
+export function useConfigState(): boolean | undefined {
+  const [checked, setChecked] = useState<boolean>()
+  const { request } = useApiContext();
+
+  useEffect(() => {
+    
+    getConfigState(request)
+      .then((result) => {
+        if (!result) {
+          setChecked(false)
+        } else {
+          const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, result.version)
+          setChecked(r?.compatible);
+        }
+      })
+      .catch((error) => {
+        setChecked(false);
+      });
+  });
+
+  return checked;
+}
+
+
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f579678f2..0fbbef7c3 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -128,132 +128,120 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
   const isSessionExpired = !onRegister
 
   return (
-    <Fragment>
-      <h1 class="nav"></h1>
-      {/* {error && (
-        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
-      )} */}
-
-      <div class="flex min-h-full flex-col justify-center">
-        {/* <div class="sm:mx-auto sm:w-full sm:max-w-sm">
-          <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h2>
-        </div> */}
-
-        <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
-          <form class="space-y-6" noValidate
-            onSubmit={(e) => {
-              e.preventDefault();
-            }}
-            autoCapitalize="none"
-            autoCorrect="off"
-          >
-            <div>
-              <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
-                <i18n.Translate>Username</i18n.Translate>
-              </label>
-              <div class="mt-2">
-                <input
-                  ref={ref}
-                  autoFocus
-                  type="text"
-                  name="username"
-                  id="username"
-                  class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
-                  value={username ?? ""}
-                  disabled={isSessionExpired}
-                  enterkeyhint="next"
-                  placeholder="identification"
-                  autocomplete="username"
-                  required
-                  onInput={(e): void => {
-                    setUsername(e.currentTarget.value);
-                  }}
-                />
-                <ShowInputErrorLabel
-                  message={errors?.username}
-                  isDirty={username !== undefined}
-                />
-              </div>
+    <div class="flex min-h-full flex-col justify-center">
+
+      <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
+        <form class="space-y-6" noValidate
+          onSubmit={(e) => {
+            e.preventDefault();
+          }}
+          autoCapitalize="none"
+          autoCorrect="off"
+        >
+          <div>
+            <label for="username" class="block text-sm font-medium leading-6 text-gray-900">
+              <i18n.Translate>Username</i18n.Translate>
+            </label>
+            <div class="mt-2">
+              <input
+                ref={ref}
+                autoFocus
+                type="text"
+                name="username"
+                id="username"
+                class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                value={username ?? ""}
+                disabled={isSessionExpired}
+                enterkeyhint="next"
+                placeholder="identification"
+                autocomplete="username"
+                required
+                onInput={(e): void => {
+                  setUsername(e.currentTarget.value);
+                }}
+              />
+              <ShowInputErrorLabel
+                message={errors?.username}
+                isDirty={username !== undefined}
+              />
             </div>
+          </div>
 
-            <div>
-              <div class="flex items-center justify-between">
-                <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
-              </div>
-              <div class="mt-2">
-                <input
-                  type="password"
-                  name="password"
-                  id="password"
-                  autocomplete="current-password"
-                  class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
-                  enterkeyhint="send"
-                  value={password ?? ""}
-                  placeholder="Password"
-                  required
-                  onInput={(e): void => {
-                    setPassword(e.currentTarget.value);
-                  }}
-                />
-                <ShowInputErrorLabel
-                  message={errors?.password}
-                  isDirty={password !== undefined}
-                />
-              </div>
+          <div>
+            <div class="flex items-center justify-between">
+              <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label>
             </div>
-
-            {isSessionExpired ? <div class="flex justify-between">
-              <button type="submit"
-                class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
-                onClick={(e) => {
-                  e.preventDefault()
-                  doLogin()
-                }}
-              >
-                <i18n.Translate>Cancel</i18n.Translate>
-              </button>
-              
-              <button type="submit"
-                class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-                disabled={!!errors}
-                onClick={(e) => {
-                  e.preventDefault()
-                  doLogin()
-                }}
-              >
-                <i18n.Translate>Renew session</i18n.Translate>
-              </button>
-            </div> : <div>
-              <button type="submit"
-                class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-                disabled={!!errors}
-                onClick={(e) => {
-                  e.preventDefault()
-                  doLogin()
+            <div class="mt-2">
+              <input
+                type="password"
+                name="password"
+                id="password"
+                autocomplete="current-password"
+                class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
+                enterkeyhint="send"
+                value={password ?? ""}
+                placeholder="Password"
+                required
+                onInput={(e): void => {
+                  setPassword(e.currentTarget.value);
                 }}
-              >
-                <i18n.Translate>Log in</i18n.Translate>
-              </button>
-            </div>}
-          </form>
-
-          {bankUiSettings.allowRegistrations && onRegister &&
-            <p class="mt-10 text-center text-sm text-gray-500 border-t">
-              <button type="submit"
-                class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
-                onClick={(e) => {
-                  e.preventDefault()
-                  onRegister()
-                }}
-              >
-                <i18n.Translate>Register</i18n.Translate>
-              </button>
-            </p>
-          }
-        </div>
+              />
+              <ShowInputErrorLabel
+                message={errors?.password}
+                isDirty={password !== undefined}
+              />
+            </div>
+          </div>
+
+          {isSessionExpired ? <div class="flex justify-between">
+            <button type="submit"
+              class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
+              onClick={(e) => {
+                e.preventDefault()
+                doLogin()
+              }}
+            >
+              <i18n.Translate>Cancel</i18n.Translate>
+            </button>
+
+            <button type="submit"
+              class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+              disabled={!!errors}
+              onClick={(e) => {
+                e.preventDefault()
+                doLogin()
+              }}
+            >
+              <i18n.Translate>Renew session</i18n.Translate>
+            </button>
+          </div> : <div>
+            <button type="submit"
+              class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+              disabled={!!errors}
+              onClick={(e) => {
+                e.preventDefault()
+                doLogin()
+              }}
+            >
+              <i18n.Translate>Log in</i18n.Translate>
+            </button>
+          </div>}
+        </form>
+
+        {bankUiSettings.allowRegistrations && onRegister &&
+          <p class="mt-10 text-center text-sm text-gray-500 border-t">
+            <button type="submit"
+              class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
+              onClick={(e) => {
+                e.preventDefault()
+                onRegister()
+              }}
+            >
+              <i18n.Translate>Register</i18n.Translate>
+            </button>
+          </p>
+        }
       </div>
-
-
-    </Fragment>
+    </div>
   );
 }
diff --git a/packages/demobank-ui/src/utils.ts b/packages/demobank-ui/src/utils.ts
index c13b9a3cb..e7673f078 100644
--- a/packages/demobank-ui/src/utils.ts
+++ b/packages/demobank-ui/src/utils.ts
@@ -88,28 +88,6 @@ export enum CashoutStatus {
   PENDING = "pending",
 }
 
-// export function partialWithObjects<T extends object>(obj: T | undefined, () => complete): WithIntermediate<T> {
-//   const root = obj === undefined ? {} : obj;
-//   return Object.entries(root).([key, value]) => {
-
-//   })
-//   return undefined as any
-// }
-
-/**
- * Craft headers with Authorization and Content-Type.
- */
-// export function prepareHeaders(username?: string, password?: string): Headers {
-//   const headers = new Headers();
-//   if (username && password) {
-//     headers.append(
-//       "Authorization",
-//       `Basic ${window.btoa(`${username}:${password}`)}`,
-//     );
-//   }
-//   headers.append("Content-Type", "application/json");
-//   return headers;
-// }
 
 export const PAGE_SIZE = 20;
 export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
-- 
cgit v1.2.3

