gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant-backoffice] branch master updated: copying pybank's look


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated: copying pybank's look. WIP
Date: Sat, 02 Apr 2022 08:56:57 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository merchant-backoffice.

The following commit(s) were added to refs/heads/master by this push:
     new 329acdc  copying pybank's look.  WIP
329acdc is described below

commit 329acdc5035845cf06352c158a6c6479bdf38fd3
Author: ms <ms@taler.net>
AuthorDate: Wed Mar 23 18:51:25 2022 +0100

    copying pybank's look.  WIP
---
 packages/bank/src/assets/logo-white.svg            |   45 +
 packages/bank/src/components/app.tsx               |    2 -
 packages/bank/src/components/menu/LangSelector.tsx |   35 +-
 packages/bank/src/pages/home/index.tsx             |  561 ++++++---
 packages/bank/src/scss/bank.scss                   |   80 ++
 packages/bank/src/scss/colors-bank.scss            |   24 +
 packages/bank/src/scss/demo.scss                   |  138 ++
 packages/bank/src/scss/main.scss                   |  238 +---
 packages/bank/src/scss/pure.scss                   | 1328 ++++++++++++++++++++
 9 files changed, 2031 insertions(+), 420 deletions(-)

diff --git a/packages/bank/src/assets/logo-white.svg 
b/packages/bank/src/assets/logo-white.svg
new file mode 100644
index 0000000..cb1f023
--- /dev/null
+++ b/packages/bank/src/assets/logo-white.svg
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   width="670"
+   height="300"
+   viewBox="0 0 201 90"
+   version="1.1"
+   id="svg8">
+  <g
+     id="logo">
+    <g
+       id="circles"
+       
style="fill:#FFF;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.327943">
+      <path
+         d="m 86.662153,1.1211936 c 15.589697,0 29.129227,9.4011664 
35.961027,23.2018054 h -5.81736 C 110.4866,13.623304 99.349002,6.5180852 
86.662153,6.5180852 c -19.690571,0 -35.652876,17.1120008 -35.652876,38.2205688 
0,10.331797 3.825597,19.704678 10.03957,26.582945 -1.342357,1.120912 
-2.771532,2.127905 -4.275488,3.006754 C 50.071485,66.553412 45.974857,56.15992 
45.974857,44.738654 c 0,-24.089211 18.216325,-43.6174604 40.687296,-43.6174604 
z M 122.51416,65.375898 c -6.86645,13.680134  [...]
+         id="path2350" />
+      <path
+         d="m 64.212372,1.1211936 c 1.052607,0 2.095998,0.042919 
3.128684,0.1270583 C 64.288864,2.8094199 61.427378,4.728606 58.802653,6.9555572 
41.679542,9.7498571 28.559494,25.601563 28.559494,44.738654 c 0,14.264563 
7.29059,26.702023 18.093843,33.268925 -1.593656,0.26719 -3.226966,0.406948 
-4.890748,0.406948 -1.239545,0 -2.46151,-0.07952 -3.663522,-0.229364 C 
29.191129,70.184015 23.525076,58.171633 23.525076,44.738654 23.525076,20.649443 
41.7414,1.1211936 64.212372,1.1211936 Z M 69.62 [...]
+         id="path2352" />
+      <path
+         d="m 41.762589,1.1211936 c 1.064296,0 2.118804,0.044379 
3.162607,0.1302161 -3.046523,1.558961 -5.903162,3.4745139 -8.52358,5.6968133 C 
19.254624,9.7205882 6.1097128,25.583465 6.1097128,44.738654 c 0,21.108568 
15.9624012,38.22057 35.6528762,38.22057 12.599746,0 23.672446,-7.007056 
30.013748,-17.583802 h 5.838515 C 70.748498,79.055727 57.26924,88.356116 
41.762589,88.356116 c -22.470907,0 -40.6871998,-19.52825 -40.6871998,-43.617462 
0,-24.089211 18.2162928,-43.6174604 40.6871998,-4 [...]
+         id="path2354" />
+    </g>
+    <g
+       id="letters"
+       style="fill:#FFF">
+      <path
+         d="m 76.135411,34.409066 h 9.161042 V 29.36588 H 61.857537 v 5.043186 
h 9.161137 v 25.92317 h 5.116737 z"
+         id="path2346" />
+      <path
+         d="m 92.647571,52.856334 h 13.659009 l 2.93009,7.476072 h 5.36461 L 
101.89122,29.144903 H 97.187186 L 84.477089,60.332406 h 5.199533 z m 
11.802109,-4.822276 h -9.944771 l 4.951718,-12.386462 z"
+         id="path2362" />
+      <path
+         d="m 123.80641,29.366084 h -4.58038 v 30.966322 h 20.54728 v 
-4.910253 c -5.32227,0 -10.64463,0 -15.9669,0 z"
+         id="path2356" />
+      <path
+         d="m 166.4722,29.366084 h -21.37564 v 30.966322 h 21.58203 v 
-4.910253 h -16.54771 v -8.27275 h 14.48439 V 42.23925 h -14.48439 v -7.962811 
h 16.34132 z"
+         id="path2360" />
+      <path
+         d="m 191.19035,39.474593 c 0,1.59947 -0.53646,2.87535 
-1.61628,3.818883 -1.07281,0.95124 -2.52409,1.422837 -4.34678,1.422837 h 
-7.44851 V 34.276439 h 7.4073 c 1.9051,0 3.38376,0.435027 4.42939,1.312178 
1.05226,0.870258 1.57488,2.167734 1.57488,3.885976 z m 6.06602,20.857813 
-7.79911,-11.723191 c 1.01771,-0.294794 1.94631,-0.714813 2.78553,-1.260566 
0.83885,-0.545619 1.56122,-1.209263 2.16629,-1.990627 0.60541,-0.781738 
1.07981,-1.681096 1.42369,-2.698345 0.34378,-1.017553 0.5156 [...]
+         id="path2358" />
+    </g>
+  </g>
+</svg>
diff --git a/packages/bank/src/components/app.tsx 
b/packages/bank/src/components/app.tsx
index 36d7b97..5739f3a 100644
--- a/packages/bank/src/components/app.tsx
+++ b/packages/bank/src/components/app.tsx
@@ -2,12 +2,10 @@ import { FunctionalComponent, h } from "preact";
 import { TranslationProvider } from "../context/translation";
 import { BankHome } from "../pages/home/index";
 import { Menu } from "./menu";
-import { LangSelector } from "./menu/LangSelector";
 
 const App: FunctionalComponent = () => {
   return (
     <TranslationProvider>
-      <LangSelector />
       <BankHome />
     </TranslationProvider>
   );
diff --git a/packages/bank/src/components/menu/LangSelector.tsx 
b/packages/bank/src/components/menu/LangSelector.tsx
index fa22a29..25560f2 100644
--- a/packages/bank/src/components/menu/LangSelector.tsx
+++ b/packages/bank/src/components/menu/LangSelector.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from "preact";
+import { h, VNode, Fragment } from "preact";
 import { useState } from "preact/hooks";
 import langIcon from "../../assets/icons/languageicon.svg";
 import { useTranslationContext } from "../../context/translation";
@@ -43,6 +43,39 @@ function getLangName(s: keyof LangsNames | string): string {
   return String(s);
 }
 
+export function LangSelectorLikePy(): VNode {
+  const [updatingLang, setUpdatingLang] = useState(false);
+  const { lang, changeLanguage } = useTranslationContext();
+
+  return (
+    <Fragment>
+      <span>{getLangName(lang)}</span>
+      <div style="position: relative; overflow: visible;">
+        <div
+            class="nav"
+            style="position: absolute; background: #0042b2; max-height: 60vh; 
overflow-y: scroll">
+          <br></br>
+          {Object.keys(messages)
+            .filter((l) => l !== lang)
+              .map((l) => (
+                <a
+                    key={l}
+                    class="navbtn"
+                    value={l}
+                    onClick={() => {
+                      changeLanguage(l);
+                      setUpdatingLang(false);
+                    }}>
+                  {getLangName(l)}
+                </a>
+          ))}
+          <br></br>
+        </div>
+      </div>
+    </Fragment>
+  );
+}
+
 export function LangSelector(): VNode {
   const [updatingLang, setUpdatingLang] = useState(false);
   const { lang, changeLanguage } = useTranslationContext();
diff --git a/packages/bank/src/pages/home/index.tsx 
b/packages/bank/src/pages/home/index.tsx
index 55d1adc..070d091 100644
--- a/packages/bank/src/pages/home/index.tsx
+++ b/packages/bank/src/pages/home/index.tsx
@@ -6,6 +6,8 @@ import { useTranslator, Translate } from "../../i18n";
 import { QR } from "../../components/QR";
 import { useNotNullLocalStorage, useLocalStorage } from "../../hooks";
 import "../../scss/main.scss";
+import talerLogo from "../../assets/logo-white.svg";
+import { LangSelectorLikePy as LangSelector} from 
"../../components/menu/LangSelector";
 
 /**
  * FIXME:
@@ -28,11 +30,11 @@ import "../../scss/main.scss";
  * Globals *
  **********/
 
-
 /************
  * Contexts *
  ***********/
 var CurrencyContext = createContext<any>(null);
+var PageContext = createContext<any>(null);
 
 /**********************************************
  * Type definitions for states and API calls. *
@@ -105,6 +107,13 @@ interface AccountStateType {
  * Helpers. *
  ***********/
 
+/**
+ * Bring the state to show the public accounts page.
+ */
+function goPublicAccounts(pageStateSetter: StateUpdater<PageStateType>) {
+  return () => pageStateSetter((prevState) => ({...prevState, 
showPublicHistories: true}))
+}
+
 /**
  * Validate (the number part of) an amount.  If needed,
  * replace comma with a dot.  Returns 'false' whenever
@@ -277,6 +286,7 @@ function usePageState(
 ): [PageStateType, StateUpdater<PageStateType>] {
   const ret = useNotNullLocalStorage("page-state", JSON.stringify(state));
   const retObj: PageStateType = JSON.parse(ret[0]);
+  console.log("Current page state", retObj);
   const retSetter: StateUpdater<PageStateType> = function(val) {
     const newVal = val instanceof Function ? JSON.stringify(val(retObj)) : 
JSON.stringify(val)
     console.log("Setting new page state", newVal)
@@ -624,8 +634,10 @@ async function registrationCall(
       headers: headers
     });
   } catch (error) {
-    console.log("Could not POST new registration to the bank", error);
-    pageStateSetter((prevState) => ({ ...prevState, hasError: true }));
+    console.log(`Could not POST new registration to the bank (${url.href})`, 
error);
+    pageStateSetter((prevState) => ({
+      ...prevState, hasError: true, error: "Registration failed, please 
report."
+    }));
     return;
   }
   if (!res.ok) {
@@ -656,6 +668,74 @@ async function registrationCall(
  * Functional components. *
  *************************/
 
+function ErrorBanner(Props: any): VNode {
+  const [pageState, pageStateSetter] = Props.pageState;
+  const i18n = useTranslator();
+  if (!pageState.hasError) return;
+  return (
+    <p class="informational informational-fail">{pageState.error}
+      &nbsp;&nbsp;<a href="#" onClick={() => {
+        pageStateSetter((prevState: PageStateType) =>({...prevState, hasError: 
false}))}}>
+        {i18n`Clear`}
+      </a>
+    </p>);
+}
+
+function BankFrame(Props: any): VNode {
+  const i18n = useTranslator();
+  const [pageState, pageStateSetter] = useContext(PageContext);
+  console.log("BankFrame state", pageState);
+  return (
+    <Fragment>
+      <header class="demobar" style="display: flex; flex-direction: row; 
justify-content: space-between;">
+        <div style="max-width: 50em; margin-left: 2em;">
+          <h1>
+            <span class="it">
+              <a href="/">{i18n`Demo Bank`}</a>
+            </span>
+          </h1>
+          <p><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="#" onclick={goPublicAccounts(pageStateSetter)}>Public 
Accounts</a>.
+       </Translate></p>
+        </div>
+        <a href="https://taler.net/";>
+        <img
+            src={talerLogo}
+         height="100"
+         width="224"
+         style="margin: 2em 2em">
+        </img>
+        </a>
+      </header>
+      <div style="display:flex; flex-direction: column;" class="navcontainer">
+        <nav class="demolist">
+          <a href="#">DEMO SITE 0</a>
+          <a href="#">DEMO SITE 1</a>
+          <a href="#">DEMO SITE 2</a>
+          <a href="#">DEMO SITE 3</a>
+          <a href="#">DEMO SITE 4</a>
+          <a href="#">DEMO SITE 5</a>
+          <div class="right">
+            <LangSelector />
+         </div>
+        </nav>
+      </div>
+      <section id="main" class="content">
+        <ErrorBanner pageState={[pageState, pageStateSetter]}/>
+        {Props.children}
+        <hr></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"></div>
+        <p>Copyright &copy; 2014&mdash;2022 Taler Systems SA</p>
+      </section>
+    </Fragment>);
+}
+
 function PaytoWireTransfer(Props: any): VNode {
   const {backendState, pageStateSetter} = Props;
   const currency = useContext(CurrencyContext);
@@ -730,18 +810,30 @@ function TalerWithdrawal(Props: any): VNode {
       )}}>{i18n`Charge Taler wallet`}
     </button>;
 
-  return <div>
-    <input
-      type="text"
-      placeholder="amount"
-      required
-      pattern={amountRegex}
-      onInput={(e): void => {
-        submitAmount = e.currentTarget.value
-      }} />
-    <label>{currency}</label> 
-    { submitButton }
-  </div>
+    return (<article>
+      <div>
+        <h2>{i18n`Withdraw Money into a Taler wallet`}</h2>
+        <div id="reserve-form"
+            class="pure-form"
+            method="post"
+            name="tform">
+          {i18n`Amount to withdraw`}
+          <select id="reserve-amount" name="withdraw-amount" class="amount" 
autofocus>
+            <option value="5.00">5.00</option>
+            <option value="10.00">10.00</option>
+            <option value="15.00">15.00</option>
+            <option value="20.00">20.00</option>
+          </select>
+          <input
+             type="text"
+             readonly
+             class="currency-indicator"
+             size={balance.currency.length}
+             tabindex="-1" value="{{ currency }}" />
+         {submitButton}
+        </div>
+      </div>
+    </article>);
 }
 
 /**
@@ -751,82 +843,107 @@ function LoginForm(Props: any): VNode {
   const {backendStateSetter, pageStateSetter} = Props;
   var submitData: CredentialsRequestType;
   const i18n = useTranslator();
-  return <Fragment>
-    <input
-      type="text"
-      placeholder="username"
-      required
-      onInput={(e): void => {
-        submitData = {
-          ...submitData,
-          username: e.currentTarget.value,
-        };}} />
-    <input
-      type="text"
-      placeholder="password"
-      required
-      onInput={(e): void => {
-        submitData = {
-          ...submitData,
-          password: e.currentTarget.value,
-        };}} />
-    <button
-      onClick={() => {
-        loginCall(
-          submitData,
-          backendStateSetter,
-          pageStateSetter
-        );
-      }}>{i18n`Login`}</button>
-  </Fragment>
+  // FIXME: try removing the outer Fragment.
+  return <div class="login-form">
+    <h2>{i18n`Please login!`}</h2>
+    <div class="pure-form">
+      <input
+        type="text"
+        placeholder="username"
+        required
+        onInput={(e): void => {
+          submitData = {
+            ...submitData,
+            username: e.currentTarget.value,
+          };}} />
+      <input
+        type="password"
+        placeholder="password"
+        required
+        onInput={(e): void => {
+          submitData = {
+            ...submitData,
+            password: e.currentTarget.value,
+          };}} />
+      <button
+        type="submit"
+        class="pure-button pure-button-primary"
+        onClick={() => {
+          if (submitData.password.length > 0 && submitData.username.length > 0)
+            loginCall(
+              submitData,
+              backendStateSetter,
+              pageStateSetter
+            );
+        }}>{i18n`Login`}</button>
+    </div>
+  </div>
 }
 
 /**
  * Collect and submit registration data.
  */
 function RegistrationForm(Props: any): VNode {
-  var {pageState, pageStateSetter} = Props;
-  var submitData: CredentialsRequestType;
+  const [pageState, pageStateSetter] = useContext(PageContext);
+  var submitData: CredentialsRequestType = {};
   const i18n = useTranslator();
-  return (<Fragment>
-    <h2>Register!</h2>
-    <input
-      type="text"
-      placeholder="username"
-      required
-      onInput={(e): void => {
-        submitData = {
-          ...submitData,
-          username: e.currentTarget.value,
-        };}} />
-    <input
-      type="text"
-      placeholder="password"
-      required
-      onInput={(e): void => {
-        submitData = {
-          ...submitData,
-          password: e.currentTarget.value,
-        };}} />
-    <button
-      onClick={() => {
-        registrationCall(
-          submitData,
-          Props.backendStateSetter,
-          pageStateSetter
-        );}}>{i18n`Register`}</button>
-    <a onClick={() => {
-      pageStateSetter((prevState: PageStateType) =>
-        ({...prevState, tryRegister: false}))}}>Go back</a>
-  </Fragment>);
+
+  return (
+    <BankFrame>
+      <h1 class="nav">{i18n`Register to the $currency bank!`}</h1>
+      <aside class="sidebar" id="left"></aside>
+        <article>
+          <a href="#" onClick={() => {
+              pageStateSetter((prevState: PageStateType) =>({...prevState, 
tryRegister: false}))}}>
+            {i18n`Go back`}
+         </a>
+        </article>
+        <article>
+          <div class="register-form">
+            <h1>{i18n`Registration form`}</h1>
+            <div class="pure-form">
+              <input
+                type="text"
+                placeholder="username"
+                required
+                autofocus
+                onInput={(e): void => {
+                  submitData = {
+                    ...submitData,
+                    username: e.currentTarget.value,
+                  };}} />
+              <input
+                type="password"
+                placeholder="password"
+                required
+                onInput={(e): void => {
+                  submitData = {
+                    ...submitData,
+                    password: e.currentTarget.value,
+                  };}} />
+              <button
+                class="pure-button pure-button-primary"
+                onClick={() => {
+                 if (!("password" in submitData) || !("username" in 
submitData)) return;
+                 if (submitData.password.length === 0 || 
submitData.username.length === 0) return;
+                  registrationCall(
+                    submitData,
+                    Props.backendStateSetter, // will store BE URL, if OK.
+                    pageStateSetter
+                  );}}>{i18n`Register`}</button>
+            </div>
+          </div>
+       </article>
+     </BankFrame>
+  )
 }
 
 /**
  * Show one page of transactions.
  */
 function Transactions(Props: any): VNode {
-
   const { pageNumber, accountLabel } = Props;
+  const i18n = useTranslator();
   const { data, error } = useSWR(
     `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`
   );
@@ -849,19 +966,37 @@ function Transactions(Props: any): VNode {
     return <p>"Transactions page loading..."</p>;
   }
   console.log(`History data of ${accountLabel}`, data);
-  return <ul>{
-    data.transactions.map(function(item: any) {
-      const sign = item.direction == "DBIT" ? "-" : "";
-      const counterpart = item.direction == "DBIT" ? item.creditorIban : 
item.debtorIban;
-      // Pattern:
-      //
-      // DD/MM YYYY subject -5 EUR
-      // DD/MM YYYY subject 5 EUR
-      const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/
-      const dateParse = dateRegex.exec(item.date)
-      const date = dateParse !== null ? `${dateParse[3]}/${dateParse[2]} 
${dateParse[1]}` : "date not found"
-      return <li>{date} {item.subject} {sign}{item.amount} {item.currency}</li>
-  })}</ul>
+  return (<div class="results">
+    <table class="pure-table pure-table-striped">
+      <thead>
+       <tr>
+          <th>{i18n`Date`}</th>
+          <th>{i18n`Amount`}</th>
+          <th>{i18n`Counterpart`}</th>
+          <th>{i18n`Subject`}</th>
+       </tr>
+      </thead>
+      <tbody>
+        {data.transactions.map(function(item: any) {
+          const sign = item.direction == "DBIT" ? "-" : "";
+          const counterpart = item.direction == "DBIT" ? item.creditorIban : 
item.debtorIban;
+          // Pattern:
+          //
+          // DD/MM YYYY subject -5 EUR
+          // DD/MM YYYY subject 5 EUR
+          const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/
+          const dateParse = dateRegex.exec(item.date)
+          const date = dateParse !== null ? `${dateParse[3]}/${dateParse[2]} 
${dateParse[1]}` : "date not found"
+          return (<tr>
+            <td>{date}</td>
+            <td>{sign}{item.amount} {item.currency}</td>
+            <td>{counterpart}</td>
+            <td>{item.subject}</td>
+          </tr>);
+        })}
+      </tbody>
+    </table>
+  </div>);
 }
 
 /**
@@ -873,6 +1008,7 @@ function Account(Props: any): VNode {
     transferOutcome,
     talerWithdrawUri,
     accountLabel } = Props;
+  const pageState = useContext(PageContext);
   /**
    * This part shows a list of transactions: with 5 elements by
    * default and offers a "load more" button.
@@ -880,7 +1016,7 @@ function Account(Props: any): VNode {
   var [txPageNumber, setTxPageNumber] = useTransactionPageNumber()
   var txsPages = []
   for (let i = 0; i <= txPageNumber; i++) {
-    txsPages.push(<Transactions accountLabel={Props.accountLabel} 
pageNumber={i} />)
+    txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />)
   }
   const i18n = useTranslator();
   /**
@@ -889,18 +1025,41 @@ function Account(Props: any): VNode {
   const { data, error } = useSWR(`access-api/accounts/${accountLabel}`);
   if (typeof error !== "undefined") {
     console.log("account error", error);
+
+    /**
+     * FIXME: try only one invocation of pageStateSetter,
+     * after having decided the error message in the case-branch.
+     */
     switch(error.status) {
       case 404: {
-        return <p>Username or account label not found</p>
+        pageState[1]((prevState) => ({
+          ...prevState,
+          hasError: true,
+          isLoggedIn: false,
+         error: i18n`Username or account label not found.`
+        }));
+        return <p>Profile not found...</p>;
       }
       case 401: {
-        return <p>Wrong credentials given</p>
+        pageState[1]((prevState) => ({
+          ...prevState,
+          hasError: true,
+          isLoggedIn: false,
+         error: i18n`Wrong credentials given.`
+        }));
+        return <p>Wrong credentials...</p>;
       }
       default: {
-        return <p>Account information could not be retrieved.</p>
+        pageState[1]((prevState) => ({
+          ...prevState,
+          hasError: true,
+          isLoggedIn: false,
+         error: i18n`Account information could not be retrieved.`
+        }));
+        return <p>Unknown problem...</p>;
+      }
       }
     }
-  }
   if (!data) return <p>Retrieving the profile page...</p>;
 
   /**
@@ -909,20 +1068,20 @@ function Account(Props: any): VNode {
    * have been reported earlier.
    */
   if (transferOutcome) {
-    return <Fragment>
+    return <BankFrame>
       <p>{transferOutcome}</p>
       {Props.children}
-    </Fragment>
+    </BankFrame>
   }
 
   /**
    * Withdrawal reached a final state: show it.
    */
   if (withdrawalOutcome) {
-    return <Fragment>
+    return <BankFrame>
       <p>{withdrawalOutcome}</p>
       {Props.children}
-    </Fragment>
+    </BankFrame>
   }
 
   /**
@@ -948,17 +1107,29 @@ function Account(Props: any): VNode {
     </Fragment>);
   }
   const balance = parseAmount(data.balance.amount)
-  return (<Fragment>
-    <p>Hello {accountLabel}, {getIbanFromPayto(data.paytoUri)}.</p>
-    <p>Your balance is {`${balance.value} ${balance.currency}`}.</p>
+  return (<BankFrame>
     <div>
-      <span>{i18n`Last transactions:`}</span> { txsPages }
-      <button onClick={() => setTxPageNumber(txPageNumber + 1)}>{i18n`Load 
more transactions`}</button>
+      <h1 class="nav">
+       <Translate>Welcome, {accountLabel} 
({getIbanFromPayto(data.paytoUri)})!</Translate>
+      </h1>
+      <a
+        href="#"
+        class="pure-button logout-button"
+        onClick={() => {
+          setTxPageNumber(0);
+          pageStateSetter((prevState) => {
+            const {
+              talerWithdrawUri,
+              withdrawalOutcome,
+              withdrawalId, ...rest } = prevState;
+              return {...rest, isLoggedIn: false, withdrawalInProgress: false};
+          })
+          }}>[{i18n`Logout`}]</a><br />
     </div>
-    <CurrencyContext.Provider value={balance.currency}>
-      {Props.children}
-    </CurrencyContext.Provider>
-  </Fragment>);
+    <section id="menu">
+      <p>{i18n`Bank account balance:`} <br /> <b>{`${balance.value} 
${balance.currency}`}</b></p>
+    </section>
+  </BankFrame>);
 }
 
 /**
@@ -1013,14 +1184,30 @@ function SWRWithoutCredentials(Props: any): VNode {
 function PublicHistories(Props: any): VNode {
   const [showAccount, setShowAccount] = useState<string | undefined>();
   const { data, error } = useSWR("access-api/public-accounts")
+  const i18n = useTranslator();
+
   if (typeof error !== "undefined") {
     console.log("account error", error);
     switch(error.status) {
       case 404: {
-        return <p>List of public accounts was not found</p>
+        console.log("public accounts: 404", error);
+        Props.pageStateSetter((prevState) => ({
+          ...prevState,
+          hasError: true,
+          showPublicHistories: false,
+         error: i18n`List of public accounts was not found.`
+        }));
+        return;
       }
       default: {
-        return <p>List of public accounts could not be retrieved.</p>
+        console.log("public accounts: non-404 error", error);
+        Props.pageStateSetter((prevState) => ({
+          ...prevState,
+          hasError: true,
+          showPublicHistories: false,
+         error: i18n`List of public accounts could not be retrieved.`
+        }));
+        return;
       }
     }
   }
@@ -1028,17 +1215,6 @@ function PublicHistories(Props: any): VNode {
   var txs: any = {};
   var accountsBar = [];
 
-  // Ask first story of all the public accounts.
-  for (const account of data.publicAccounts) {
-    console.log("Asking transactions for", account.accountLabel)
-    accountsBar.push(
-      <li><a onClick={() => 
setShowAccount(account.accountLabel)}>{account.accountLabel}</a></li>
-    );
-    txs[account.accountLabel] = 
-      <div>{account.accountLabel} latest transactions:
-        <Transactions accountLabel={account.accountLabel} pageNumber={0} />
-      </div>
-  }
   /**
    * Show the account specified in the props, or just one
    * from the list if that's not given.
@@ -1046,10 +1222,29 @@ function PublicHistories(Props: any): VNode {
   if (typeof showAccount === "undefined" && Object.keys(txs).length > 0)
     setShowAccount(Object.keys(txs).pop());
   console.log(`Public history tab: ${showAccount}`);
+
+  // Ask first story of all the public accounts.
+  for (const account of data.publicAccounts) {
+    console.log("Asking transactions for", account.accountLabel)
+    accountsBar.push(
+      <li>
+        <a
+         class={(account.accountLabel == showAccount) ? "pure-menu-item 
pure-menu" : "pure-menu-item pure-menu-selected"}
+         onClick={() => 
setShowAccount(account.accountLabel)}>{account.accountLabel}</a>
+      </li>
+    );
+    txs[account.accountLabel] = <Transactions 
accountLabel={account.accountLabel} pageNumber={0} />
+  }
+
   return <Fragment>
-    <ul>{accountsBar}</ul>
-    {typeof showAccount !== "undefined" ? txs[showAccount] : <p>No public 
transactions found.</p>}
-    {Props.children}
+    <h1 class="nav">{i18n`History of public accounts`}</h1>
+    <section id="main">
+      <div name="accountMenu" class="pure-menu pure-menu-horizontal">
+        <ul class="pure-menu-list">{accountsBar}</ul>
+        {typeof showAccount !== "undefined" ? txs[showAccount] : <p>No public 
transactions found.</p>}
+        {Props.children}
+      </div>
+    </section>
   </Fragment>;
 }
 
@@ -1064,18 +1259,9 @@ export function BankHome(): VNode {
   const setTxPageNumber = useTransactionPageNumber()[1];
   var i18n = useTranslator();
 
-  if (pageState.hasError) {
-    return <Fragment>
-      <p>{i18n`Page has a problem:`} {pageState.error}</p>
-      <a onClick={() => {
-        pageStateSetter((prevState) => ({...prevState, hasError: false}))
-      }}>{i18n`Go back`}</a>
-    </Fragment>;
-  }
-
   if (pageState.showPublicHistories) {
     return (<SWRWithoutCredentials baseUrl={getRootPath()}>
-      <PublicHistories>
+      <PublicHistories pageStateSetter={pageStateSetter}>
         <a onClick={() => {
           pageStateSetter((prevState: PageStateType) =>
             ({...prevState, showPublicHistories: false}))}}>Go back</a>
@@ -1084,10 +1270,11 @@ export function BankHome(): VNode {
   }
 
   if (pageState.tryRegister) {
-    return (<RegistrationForm
-      pageState={pageState}
-      pageStateSetter={pageStateSetter}
-      backendStateSetter={backendStateSetter} />);
+    return (
+      <PageContext.Provider value={[pageState, pageStateSetter]}>
+        <RegistrationForm backendStateSetter={backendStateSetter} />
+      </PageContext.Provider>
+    );
   }
   /**
    * Credentials were correct, now render the bank account page,
@@ -1099,32 +1286,29 @@ export function BankHome(): VNode {
       pageStateSetter((prevState) => ({
         ...prevState,
        hasError: true,
+       isLoggedIn: false,
        error: i18n`Page has a problem: logged in but backend state is lost.`
       }));
       return <p>Error: waiting for details...</p>;
     }
-
+    console.log("Showing the profile page..");
     return (
       <SWRWithCredentials
-        username={backendState.username}
-        password={backendState.password}
-        backendUrl={backendState.url}>
-        <Fragment>
-          <Account
-            withdrawalOutcome={pageState.withdrawalOutcome}
-            talerWithdrawUri={pageState.talerWithdrawUri}
-            transferOutcome={pageState.transferOutcome}
-            accountLabel={backendState.username}>
+          username={backendState.username}
+          password={backendState.password}
+          backendUrl={backendState.url}>
+        <PageContext.Provider value={[pageState, pageStateSetter]}>
+          <Account>
   
             { /**
                * No action is currently being performed (page is 'pristine'):
-              * offer the Taler withdrawal button.
+               * offer the Taler withdrawal button.
                */
               !pageState.withdrawalInProgress && !pageState.transferOutcome && 
<TalerWithdrawal
-               backendState={backendState}
-               pageStateSetter={pageStateSetter} />
+                backendState={backendState}
+               pageStateSetter={pageStateSetter} />
             }
-
+  
             { /**
                * Wire transfer reached a persisten state: offer to
                * return back to the pristine profile page.
@@ -1143,10 +1327,10 @@ export function BankHome(): VNode {
                 pageStateSetter((prevState) => {
                   const { withdrawalOutcome, withdrawalId, ...rest } = 
prevState;
                   return {
-                   ...rest,
-                   withdrawalInProgress: false
-                 };}
-               )}}>{i18n`Close Taler withdrawal`}</button>
+                   ...rest,
+                   withdrawalInProgress: false
+                 };}
+               )}}>{i18n`Close Taler withdrawal`}</button>
             }
   
             { /**
@@ -1164,36 +1348,49 @@ export function BankHome(): VNode {
                     pageState.withdrawalId,
                     pageStateSetter);}}>{i18n`Abort withdrawal`}</button>
               </div>
-           }
-
-           { /**
-              * Profile page is pristine: offer the wire transfer form.
-              */
+            }
+  
+            { /**
+               * Profile page is pristine: offer the wire transfer form.
+               */
               !pageState.withdrawalInProgress &&
-               !pageState.transferOutcome &&
-                 <PaytoWireTransfer pageStateSetter={pageStateSetter}
-                                    backendState={backendState} />
-           }
+                !pageState.transferOutcome &&
+                  <PaytoWireTransfer pageStateSetter={pageStateSetter}
+                                    backendState={backendState} />
+            }
           </Account>
-          { /* The user is logged in: offer to log out.  */ }
-          <button onClick={() => {
-            setTxPageNumber(0);
-            pageStateSetter((prevState) => {
-              const {
-                talerWithdrawUri,
-                withdrawalOutcome,
-                withdrawalId, ...rest } = prevState;
-                return {...rest, isLoggedIn: false, withdrawalInProgress: 
false};
-            })
-          }}>Sign out</button>
-        </Fragment>
+        </PageContext.Provider>
       </SWRWithCredentials>
     );
   } // end of logged-in state.
+  /**
+   * Currency only known _after_ a user logs in / registers.  Thus not
+   * mentioning the currency right at the home page (as instead the Python
+   * bank did.)  FIXME: currency needed at startup too.
+   */
   return (
-    <Fragment>
-      <p>{i18n`Welcome to euFin bank: Taler+IBAN now possible!`}</p>
-      <SWRWithoutCredentials baseUrl={getRootPath()}>
+    <PageContext.Provider value={[pageState, pageStateSetter]}>
+      <BankFrame>
+        <h1 class="nav">{i18n`Welcome to the $currency bank!`}</h1>
+        <LoginForm
+            pageStateSetter={pageStateSetter}
+            backendStateSetter={backendStateSetter} />
+        <p><Translate>
+          If you are a new customer please <a href="#" onClick={() => {
+            pageStateSetter((prevState) => ({...prevState, tryRegister: 
true}))}}>register!</a>
+       &nbsp;&nbsp; Registration is fast and free, and it gives you a 
registration bonus
+       of 100 $currency
+        </Translate></p>
+        <p><Translate>To view transactions of public accounts, please <a 
href="#"
+          onClick={goPublicAccounts(pageStateSetter)}>click here</a>.
+        </Translate></p>
+      </BankFrame>
+    </PageContext.Provider>
+  );
+}
+
+/*
+<SWRWithoutCredentials baseUrl={getRootPath()}>
         <LoginForm
          pageStateSetter={pageStateSetter}
           backendStateSetter={backendStateSetter} />
@@ -1203,6 +1400,4 @@ export function BankHome(): VNode {
           pageStateSetter((prevState) => (
             {...prevState, showPublicHistories: true}))}}>transactions</a></p>
       </SWRWithoutCredentials>
-    </Fragment>
-  );
-}
+*/
diff --git a/packages/bank/src/scss/bank.scss b/packages/bank/src/scss/bank.scss
new file mode 100644
index 0000000..329bd1e
--- /dev/null
+++ b/packages/bank/src/scss/bank.scss
@@ -0,0 +1,80 @@
+.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;
+}
+
+a.page-number {
+  color: blue;
+}
+
+a.current-page-number {
+  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;
+}
+
+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;
+}
+
+/*
+ * Currency indicator to the right of input fields,
+ * with non-rounded corners to the left.
+ */
+#main .currency-indicator {
+  color: black;
+  display: inline-block;
+  border-radius: 0px 4px 4px 0px;
+}
+
+#main .fieldlabel {
+  display: block;
+  padding-bottom: 0.5em;
+}
+
+#main .fieldbox {
+  margin-right: 1em;
+  margin-bottom: 0.5em;
+}
+
+#logout-button {
+  display: block;
+  width: fit-content;
+}
diff --git a/packages/bank/src/scss/colors-bank.scss 
b/packages/bank/src/scss/colors-bank.scss
new file mode 100644
index 0000000..8f41bdf
--- /dev/null
+++ b/packages/bank/src/scss/colors-bank.scss
@@ -0,0 +1,24 @@
+nav,
+nav a,
+nav span,
+.navcontainer,
+.demobar,
+.navbtn {
+  color: white;
+  background: #c00000;
+}
+
+nav a.active,
+nav span.active,
+.navbtn.active {
+  background-color: #7a0606;
+}
+
+nav a.active:hover,
+nav span.active:hover,
+.navbtn.active:hover,
+nav a:hover,
+nav span:hover,
+.navbtn:hover {
+  background: #df3d3d;
+}
diff --git a/packages/bank/src/scss/demo.scss b/packages/bank/src/scss/demo.scss
new file mode 100644
index 0000000..d075c20
--- /dev/null
+++ b/packages/bank/src/scss/demo.scss
@@ -0,0 +1,138 @@
+@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;
+}
+
+.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: 2em;
+  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: 10000;
+}
+
+nav {
+  left: 1vw;
+  position: relative;
+  background: #0042b2;
+  z-index: 10000;
+}
+
+nav a,
+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 span,
+.navbtn {
+  padding: 15px 32px;
+}
+
+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 span.active:hover,
+.navbtn.active:hover {
+  background: #3daee9;
+}
+
+nav a,
+nav span,
+.navbtn {
+  cursor: pointer;
+}
+
+nav .right {
+  float: right;
+  margin-right: 5vw;
+}
+nav .right div.nav {
+  display: none;
+}
+nav .right div.nav:hover {
+  display: block;
+}
+
+nav .right:hover div.nav {
+  display: block;
+}
+
+.langbtn {
+  width: 100%;
+  text-align: left;
+}
diff --git a/packages/bank/src/scss/main.scss b/packages/bank/src/scss/main.scss
index 9311fbb..ebe36b9 100644
--- a/packages/bank/src/scss/main.scss
+++ b/packages/bank/src/scss/main.scss
@@ -1,234 +1,4 @@
-/*
- 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)
- */
-
-/* Theme style (colors & sizes) */
-@import "theme-default";
-
-/* Core Libs & Lib configs */
-@import "libs/all";
-
-/* Mixins */
-@import "mixins";
-
-/* Theme components */
-@import "nav-bar";
-@import "aside";
-@import "title-bar";
-@import "hero-bar";
-@import "card";
-@import "table";
-@import "tiles";
-@import "form";
-@import "main-section";
-@import "modal";
-@import "footer";
-@import "misc";
-@import "custom-calendar";
-@import "loading";
-
-@import "fonts/nunito.css";
-@import "icons/materialdesignicons-4.9.95.min.css";
-
-$tooltip-color: red;
-
-@import 
"../../node_modules/@creativebulma/bulma-tooltip/dist/bulma-tooltip.min.css";
-// @import "../../node_modules/bulma-timeline/dist/css/bulma-timeline.min.css";
-
-.notification {
-  background-color: transparent;
-}
-
-.timeline .timeline-item .timeline-content {
-  padding-top: 0;
-}
-
-.timeline .timeline-item:last-child::before {
-  display: none;
-}
-
-.timeline .timeline-item .timeline-marker {
-  top: 0;
-}
-
-.toast {
-  position: absolute;
-  width: 60%;
-  margin-left: 10%;
-  margin-right: 10%;
-  z-index: 999;
-
-  display: flex;
-  flex-direction: column;
-  padding: 15px;
-  text-align: center;
-  pointer-events: none;
-}
-
-.toast > .message {
-  white-space: pre-wrap;
-  opacity: 80%;
-}
-
-div {
-  &.is-loading {
-    position: relative;
-    pointer-events: none;
-    opacity: 0.5;
-    &:after {
-      // @include loader;
-      position: absolute;
-      top: calc(50% - 2.5em);
-      left: calc(50% - 2.5em);
-      width: 5em;
-      height: 5em;
-      border-width: 0.25em;
-    }
-  }
-}
-
-input[type="checkbox"]:indeterminate + .check {
-  background: red !important;
-}
-
-.right-sticky {
-  position: sticky;
-  right: 0px;
-  background-color: $white;
-}
-
-.right-sticky .buttons {
-  flex-wrap: nowrap;
-}
-
-.table.is-striped tbody tr:not(.is-selected):nth-child(even) .right-sticky {
-  background-color: #fafafa;
-}
-
-tr:hover .right-sticky {
-  background-color: hsl(0, 0%, 80%);
-}
-.table.is-striped tbody tr:nth-child(even):hover .right-sticky {
-  background-color: hsl(0, 0%, 95%);
-}
-
-.content-full-size {
-  height: calc(100% - 3rem);
-  position: absolute;
-  width: calc(100% - 14rem);
-  display: flex;
-}
-
-.content-full-size .column .card {
-  min-width: 200px;
-}
-
-@include touch {
-  .content-full-size {
-    height: 100%;
-    position: absolute;
-    width: 100%;
-  }
-}
-
-.column.is-half {
-  flex: none;
-  width: 50%;
-}
-
-input:read-only {
-  cursor: initial;
-}
-
-[data-tooltip]:before {
-  max-width: 15rem;
-  width: max-content;
-  text-align: left;
-  transition: opacity 0.1s linear 1s;
-  // transform: inherit !important;
-  white-space: pre-wrap !important;
-  font-weight: normal;
-  // position: relative;
-}
-
-.icon[data-tooltip]:before {
-  transition: none;
-  z-index: 5;
-}
-
-span[data-tooltip] {
-  border-bottom: none;
-}
-
-div[data-tooltip]::before {
-  position: absolute;
-}
-
-.modal-card-body > p {
-  padding: 1em;
-}
-
-.modal-card-body > p.warning {
-  background-color: #fffbdd;
-  border: solid 1px #f2e9bf;
-}
-
-.home {
-  padding: 1em 1em;
-  min-height: 100%;
-  width: 100%;
-  // max-width: 40em;
-}
-
-// .home div {
-//   margin-top: 0.5em;
-//   margin-bottom: 0.5em;
-// }
-
-.policy {
-  padding: 0.5em;
-  border: 1px solid black;
-  border-radius: 0.5em;
-  border-radius: 0.5em;
-}
-
-.home > #error {
-  padding: 0.5em;
-  border: 1px solid black;
-  background-color: rgb(228, 189, 197);
-  border-radius: 0.5em;
-}
-
-.profile {
-  padding: 56px 20px;
-  min-height: 100%;
-  width: 100%;
-}
-
-.notfound {
-  padding: 0 5%;
-  margin: 100px 0;
-}
-
-h1 {
-  font-size: 1.5em;
-  margin-top: 0.8em;
-  margin-bottom: 0.8em;
-}
+@import "pure";
+@import "bank";
+@import "demo";
+@import "colors-bank";
diff --git a/packages/bank/src/scss/pure.scss b/packages/bank/src/scss/pure.scss
new file mode 100644
index 0000000..3dd68e6
--- /dev/null
+++ b/packages/bank/src/scss/pure.scss
@@ -0,0 +1,1328 @@
+/*!
+Pure v0.6.2
+Copyright 2013 Yahoo!
+Licensed under the BSD License.
+https://github.com/yahoo/pure/blob/master/LICENSE.md
+*/
+/*!
+normalize.css v^3.0 | MIT License | git.io/normalize
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS and IE text size adjust after device orientation change,
+ *    without disabling user zoom.
+ */
+html {
+  font-family: sans-serif;
+  /* 1 */
+  -ms-text-size-adjust: 100%;
+  /* 2 */
+  -webkit-text-size-adjust: 100%;
+  /* 2 */ }
+
+/**
+ * Remove default margin.
+ */
+body {
+  margin: 0; }
+
+/* HTML5 display definitions
+   ========================================================================== 
*/
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11
+ * and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+  display: block; }
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+audio,
+canvas,
+progress,
+video {
+  display: inline-block;
+  /* 1 */
+  vertical-align: baseline;
+  /* 2 */ }
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+audio:not([controls]) {
+  display: none;
+  height: 0; }
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
+ */
+[hidden],
+template {
+  display: none; }
+
+/* Links
+   ========================================================================== 
*/
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+a {
+  background-color: transparent; }
+
+/**
+ * Improve readability of focused elements when they are also in an
+ * active/hover state.
+ */
+a:active,
+a:hover {
+  outline: 0; }
+
+/* Text-level semantics
+   ========================================================================== 
*/
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+abbr[title] {
+  border-bottom: 1px dotted; }
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+b,
+strong {
+  font-weight: bold; }
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+dfn {
+  font-style: italic; }
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0; }
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+mark {
+  background: #ff0;
+  color: #000; }
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+small {
+  font-size: 80%; }
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline; }
+
+sup {
+  top: -0.5em; }
+
+sub {
+  bottom: -0.25em; }
+
+/* Embedded content
+   ========================================================================== 
*/
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+img {
+  border: 0; }
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+svg:not(:root) {
+  overflow: hidden; }
+
+/* Grouping content
+   ========================================================================== 
*/
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+figure {
+  margin: 1em 40px; }
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+hr {
+  box-sizing: content-box;
+  height: 0; }
+
+/**
+ * Contain overflow in all browsers.
+ */
+pre {
+  overflow: auto; }
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, monospace;
+  font-size: 1em; }
+
+/* Forms
+   ========================================================================== 
*/
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+/**
+ * 1. Correct color not being inherited.
+ *    Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+button,
+input,
+optgroup,
+select,
+textarea {
+  color: inherit;
+  /* 1 */
+  font: inherit;
+  /* 2 */
+  margin: 0;
+  /* 3 */ }
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+button {
+  overflow: visible; }
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+button,
+select {
+  text-transform: none; }
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ *    and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ *    `input` and others.
+ */
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button;
+  /* 2 */
+  cursor: pointer;
+  /* 3 */ }
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+button[disabled],
+html input[disabled] {
+  cursor: default; }
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  border: 0;
+  padding: 0; }
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+input {
+  line-height: normal; }
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+input[type="checkbox"],
+input[type="radio"] {
+  box-sizing: border-box;
+  /* 1 */
+  padding: 0;
+  /* 2 */ }
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  height: auto; }
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
+ */
+input[type="search"] {
+  -webkit-appearance: textfield;
+  /* 1 */
+  box-sizing: content-box;
+  /* 2 */ }
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none; }
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+fieldset {
+  border: 1px solid #c0c0c0;
+  margin: 0 2px;
+  padding: 0.35em 0.625em 0.75em; }
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+legend {
+  border: 0;
+  /* 1 */
+  padding: 0;
+  /* 2 */ }
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+textarea {
+  overflow: auto; }
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+optgroup {
+  font-weight: bold; }
+
+/* Tables
+   ========================================================================== 
*/
+/**
+ * Remove most spacing between table cells.
+ */
+table {
+  border-collapse: collapse;
+  border-spacing: 0; }
+
+td,
+th {
+  padding: 0; }
+
+/*csslint important:false*/
+/* ==========================================================================
+   Pure Base Extras
+   ========================================================================== 
*/
+/**
+ * Extra rules that Pure adds on top of Normalize.css
+ */
+/**
+ * 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 */
+  *letter-spacing: normal;
+  /* reset IE < 8 */
+  *word-spacing: -0.43em;
+  /* IE < 8: 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/yahoo/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: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-flex-flow: row wrap;
+  -ms-flex-flow: row wrap;
+  flex-flow: row wrap;
+  /* Prevents distributing space between rows */
+  -webkit-align-content: flex-start;
+  -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;
+  *display: inline;
+  /* IE < 8: fake inline-block */
+  zoom: 1;
+  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;
+  *display: inline;
+  zoom: 1;
+  letter-spacing: normal;
+  word-spacing: normal;
+  vertical-align: top;
+  text-rendering: auto; }
+
+.pure-u-1-24 {
+  width: 4.1667%;
+  *width: 4.1357%; }
+
+.pure-u-1-12,
+.pure-u-2-24 {
+  width: 8.3333%;
+  *width: 8.3023%; }
+
+.pure-u-1-8,
+.pure-u-3-24 {
+  width: 12.5000%;
+  *width: 12.4690%; }
+
+.pure-u-1-6,
+.pure-u-4-24 {
+  width: 16.6667%;
+  *width: 16.6357%; }
+
+.pure-u-1-5 {
+  width: 20%;
+  *width: 19.9690%; }
+
+.pure-u-5-24 {
+  width: 20.8333%;
+  *width: 20.8023%; }
+
+.pure-u-1-4,
+.pure-u-6-24 {
+  width: 25%;
+  *width: 24.9690%; }
+
+.pure-u-7-24 {
+  width: 29.1667%;
+  *width: 29.1357%; }
+
+.pure-u-1-3,
+.pure-u-8-24 {
+  width: 33.3333%;
+  *width: 33.3023%; }
+
+.pure-u-3-8,
+.pure-u-9-24 {
+  width: 37.5000%;
+  *width: 37.4690%; }
+
+.pure-u-2-5 {
+  width: 40%;
+  *width: 39.9690%; }
+
+.pure-u-5-12,
+.pure-u-10-24 {
+  width: 41.6667%;
+  *width: 41.6357%; }
+
+.pure-u-11-24 {
+  width: 45.8333%;
+  *width: 45.8023%; }
+
+.pure-u-1-2,
+.pure-u-12-24 {
+  width: 50%;
+  *width: 49.9690%; }
+
+.pure-u-13-24 {
+  width: 54.1667%;
+  *width: 54.1357%; }
+
+.pure-u-7-12,
+.pure-u-14-24 {
+  width: 58.3333%;
+  *width: 58.3023%; }
+
+.pure-u-3-5 {
+  width: 60%;
+  *width: 59.9690%; }
+
+.pure-u-5-8,
+.pure-u-15-24 {
+  width: 62.5000%;
+  *width: 62.4690%; }
+
+.pure-u-2-3,
+.pure-u-16-24 {
+  width: 66.6667%;
+  *width: 66.6357%; }
+
+.pure-u-17-24 {
+  width: 70.8333%;
+  *width: 70.8023%; }
+
+.pure-u-3-4,
+.pure-u-18-24 {
+  width: 75%;
+  *width: 74.9690%; }
+
+.pure-u-19-24 {
+  width: 79.1667%;
+  *width: 79.1357%; }
+
+.pure-u-4-5 {
+  width: 80%;
+  *width: 79.9690%; }
+
+.pure-u-5-6,
+.pure-u-20-24 {
+  width: 83.3333%;
+  *width: 83.3023%; }
+
+.pure-u-7-8,
+.pure-u-21-24 {
+  width: 87.5000%;
+  *width: 87.4690%; }
+
+.pure-u-11-12,
+.pure-u-22-24 {
+  width: 91.6667%;
+  *width: 91.6357%; }
+
+.pure-u-23-24 {
+  width: 95.8333%;
+  *width: 95.8023%; }
+
+.pure-u-1,
+.pure-u-1-1,
+.pure-u-5-5,
+.pure-u-24-24 {
+  width: 100%; }
+
+.pure-button {
+  /* Structure */
+  display: inline-block;
+  zoom: 1;
+  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;
+  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 */
+  *letter-spacing: normal;
+  /* reset IE < 8 */
+  *word-spacing: -0.43em;
+  /* IE < 8: 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: #444;
+  /* rgba not supported (IE 8) */
+  color: rgba(0, 0, 0, 0.8);
+  /* rgba supported */
+  border: 1px solid #999;
+  /*IE 6/7/8*/
+  border: none rgba(0, 0, 0, 0);
+  /*IE9 + everything else*/
+  background-color: #E6E6E6;
+  text-decoration: none;
+  border-radius: 2px; }
+
+.pure-button-hover,
+.pure-button:hover,
+.pure-button:focus {
+  /* csslint ignore:start */
+  filter: alpha(opacity=90);
+  /* csslint ignore:end */
+  background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 
40%, 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 {
+  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;
+  /* csslint ignore:start */
+  filter: alpha(opacity=40);
+  /* csslint ignore:end */
+  opacity: 0.40;
+  cursor: not-allowed;
+  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: #0078e7;
+  color: #fff; }
+
+/* Button Groups */
+.pure-button-group .pure-button {
+  margin: 0;
+  border-radius: 0;
+  border-right: 1px solid #111;
+  /* fallback color for rgba() for IE7/8 */
+  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;
+  box-shadow: inset 0 1px 3px #ddd;
+  border-radius: 4px;
+  vertical-align: middle;
+  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;
+  box-shadow: inset 0 1px 3px #ddd;
+  border-radius: 4px;
+  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; }
+
+.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-aligned .pure-help-inline,
+.pure-form-message-inline {
+  display: inline-block;
+  *display: inline;
+  *zoom: 1;
+  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 */
+/* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline 
instead. */
+.pure-form .pure-help-inline,
+.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; }
+
+  /* NOTE: pure-help-inline is deprecated. Use .pure-form-message-inline 
instead. */
+  .pure-form .pure-help-inline,
+  .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 {
+  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;
+  *display: inline;
+  zoom: 1;
+  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;
+  -ms-overflow-style: none;
+  -webkit-overflow-scrolling: touch;
+  /* a little extra padding for this style to allow for scrollbars */
+  padding: .5em 0; }
+
+.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar {
+  display: none; }
+
+/* misc default styling */
+.pure-menu-separator,
+.pure-menu-horizontal .pure-menu-children .pure-menu-separator {
+  background-color: #ccc;
+  height: 1px;
+  margin: .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-disabled,
+.pure-menu-heading {
+  padding: .5em 1em; }
+
+.pure-menu-disabled {
+  opacity: .5; }
+
+.pure-menu-disabled .pure-menu-link:hover {
+  background-color: transparent; }
+
+.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 */ }
+
+/* Consider removing this next declaration block, as it causes problems when
+there's a rowspan on the first cell. Case added to the tests. issue#432 */
+.pure-table td:first-child,
+.pure-table th:first-child {
+  border-left-width: 0; }
+
+.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; }
+
+/*# sourceMappingURL=pure.css.map */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]