From 073393af311d36c8ca7570ff0d3f0a3117c0b544 Mon Sep 17 00:00:00 2001
From: elijah <elijah@riseup.net>
Date: Fri, 16 Sep 2016 14:02:32 -0700
Subject: [pkg] rename www to ui

---
 .gitignore                                         |   6 +-
 Makefile                                           |   2 +-
 README.rst                                         |   4 +-
 src/leap/bitmask/core/web/README                   |   6 +-
 src/leap/bitmask/gui/README.rst                    |   2 +-
 ui/Makefile                                        |  47 ++++
 ui/README.md                                       | 112 ++++++++
 ui/app/app.js                                      |  33 +++
 ui/app/components/area.js                          |  65 +++++
 ui/app/components/center.js                        |  39 +++
 ui/app/components/debug_panel.js                   |  40 +++
 ui/app/components/error_panel.js                   |  21 ++
 ui/app/components/greeter_panel.js                 |  34 +++
 ui/app/components/list_edit.js                     | 122 ++++++++
 ui/app/components/login.js                         | 302 ++++++++++++++++++++
 ui/app/components/main_panel/account_list.js       | 113 ++++++++
 ui/app/components/main_panel/email_section.js      |  48 ++++
 ui/app/components/main_panel/index.js              |  80 ++++++
 ui/app/components/main_panel/main_panel.less       | 212 ++++++++++++++
 ui/app/components/main_panel/section_layout.js     |  59 ++++
 ui/app/components/main_panel/user_section.js       |  71 +++++
 ui/app/components/main_panel/vpn_section.js        |   0
 ui/app/components/panel_switcher.js                |  58 ++++
 ui/app/components/spinner/index.js                 |  15 +
 ui/app/components/spinner/spinner.css              |  42 +++
 ui/app/components/splash.js                        | 132 +++++++++
 ui/app/components/wizard/add_provider_modal.js     |  94 +++++++
 ui/app/components/wizard/index.js                  |  38 +++
 ui/app/components/wizard/provider_select_stage.js  |  86 ++++++
 ui/app/components/wizard/stage_layout.js           |  37 +++
 ui/app/components/wizard/wizard.less               |  44 +++
 ui/app/css/bootstrap.less                          |   5 +
 ui/app/css/common.css                              |  80 ++++++
 ui/app/img/arrow-down.svg                          |  65 +++++
 ui/app/img/arrow-up.svg                            |  65 +++++
 ui/app/img/cloud.svg                               |  65 +++++
 ui/app/img/disabled.svg                            |  72 +++++
 ui/app/img/envelope.svg                            |  64 +++++
 ui/app/img/gear.svg                                |  61 ++++
 ui/app/img/mask.svg                                | 133 +++++++++
 ui/app/img/off.svg                                 |  88 ++++++
 ui/app/img/on.svg                                  |  78 ++++++
 ui/app/img/planet.svg                              |  60 ++++
 ui/app/img/unknown.svg                             |  82 ++++++
 ui/app/img/user.svg                                |  65 +++++
 ui/app/img/wait.svg                                |  75 +++++
 ui/app/index.html                                  |  13 +
 ui/app/lib/bitmask.js                              | 306 +++++++++++++++++++++
 ui/app/lib/color.js                                |  65 +++++
 ui/app/lib/colors.js                               | 289 +++++++++++++++++++
 ui/app/lib/common.js                               |   7 +
 ui/app/lib/validate.js                             |  82 ++++++
 ui/app/main.js                                     |  26 ++
 ui/app/models/account.js                           | 143 ++++++++++
 ui/app/models/dummy_account.js                     |  33 +++
 ui/package.json                                    |  34 +++
 ui/pydist/README.md                                |   4 +
 ui/pydist/setup.py                                 |  56 ++++
 ui/webpack.config.js                               |  84 ++++++
 www/Makefile                                       |  47 ----
 www/README.md                                      | 112 --------
 www/app/app.js                                     |  33 ---
 www/app/components/area.js                         |  65 -----
 www/app/components/center.js                       |  39 ---
 www/app/components/debug_panel.js                  |  40 ---
 www/app/components/error_panel.js                  |  21 --
 www/app/components/greeter_panel.js                |  34 ---
 www/app/components/list_edit.js                    | 122 --------
 www/app/components/login.js                        | 302 --------------------
 www/app/components/main_panel/account_list.js      | 113 --------
 www/app/components/main_panel/email_section.js     |  48 ----
 www/app/components/main_panel/index.js             |  80 ------
 www/app/components/main_panel/main_panel.less      | 212 --------------
 www/app/components/main_panel/section_layout.js    |  59 ----
 www/app/components/main_panel/user_section.js      |  71 -----
 www/app/components/main_panel/vpn_section.js       |   0
 www/app/components/panel_switcher.js               |  58 ----
 www/app/components/spinner/index.js                |  15 -
 www/app/components/spinner/spinner.css             |  42 ---
 www/app/components/splash.js                       | 132 ---------
 www/app/components/wizard/add_provider_modal.js    |  94 -------
 www/app/components/wizard/index.js                 |  38 ---
 www/app/components/wizard/provider_select_stage.js |  86 ------
 www/app/components/wizard/stage_layout.js          |  37 ---
 www/app/components/wizard/wizard.less              |  44 ---
 www/app/css/bootstrap.less                         |   5 -
 www/app/css/common.css                             |  80 ------
 www/app/img/arrow-down.svg                         |  65 -----
 www/app/img/arrow-up.svg                           |  65 -----
 www/app/img/cloud.svg                              |  65 -----
 www/app/img/disabled.svg                           |  72 -----
 www/app/img/envelope.svg                           |  64 -----
 www/app/img/gear.svg                               |  61 ----
 www/app/img/mask.svg                               | 133 ---------
 www/app/img/off.svg                                |  88 ------
 www/app/img/on.svg                                 |  78 ------
 www/app/img/planet.svg                             |  60 ----
 www/app/img/unknown.svg                            |  82 ------
 www/app/img/user.svg                               |  65 -----
 www/app/img/wait.svg                               |  75 -----
 www/app/index.html                                 |  13 -
 www/app/lib/bitmask.js                             | 306 ---------------------
 www/app/lib/color.js                               |  65 -----
 www/app/lib/colors.js                              | 289 -------------------
 www/app/lib/common.js                              |   7 -
 www/app/lib/validate.js                            |  82 ------
 www/app/main.js                                    |  26 --
 www/app/models/account.js                          | 143 ----------
 www/app/models/dummy_account.js                    |  33 ---
 www/package.json                                   |  34 ---
 www/pydist/README.md                               |   4 -
 www/pydist/setup.py                                |  56 ----
 www/webpack.config.js                              |  84 ------
 113 files changed, 4154 insertions(+), 4154 deletions(-)
 create mode 100644 ui/Makefile
 create mode 100644 ui/README.md
 create mode 100644 ui/app/app.js
 create mode 100644 ui/app/components/area.js
 create mode 100644 ui/app/components/center.js
 create mode 100644 ui/app/components/debug_panel.js
 create mode 100644 ui/app/components/error_panel.js
 create mode 100644 ui/app/components/greeter_panel.js
 create mode 100644 ui/app/components/list_edit.js
 create mode 100644 ui/app/components/login.js
 create mode 100644 ui/app/components/main_panel/account_list.js
 create mode 100644 ui/app/components/main_panel/email_section.js
 create mode 100644 ui/app/components/main_panel/index.js
 create mode 100644 ui/app/components/main_panel/main_panel.less
 create mode 100644 ui/app/components/main_panel/section_layout.js
 create mode 100644 ui/app/components/main_panel/user_section.js
 create mode 100644 ui/app/components/main_panel/vpn_section.js
 create mode 100644 ui/app/components/panel_switcher.js
 create mode 100644 ui/app/components/spinner/index.js
 create mode 100644 ui/app/components/spinner/spinner.css
 create mode 100644 ui/app/components/splash.js
 create mode 100644 ui/app/components/wizard/add_provider_modal.js
 create mode 100644 ui/app/components/wizard/index.js
 create mode 100644 ui/app/components/wizard/provider_select_stage.js
 create mode 100644 ui/app/components/wizard/stage_layout.js
 create mode 100644 ui/app/components/wizard/wizard.less
 create mode 100644 ui/app/css/bootstrap.less
 create mode 100644 ui/app/css/common.css
 create mode 100644 ui/app/img/arrow-down.svg
 create mode 100644 ui/app/img/arrow-up.svg
 create mode 100644 ui/app/img/cloud.svg
 create mode 100644 ui/app/img/disabled.svg
 create mode 100644 ui/app/img/envelope.svg
 create mode 100644 ui/app/img/gear.svg
 create mode 100644 ui/app/img/mask.svg
 create mode 100644 ui/app/img/off.svg
 create mode 100644 ui/app/img/on.svg
 create mode 100644 ui/app/img/planet.svg
 create mode 100644 ui/app/img/unknown.svg
 create mode 100644 ui/app/img/user.svg
 create mode 100644 ui/app/img/wait.svg
 create mode 100644 ui/app/index.html
 create mode 100644 ui/app/lib/bitmask.js
 create mode 100644 ui/app/lib/color.js
 create mode 100644 ui/app/lib/colors.js
 create mode 100644 ui/app/lib/common.js
 create mode 100644 ui/app/lib/validate.js
 create mode 100644 ui/app/main.js
 create mode 100644 ui/app/models/account.js
 create mode 100644 ui/app/models/dummy_account.js
 create mode 100644 ui/package.json
 create mode 100644 ui/pydist/README.md
 create mode 100644 ui/pydist/setup.py
 create mode 100644 ui/webpack.config.js
 delete mode 100644 www/Makefile
 delete mode 100644 www/README.md
 delete mode 100644 www/app/app.js
 delete mode 100644 www/app/components/area.js
 delete mode 100644 www/app/components/center.js
 delete mode 100644 www/app/components/debug_panel.js
 delete mode 100644 www/app/components/error_panel.js
 delete mode 100644 www/app/components/greeter_panel.js
 delete mode 100644 www/app/components/list_edit.js
 delete mode 100644 www/app/components/login.js
 delete mode 100644 www/app/components/main_panel/account_list.js
 delete mode 100644 www/app/components/main_panel/email_section.js
 delete mode 100644 www/app/components/main_panel/index.js
 delete mode 100644 www/app/components/main_panel/main_panel.less
 delete mode 100644 www/app/components/main_panel/section_layout.js
 delete mode 100644 www/app/components/main_panel/user_section.js
 delete mode 100644 www/app/components/main_panel/vpn_section.js
 delete mode 100644 www/app/components/panel_switcher.js
 delete mode 100644 www/app/components/spinner/index.js
 delete mode 100644 www/app/components/spinner/spinner.css
 delete mode 100644 www/app/components/splash.js
 delete mode 100644 www/app/components/wizard/add_provider_modal.js
 delete mode 100644 www/app/components/wizard/index.js
 delete mode 100644 www/app/components/wizard/provider_select_stage.js
 delete mode 100644 www/app/components/wizard/stage_layout.js
 delete mode 100644 www/app/components/wizard/wizard.less
 delete mode 100644 www/app/css/bootstrap.less
 delete mode 100644 www/app/css/common.css
 delete mode 100644 www/app/img/arrow-down.svg
 delete mode 100644 www/app/img/arrow-up.svg
 delete mode 100644 www/app/img/cloud.svg
 delete mode 100644 www/app/img/disabled.svg
 delete mode 100644 www/app/img/envelope.svg
 delete mode 100644 www/app/img/gear.svg
 delete mode 100644 www/app/img/mask.svg
 delete mode 100644 www/app/img/off.svg
 delete mode 100644 www/app/img/on.svg
 delete mode 100644 www/app/img/planet.svg
 delete mode 100644 www/app/img/unknown.svg
 delete mode 100644 www/app/img/user.svg
 delete mode 100644 www/app/img/wait.svg
 delete mode 100644 www/app/index.html
 delete mode 100644 www/app/lib/bitmask.js
 delete mode 100644 www/app/lib/color.js
 delete mode 100644 www/app/lib/colors.js
 delete mode 100644 www/app/lib/common.js
 delete mode 100644 www/app/lib/validate.js
 delete mode 100644 www/app/main.js
 delete mode 100644 www/app/models/account.js
 delete mode 100644 www/app/models/dummy_account.js
 delete mode 100644 www/package.json
 delete mode 100644 www/pydist/README.md
 delete mode 100644 www/pydist/setup.py
 delete mode 100644 www/webpack.config.js

diff --git a/.gitignore b/.gitignore
index 31d862b4..0fa20c72 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,9 +35,9 @@ venv/
 ENV/
 
 # Javascript, web-ui
-www/node_modules
-www/npm-debug.log
-www/pydist/bitmask_js
+ui/node_modules
+ui/npm-debug.log
+ui/pydist/bitmask_js
 
 # vim
 *.swp
diff --git a/Makefile b/Makefile
index 46a6ca80..49472bb5 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ clean:
 
 dev-mail:
 	pip install -e '.[mail]'
-	make -C www dev-install-prebuilt
+	make -C ui dev-install-prebuilt
 
 dev-all:
 	pip install -e '.[all]'
diff --git a/README.rst b/README.rst
index 6f2f98d6..d8f6a3b9 100644
--- a/README.rst
+++ b/README.rst
@@ -111,11 +111,11 @@ First, install the javascript prerequisites:
 Next, run ``dev-install``:
 
   source venv/bin/activate    # if not already activated
-  cd www
+  cd ui
   make dev-install            # install JS user interface as a python package in "develop" mode.
   node run watch              # continually rebuild javascript bundle when source files change.
 
-For more information, see ``www/README.md``.
+For more information, see ``ui/README.md``.
 
 cross-testing
 +++++++++++++++++++++++++++++++++++++++
diff --git a/src/leap/bitmask/core/web/README b/src/leap/bitmask/core/web/README
index 76a745df..5826c527 100644
--- a/src/leap/bitmask/core/web/README
+++ b/src/leap/bitmask/core/web/README
@@ -1,9 +1,9 @@
 This is the original implementation of the bitmask.js library, which uses the
 REST api exposed by the HTTPRequestDispatcher.
 
-The development of bitmask_js is in the www/ folder in this bitmask-dev repo.
+The development of bitmask_js is in the ui/ folder in this bitmask-dev repo.
 
-A pre-compiled version of the html+js web-ui can be found in the leap.bitmask_www package.
+A pre-compiled version of the html+js web-ui can be found in the bitmask_js package.
 
 This remains here to be able to develop against the REST api without the need
-of installing the full-fledged bitmask_www package.
+of installing the full-fledged bitmask_js package.
diff --git a/src/leap/bitmask/gui/README.rst b/src/leap/bitmask/gui/README.rst
index 431bcfc8..c3a6a0f2 100644
--- a/src/leap/bitmask/gui/README.rst
+++ b/src/leap/bitmask/gui/README.rst
@@ -1,5 +1,5 @@
 bitmask.gui is a module that depends on PyQt.
 It is declared as an extra in bitmask setup.py
 
-Its function is to launch a minimalistic browser that serves the bitmask_www
+Its function is to launch a minimalistic browser that serves the bitmask_js
 interface.
diff --git a/ui/Makefile b/ui/Makefile
new file mode 100644
index 00000000..d67a02b3
--- /dev/null
+++ b/ui/Makefile
@@ -0,0 +1,47 @@
+#
+# builds for development mode
+#
+
+dev-build: build-clean
+	npm install
+	npm run build
+	touch pydist/bitmask_js/__init__.py
+
+dev-install: dev-build
+	pip install -e pydist
+
+#
+# installs python package, but does not rebuild the js.
+# for usage when you don't want to install nodejs
+#
+dev-install-prebuilt:
+	pip install -e pydist
+
+
+#
+# distribution builds
+#
+
+dist-build: build-clean
+	npm install
+	npm run build:production
+	touch pydist/bitmask_js/__init__.py
+	cd pydist && python setup.py bdist_wheel
+
+dist-install: dist-build
+	pip install pydist/dist/*.whl
+
+#
+# cleaning up
+#
+
+build-clean:
+	rm -rf pydist/bitmask_js
+	rm -rf pydist/dist
+	rm -rf pydist/build
+
+clean: build-clean
+	rm -rf node_modules
+
+uninstall:
+	pip uninstall bitmask_js
diff --git a/ui/README.md b/ui/README.md
new file mode 100644
index 00000000..3f276c00
--- /dev/null
+++ b/ui/README.md
@@ -0,0 +1,112 @@
+Bitmask Javascript UI
+=================================================================
+
+Here lies the user interface for Bitmask, written in Javascript.
+
+quick start:
+
+    sudo apt install nodejs npm nodejs-legacy
+    npm install       # installs development dependencies in "node_modules"
+    npm run watch     # continually rebuilds source .js into "pydist"
+
+build for deployment:
+
+    npm install
+    npm run build:production
+
+After 'build', 'build:production', or 'watch' is run, everything needed for
+Bitmask JS is contained in the 'pydist' directory. No additional files are
+needed. Open the pydist/bitmask_js/public/index.html file in a browser or web
+widget.
+
+However! Because of the single origin policy of browsers, you will need to
+open public/index.html through the webserver included with bitmaskd (e.g.
+http://localhost:7070)
+
+In order for this JS app to be loaded by bitmask, it must be packaged as a
+python package and installed in the virtualenv:
+
+    source path-to-virtualenv/bin/activate
+    make dev-install  # builds and installs JS app as python package
+    pkill bitmaskd    # make sure bitmaskd is not already running
+    bitmaskd          # launch backend
+    npm run open      # opens http://localhost:7070/ in a browser
+    npm run watch     # rebuild JS whenever source file is changed.
+
+In order to package for distribution:
+
+    make dist-build
+
+NOTE: If you make changes to the asset files, like add or modify an image, you
+      will need to stop then rerun `npm run watch` for the changes to take
+      effect.
+
+Development Dependencies
+-----------------------------------------------------------------
+
+This application has no "runtime" dependencies: all the javascript needed is
+bundled and included. However, there are many development dependencies.
+Run `npm ls` for a full list.
+
+**npm**
+
+Package management, controlled by the package.json file.
+
+**webpack**
+
+Asset bundling and transformation. It takes all your javascript and CSS and
+bundles it into one (or more) files, handling support for 'require' and scope
+separation.
+
+loaders & plugins:
+
+* babel-loader: use Babel with Webpack.
+
+* style-loader/css-loader: standard css loader.
+
+* less-loader: allows use the less stylesheet.
+
+* copy-webpack-plugin: allows us to specify what files to copy using Webpack.
+
+* extract-text-webpack-plugin: allows you to split js and css into separate
+  files.
+
+**babel**
+
+Babel is used to compile javascript into javascript, transforming along the
+way. We have enabled these plugins:
+
+* babel-presets-react: Adds support for React features, such as JSX (html
+  inlined in js files).
+
+* babel-presets-es2015: Allows the use of modern ES6 javascript.
+
+* babel-presets-stage-0: Allows the use of some ES7 proposals, even though
+  these are not standardized yet. Makes classes nicer.
+
+* babel-polyfill: This is not part of the babel transpiling, but is distributed by babel. This polyfill will give you a full ES2015 environment even if the browser is missing some javascript features. We include this in the 'entry' option of the webpack config. https://babeljs.io/docs/usage/polyfill/
+
+**react**
+
+React is an efficient way to generate HTML views with Javascript. It allows you
+to create interactive UIs without ever modifying the DOM by using "one way"
+data binding. This greatly simplifies the code and reduces errors.
+
+**bootstrap**
+
+The world's most popular CSS styles for UI elements. The npm package includes
+both pre-compiled css and less stylesheets. Even though Semantic UI is better,
+Bootstrap components for React are much more stable, I have found, and also are
+easy to theme.
+
+To integrate Bootstrap with React:
+
+* react-bootstrap: React components that use Bootstrap styles. These component
+  include all the needed javascript and don't require JQuery, although they do
+  require that the bootstrap CSS is loaded independently.
+
+**zxcvbn**
+
+A password strength checker that doesn't suck, but which is big. This JS is
+only loaded when we think we are about to need it.
+
diff --git a/ui/app/app.js b/ui/app/app.js
new file mode 100644
index 00000000..57120a4e
--- /dev/null
+++ b/ui/app/app.js
@@ -0,0 +1,33 @@
+import bitmask from 'lib/bitmask'
+import Account from 'models/account'
+
+class Application {
+  constructor() {
+  }
+
+  //
+  // main entry point for the application
+  //
+  start() {
+    Account.active().then(account => {
+      if (account == null) {
+        this.show('greeter', {onLogin: this.onLogin.bind(this)})
+      } else {
+        this.show('main', {initialAccount: account})
+      }
+    }, error => {
+      this.show('error', {error: error})
+    })
+  }
+
+  onLogin(account) {
+    this.show('main', {initialAccount: account})
+  }
+
+  show(panel, properties) {
+    this.switcher.show(panel, properties)
+  }
+}
+
+var App = new Application
+export default App
\ No newline at end of file
diff --git a/ui/app/components/area.js b/ui/app/components/area.js
new file mode 100644
index 00000000..e903e5f5
--- /dev/null
+++ b/ui/app/components/area.js
@@ -0,0 +1,65 @@
+//
+// A bootstrap panel, but with some extra options
+//
+
+import React from 'react'
+// import {Panel} from 'react-bootstrap'
+
+class Area extends React.Component {
+
+  static get defaultProps() {return{
+     position: null,  // top or bottom
+     size: 'small',   // small or big
+     type: null,      // light or dark
+     className: null
+  }}
+
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    let style = {}
+    let innerstyle = {}
+    if (this.props.position == 'top') {
+      style.borderBottomRightRadius = '0px'
+      style.borderBottomLeftRadius = '0px'
+      style.marginBottom = '0px'
+      style.borderBottom = '0px'
+      if (this.props.size == 'big') {
+        innerstyle.padding = '25px'
+      }
+    } else if (this.props.position == 'bottom') {
+      style.borderTopRightRadius = '0px'
+      style.borderTopLeftRadius = '0px'
+      style.borderTop = '0px'
+      if (this.props.size == 'big') {
+        innerstyle.padding = '15px 25px'
+      }
+    }
+
+    let type = this.props.type ? "area-" + this.props.type : ""
+    let className = ['panel', 'panel-default', type, this.props.className].join(' ')
+    return(
+      <div className={className} style={style}>
+        <div className="panel-body" style={innerstyle}>
+          {this.props.children}
+        </div>
+      </div>
+    )
+  }
+
+}
+
+// Area.propTypes = {
+//   children: React.PropTypes.oneOfType([
+//     React.PropTypes.element,
+//     React.PropTypes.arrayOf(React.PropTypes.element)
+//   ])
+// }
+
+//Area.propTypes = {
+//  children: React.PropTypes.element.isRequired
+//}
+
+export default Area
diff --git a/ui/app/components/center.js b/ui/app/components/center.js
new file mode 100644
index 00000000..6fa62128
--- /dev/null
+++ b/ui/app/components/center.js
@@ -0,0 +1,39 @@
+//
+// puts a block right in the center of the window
+//
+
+import React from 'react'
+
+class Center extends React.Component {
+
+  static get defaultProps() {return{
+    width: null
+  }}
+
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    let style = null
+    if (this.props.width) {
+      style = {width: this.props.width + 'px'}
+    }
+    return (
+      <div className="center-container">
+        <div className="center-item" style={style}>
+          {this.props.children}
+        </div>
+      </div>
+    )
+  }
+}
+
+Center.propTypes = {
+  children: React.PropTypes.oneOfType([
+    React.PropTypes.element,
+    React.PropTypes.arrayOf(React.PropTypes.element)
+  ])
+}
+
+export default Center
diff --git a/ui/app/components/debug_panel.js b/ui/app/components/debug_panel.js
new file mode 100644
index 00000000..7515ba84
--- /dev/null
+++ b/ui/app/components/debug_panel.js
@@ -0,0 +1,40 @@
+import React from 'react'
+import App from '../app'
+
+
+class DebugPanel extends React.Component {
+
+  constructor(props) {
+    super(props)
+    this.click = this.click.bind(this)
+  }
+
+  componentDidMount() {
+    this.click(window.location.hash.replace('#', ''))
+  }
+
+  click(panel_name) {
+    window.location.hash = panel_name
+    App.show(panel_name)
+  }
+
+  panel(panel_name) {
+    return elem(
+      'a',
+      { onClick: () => this.click(panel_name), key: panel_name },
+      panel_name
+    )
+  }
+
+  render() {
+    return elem('div', {className: 'debug-panel'},
+      this.panel('splash'),
+      this.panel('greeter'),
+      this.panel('wizard'),
+      this.panel('main')
+    )
+  }
+
+}
+
+export default DebugPanel
\ No newline at end of file
diff --git a/ui/app/components/error_panel.js b/ui/app/components/error_panel.js
new file mode 100644
index 00000000..fc88d459
--- /dev/null
+++ b/ui/app/components/error_panel.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import Center from './center'
+import Area from './area'
+
+export default class ErrorPanel extends React.Component {
+
+  constructor(props) {
+    super(props)
+  }
+
+  render () {
+    return (
+      <Center width="400">
+        <Area>
+          <h1>Error</h1>
+          {this.props.error}
+        </Area>
+      </Center>
+    )
+  }
+}
diff --git a/ui/app/components/greeter_panel.js b/ui/app/components/greeter_panel.js
new file mode 100644
index 00000000..4552db18
--- /dev/null
+++ b/ui/app/components/greeter_panel.js
@@ -0,0 +1,34 @@
+import React from 'react'
+import Login from './login'
+import Center from './center'
+import Splash from './splash'
+import Area from './area'
+import { Glyphicon } from 'react-bootstrap'
+import App from 'app'
+
+export default class GreeterPanel extends React.Component {
+
+  constructor(props) {
+    super(props)
+  }
+
+  newAccount() {
+    App.show('wizard')
+  }
+
+  render () {
+    return <div>
+      <Splash speed="slow" mask={false} />
+      <Center width="400">
+        <Area position="top" type="light" className="greeter">
+          <Login {...this.props} rememberAllowed={false}/>
+        </Area>
+        <Area position="bottom" type="dark" className="greeter">
+          <Glyphicon glyph="user" />
+          &nbsp;
+          <a href="#" onClick={this.newAccount.bind(this)}>Create a new account...</a>
+        </Area>
+      </Center>
+    </div>
+  }
+}
diff --git a/ui/app/components/list_edit.js b/ui/app/components/list_edit.js
new file mode 100644
index 00000000..0d557d22
--- /dev/null
+++ b/ui/app/components/list_edit.js
@@ -0,0 +1,122 @@
+//
+// A simple list of items, with minus and plus buttons to add and remove
+// items.
+//
+
+import React from 'react'
+import {Button, ButtonGroup, ButtonToolbar, Glyphicon, FormControl} from 'react-bootstrap'
+
+const CONTAINER_CSS = {
+  display: "flex",
+  flexDirection: "column"
+}
+const SELECT_CSS = {
+  padding: "0px",
+  flex: "1 1 1000px",
+  overflowY: "scroll"
+}
+const OPTION_CSS = {
+  padding: "10px"
+}
+const TOOLBAR_CSS = {
+  paddingTop: "10px",
+  flex: "0 0 auto"
+}
+
+class ListEdit extends React.Component {
+
+  static get defaultProps() {return{
+    width: null,
+    items: [
+      'aaaaaaa',
+      'bbbbbbb',
+      'ccccccc'
+    ],
+    selected: null,
+    onRemove: null,
+    onAdd: null,
+  }}
+
+  constructor(props) {
+    super(props)
+    let index = 0
+    if (props.selected) {
+      index = props.items.indexOf(props.selected)
+    }
+    this.state = {
+      selected: index
+    }
+    this.click  = this.click.bind(this)
+    this.add    = this.add.bind(this)
+    this.remove = this.remove.bind(this)
+  }
+
+  setSelected(index) {
+    this.setState({
+      selected: index
+    })
+  }
+
+  click(e) {
+    let row = parseInt(e.target.value)
+    if (row >= 0) {
+      this.setState({selected: row})
+    }
+  }
+
+  add() {
+    if (this.props.onAdd) {
+      this.props.onAdd()
+    }
+  }
+
+  remove() {
+    if (this.state.selected >= 0 && this.props.onRemove) {
+      if (this.props.items.length == this.state.selected + 1) {
+        // if we remove the last item, set the selected item
+        // to the one right before it.
+        this.setState({selected: (this.state.selected - 1)})
+      }
+      this.props.onRemove(this.props.items[this.state.selected])
+    }
+  }
+
+  render() {
+    let options = null
+    if (this.props.items) {
+      options = this.props.items.map((item, i) => {
+        return <option style={OPTION_CSS} key={i} value={i}>{item}</option>
+      }, this)
+    }
+    return(
+      <div style={CONTAINER_CSS}>
+        <FormControl
+          value={this.state.selected}
+          style={SELECT_CSS} className="select-list"
+          componentClass="select" size="5" onChange={this.click}>
+          {options}
+        </FormControl>
+        <ButtonToolbar className="pull-right" style={TOOLBAR_CSS}>
+          <ButtonGroup>
+            <Button onClick={this.add}>
+              <Glyphicon glyph="plus" />
+            </Button>
+            <Button disabled={this.state.selected < 0} onClick={this.remove}>
+              <Glyphicon glyph="minus" />
+            </Button>
+          </ButtonGroup>
+        </ButtonToolbar>
+      </div>
+    )
+  }
+
+}
+
+ListEdit.propTypes = {
+  children: React.PropTypes.oneOfType([
+    React.PropTypes.element,
+    React.PropTypes.arrayOf(React.PropTypes.element)
+  ])
+}
+
+export default ListEdit
diff --git a/ui/app/components/login.js b/ui/app/components/login.js
new file mode 100644
index 00000000..fe4ef5b2
--- /dev/null
+++ b/ui/app/components/login.js
@@ -0,0 +1,302 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import { FormGroup, ControlLabel, FormControl, HelpBlock, Button,
+  Checkbox, Glyphicon, Overlay, Tooltip, Alert } from 'react-bootstrap'
+import Spinner from './spinner'
+
+import Validate from 'lib/validate'
+import App from 'app'
+import Account from 'models/account'
+
+class Login extends React.Component {
+
+  static get defaultProps() {return{
+    rememberAllowed: false,   // if set, show remember password checkbox
+    domain: null,             // if set, only allow this domain
+    onLogin: null
+  }}
+
+  constructor(props) {
+    super(props)
+
+    // validation states can be null, 'success', 'warning', or 'error'
+
+    this.state = {
+      loading: false,
+
+      authError: false,     // authentication error message
+
+      username: "etest1@riseup.net",
+      usernameState: null,  // username validation state
+      usernameError: false, // username help message
+
+      password: "whatever",
+      passwordState: null,  // password validation state
+      passwordError: false, // password help message
+
+      disabled: false,
+      remember: false       // remember is checked?
+    }
+
+    // prebind:
+    this.onUsernameChange = this.onUsernameChange.bind(this)
+    this.onUsernameBlur   = this.onUsernameBlur.bind(this)
+    this.onPassword = this.onPassword.bind(this)
+    this.onSubmit   = this.onSubmit.bind(this)
+    this.onRemember = this.onRemember.bind(this)
+  }
+
+  componentDidMount() {
+    Validate.loadPasswdLib()
+  }
+
+  render () {
+    let rememberCheck = ""
+    let submitButton  = ""
+    let usernameHelp  = null
+    let passwordHelp  = null
+    let message = null
+
+    if (this.props.rememberAllowed) {
+      let props = {
+        style: {marginTop: "0px"},
+        onChange: this.onRemember
+      }
+
+      if (this.state.remember) {
+        rememberCheck = <Checkbox {...props} checked>
+          Remember username and password
+        </Checkbox>
+      } else {
+        rememberCheck = <Checkbox {...props}>
+          Remember username and password
+        </Checkbox>
+      }
+    }
+
+    if (this.state.authError) {
+      // style may be: success, warning, danger, info
+      message = (
+        <Alert bsStyle="danger">{this.state.authError}</Alert>
+      )
+    }
+
+    if (this.state.usernameError) {
+      usernameHelp = <HelpBlock>{this.state.usernameError}</HelpBlock>
+      // let props = {shouldUpdatePosition: true, show:true, placement:"right",
+      //              target:this.refs.username}
+      // usernameHelp = (
+      //   <Overlay {...props}>
+      //     <Tooltip id="username-tooltip">{this.state.usernameError}</Tooltip>
+      //   </Overlay>
+      // )
+    } else {
+      //usernameHelp = <HelpBlock>&nbsp;</HelpBlock>
+    }
+
+    if (this.state.passwordError) {
+      passwordHelp = <HelpBlock>{this.state.passwordError}</HelpBlock>
+      // let props = {shouldUpdatePosition: true, show:true, placement:"right",
+      //              target:this.refs.password, component: {this}}
+      // passwordHelp = (
+      //   <Overlay {...props}>
+      //     <Tooltip id="password-tooltip">{this.state.passwordError}</Tooltip>
+      //   </Overlay>
+      // )
+    } else {
+      //passwordHelp = <HelpBlock>&nbsp;</HelpBlock>
+    }
+
+    let buttonProps = {
+      type: "button",
+      onClick: this.onSubmit,
+      disabled: !this.maySubmit()
+    }
+    if (this.state.loading) {
+       submitButton = <Button block {...buttonProps}><Spinner /></Button>
+    } else {
+       submitButton = <Button block {...buttonProps}>Log In</Button>
+    }
+
+    let usernameref = null
+    if (this.props.domain) {
+      usernameref = function(c) {
+        if (c != null) {
+          let textarea = ReactDOM.findDOMNode(c)
+          let start = textarea.value.indexOf('@')
+          if (textarea.selectionStart > start) {
+            textarea.setSelectionRange(start, start)
+          }
+        }
+      }
+    }
+
+    let form = <form onSubmit={this.onSubmit}>
+      {message}
+      <FormGroup style={{marginBottom: '10px' }} controlId="loginUsername" validationState={this.state.usernameState}>
+        <ControlLabel>Username</ControlLabel>
+        <FormControl
+          componentClass="textarea"
+          style={{resize: "none"}}
+          rows="1"
+          ref={usernameref}
+          autoFocus
+          value={this.state.username}
+          onChange={this.onUsernameChange}
+          onBlur={this.onUsernameBlur} />
+        {this.state.usernameState == 'success' ? null : <FormControl.Feedback/>}
+        {usernameHelp}
+      </FormGroup>
+
+      <FormGroup controlId="loginPassword" validationState={this.state.passwordState}>
+        <ControlLabel>Password</ControlLabel>
+        <FormControl
+          type="password"
+          ref="password"
+          value={this.state.password}
+          onChange={this.onPassword} />
+        {this.state.passwordState == 'success' ? null : <FormControl.Feedback/>}
+        {passwordHelp}
+      </FormGroup>
+
+      {submitButton}
+      {rememberCheck}
+    </form>
+
+    return form
+  }
+
+  //
+  // Here we do a partial validation, because the user has not stopped typing.
+  //
+  onUsernameChange(e) {
+    let username = e.target.value.toLowerCase().replace("\n", "")
+    if (this.props.domain) {
+      let [userpart, domainpart] = username.split(
+        new RegExp('@|' + this.props.domain.replace(".", "\\.") + '$')
+      )
+      username = [userpart, this.props.domain].join('@')
+    }
+    let error = Validate.usernameInteractive(username, this.props.domain)
+    let state = null
+    if (error) {
+      state = 'error'
+    } else {
+      if (username && username.length > 0) {
+        let finalError = Validate.username(username)
+        state = finalError ? null : 'success'
+      }
+    }
+    this.setState({
+      username: username,
+      usernameState: state,
+      usernameError: error ? error : null
+    })
+  }
+
+  //
+  // Here we do a more complete validation, since the user have left the field.
+  //
+  onUsernameBlur(e) {
+    let username = e.target.value.toLowerCase()
+    this.setState({
+      username: username
+    })
+    if (username.length > 0) {
+      this.validateUsername(username)
+    } else {
+      this.setState({
+        usernameState: null,
+        usernameError: null
+      })
+    }
+  }
+
+  onPassword(e) {
+    let password = e.target.value
+    this.setState({password: password})
+    if (password.length > 0) {
+      this.validatePassword(password)
+    } else {
+      this.setState({
+        passwordState: null,
+        passwordError: null
+      })
+    }
+  }
+
+  onRemember(e) {
+    let currentValue = e.target.value == 'on' ? true : false
+    let value = !currentValue
+    this.setState({remember: value})
+  }
+
+  validateUsername(username) {
+    let error = Validate.username(username, this.props.domain)
+    this.setState({
+      usernameState: error ? 'error' : 'success',
+      usernameError: error ? error : null
+    })
+  }
+
+  validatePassword(password) {
+    let state = null
+    let message = null
+    let result = Validate.passwordStrength(password)
+    if (result) {
+      message = "Time to crack: " + result.crack_times_display.offline_slow_hashing_1e4_per_second
+      if (result.score == 0) {
+        state = 'error'
+      } else if (result.score == 1 || result.score == 2) {
+        state = 'warning'
+      } else {
+        state = 'success'
+      }
+    }
+    this.setState({
+      passwordState: state,
+      passwordError: message
+    })
+  }
+
+  maySubmit() {
+    return(
+      !this.stateLoading &&
+      !this.state.usernameError &&
+      this.state.username != "" &&
+      this.state.password != ""
+    )
+  }
+
+  onSubmit(e) {
+    e.preventDefault() // don't reload the page please!
+    if (!this.maySubmit()) { return }
+    this.setState({loading: true})
+
+    let account = Account.find(this.state.username)
+    account.login(this.state.password).then(
+      account => {
+        this.setState({loading: false})
+        if (this.props.onLogin) {
+          this.props.onLogin(account)
+        }
+      },
+      error => {
+        console.log(error)
+        if (error == "") {
+          error = 'Something failed, but we did not get a message'
+        }
+        this.setState({
+          loading: false,
+          usernameState: 'error',
+          passwordState: 'error',
+          authError: error
+        })
+      }
+    )
+  }
+
+}
+
+export default Login
\ No newline at end of file
diff --git a/ui/app/components/main_panel/account_list.js b/ui/app/components/main_panel/account_list.js
new file mode 100644
index 00000000..d0ef092f
--- /dev/null
+++ b/ui/app/components/main_panel/account_list.js
@@ -0,0 +1,113 @@
+import React from 'react'
+import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap'
+
+import App from 'app'
+import Account from 'models/account'
+
+export default class AccountList extends React.Component {
+
+  static get defaultProps() {return{
+    account: null,
+    accounts: [],
+    onAdd: null,
+    onRemove: null,
+    onSelect: null
+  }}
+
+  constructor(props) {
+    super(props)
+
+    this.state = {
+      mode: 'expanded'
+    }
+
+    // prebind:
+    this.select = this.select.bind(this)
+    this.add    = this.add.bind(this)
+    this.remove = this.remove.bind(this)
+    this.expand = this.expand.bind(this)
+    this.collapse = this.collapse.bind(this)
+  }
+
+  select(e) {
+    let account = this.props.accounts.find(
+      account => account.id == e.currentTarget.dataset.id
+    )
+    if (this.props.onSelect) {
+      this.props.onSelect(account)
+    }
+  }
+
+  add() {
+    App.show('wizard')
+  }
+
+  remove() {
+  }
+
+  expand() {
+    this.setState({mode: 'expanded'})
+  }
+
+  collapse() {
+    this.setState({mode: 'collapsed'})
+  }
+
+  render() {
+    let style = {}
+    let expandButton = null
+    let plusminusButtons = null
+
+    if (this.state.mode == 'expanded') {
+      expandButton = (
+        <Button onClick={this.collapse} className="expander btn-inverse btn-flat pull-right">
+          <Glyphicon glyph="triangle-left" />
+        </Button>
+      )
+      plusminusButtons = (
+        <ButtonGroup style={style}>
+          <Button onClick={this.add} className="btn-inverse">
+            <Glyphicon glyph="plus" />
+          </Button>
+          <Button disabled={this.props.account == null} onClick={this.remove} className="btn-inverse">
+            <Glyphicon glyph="minus" />
+          </Button>
+        </ButtonGroup>
+      )
+    } else {
+      style.width = '60px'
+      expandButton = (
+        <Button onClick={this.expand} className="expander btn-inverse btn-flat pull-right">
+          <Glyphicon glyph="triangle-right" />
+        </Button>
+      )
+    }
+
+    let items = this.props.accounts.map((account, i) => {
+      let className = account == this.props.account ? 'active' : 'inactive'
+      return (
+        <li key={i} className={className} onClick={this.select} data-id={account.id}>
+          <span className="username">{account.userpart}</span>
+          <span className="domain">{account.domain}</span>
+          <span className="arc top"></span>
+          <span className="arc bottom"></span>
+        </li>
+      )
+    })
+
+
+    return (
+      <div className="accounts" style={style}>
+        <ul>
+          {items}
+        </ul>
+        <ButtonToolbar>
+          {plusminusButtons}
+          {expandButton}
+        </ButtonToolbar>
+      </div>
+    )
+  }
+
+
+}
diff --git a/ui/app/components/main_panel/email_section.js b/ui/app/components/main_panel/email_section.js
new file mode 100644
index 00000000..a6525d92
--- /dev/null
+++ b/ui/app/components/main_panel/email_section.js
@@ -0,0 +1,48 @@
+import React from 'react'
+//import { Button, Glyphicon, Alert } from 'react-bootstrap'
+import SectionLayout from './section_layout'
+import Account from 'models/account'
+import Spinner from 'components/spinner'
+import bitmask from 'lib/bitmask'
+
+export default class EmailSection extends React.Component {
+
+  static get defaultProps() {return{
+    account: null
+  }}
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      status: null
+    }
+    this.openKeys  = this.openKeys.bind(this)
+    this.openApp   = this.openApp.bind(this)
+    this.openPrefs = this.openPrefs.bind(this)
+
+    console.log('email constructor')
+  }
+
+  openKeys() {}
+  openApp() {}
+  openPrefs() {}
+
+  render () {
+    //let message = null
+    //if (this.state.error) {
+    //  // style may be: success, warning, danger, info
+    //  message = (
+    //    <Alert bsStyle="danger">{this.state.error}</Alert>
+    //  )
+    //}
+    let button = null
+    if (this.state.status == 'ready') {
+      button = <Button onClick={this.openApp}>Open Email</Button>
+    }
+    return (
+      <SectionLayout icon="envelope" status="on" button={button}>
+        <h1>inbox: </h1>
+      </SectionLayout>
+    )
+  }
+}
diff --git a/ui/app/components/main_panel/index.js b/ui/app/components/main_panel/index.js
new file mode 100644
index 00000000..3cc6c11f
--- /dev/null
+++ b/ui/app/components/main_panel/index.js
@@ -0,0 +1,80 @@
+//
+// The main panel manages the current account and the list of available accounts
+//
+// It displays multiple sections, one for each service.
+//
+
+import React from 'react'
+import App from 'app'
+import Login from 'components/login'
+import Account from 'models/account'
+import DummyAccount from 'models/dummy_account'
+
+import './main_panel.less'
+import AccountList from './account_list'
+import UserSection from './user_section'
+import EmailSection from './email_section'
+
+export default class MainPanel extends React.Component {
+
+  static get defaultProps() {return{
+    initialAccount: null
+  }}
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      account: null,
+      accounts: []
+    }
+    this.activateAccount = this.activateAccount.bind(this)
+  }
+
+  componentWillMount() {
+    if (this.props.initialAccount) {
+      console.log(Account.list)
+      Account.add(this.props.initialAccount)
+      Account.add(new DummyAccount(this.props.initialAccount))
+      this.setState({
+        account: this.props.initialAccount,
+        accounts: Account.list
+      })
+    }
+  }
+
+  activateAccount(account) {
+    this.setState({
+      account: account,
+      accounts: Account.list
+    })
+  }
+
+  //setAccounts(accounts) {
+  //  this.setState({
+  //    accounts: accounts
+  //  })
+  //}
+
+  render() {
+    let emailSection = null
+    let vpnSection = null
+
+    if (this.state.account.authenticated) {
+      if (this.state.account.hasEmail) {
+        emailSection = <EmailSection account={this.state.account} />
+      }
+    }
+
+    return (
+      <div className="main-panel">
+        <AccountList account={this.state.account} accounts={this.state.accounts} onSelect={this.activateAccount} />
+        <div className="body">
+          <UserSection account={this.state.account} onLogin={this.activateAccount} onLogout={this.activateAccount}/>
+          {vpnSection}
+          {emailSection}
+        </div>
+      </div>
+    )
+  }
+
+}
diff --git a/ui/app/components/main_panel/main_panel.less b/ui/app/components/main_panel/main_panel.less
new file mode 100644
index 00000000..4e0ecb05
--- /dev/null
+++ b/ui/app/components/main_panel/main_panel.less
@@ -0,0 +1,212 @@
+// The space around account entries:
+@accounts-padding: 8px;
+@accounts-corner: 6px;
+@accounts-width: 200px;
+
+//
+// LAYOUT
+//
+
+.main-panel {
+  position: absolute;
+  height: 100%;
+  width: 100%;
+  display: -webkit-flex;
+  -webkit-flex-direction: row;
+
+  > .body {
+    -webkit-flex: 1 1 auto;
+    overflow: auto;
+  }
+
+  .accounts {
+    -webkit-flex: 0 0 auto;
+    overflow-y: auto;
+    overflow-x: hidden;
+
+    display: -webkit-flex;
+    -webkit-flex-direction: column;
+    ul {
+      -webkit-flex: 1 1 1000px;
+    }
+    .btn-toolbar {
+      -webkit-flex: 0 0 auto;
+    }
+
+  }
+
+}
+
+//
+// Style
+//
+
+
+.main-panel > .body {
+  padding: 20px;
+}
+
+.main-panel .accounts {
+  background-color: #333;
+  width: @accounts-width;
+  padding: @accounts-padding;
+  padding-right: 0px;
+}
+
+.main-panel .accounts ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
+.main-panel .accounts li {
+  position: relative;
+  cursor: pointer;
+  color: white;
+  padding: 15px;
+  background-color: #444;
+  margin-bottom: @accounts-padding;
+  border-top-left-radius: @accounts-corner - 1;
+  border-bottom-left-radius: @accounts-corner - 1;
+  z-index: 100;
+}
+
+.main-panel .accounts li span.domain {
+  display: block;
+  font-weight: bold;
+  //margin-left: 40px;
+}
+
+.main-panel .accounts li span.username {
+  display: block;
+  //margin-left: 40px;
+  //line-height: 7px;
+  //margin-bottom: 4px;
+}
+
+/*.main-panel .accounts li span.icon {
+  display: block;
+  height: 32px;
+  width: 32px;
+  background-color: #999;
+  float: left;
+}
+*/
+
+.main-panel .accounts li.active {
+  background-color: white;
+  color: #333;
+}
+
+.main-panel .accounts li.active span.arc {
+  display: block;
+  height: @accounts-corner;
+  width: @accounts-corner;
+  background-color: white;
+  position: absolute;
+  right: 0;
+}
+
+.main-panel .accounts li.active span.arc.top {
+  top: 0;
+  margin-top: -@accounts-corner;
+}
+.main-panel .accounts li.active span.arc.bottom {
+  bottom: 0;
+  margin-bottom: -@accounts-corner;
+}
+.main-panel .accounts li.active span.arc:after {
+  display: block;
+  content: "";
+  border-radius: 100%;
+  height: 0px;
+  width: 0px;
+  margin-left: -@accounts-corner;
+}
+.main-panel .accounts li.active span.arc.top:after {
+  border: @accounts-corner solid transparent;
+  border-right: @accounts-corner solid #333;
+  margin-top: -@accounts-corner;
+  -webkit-transform: rotate(45deg);
+  transform: rotate(45deg);
+}
+.main-panel .accounts li.active span.arc.bottom:after {
+  border: @accounts-corner solid #333;
+}
+
+.main-panel .accounts .btn.expander {
+   margin-right: @accounts-padding;
+}
+
+//
+// SECTIONS
+//
+
+@icon-size: 32px;
+@status-size: 24px;
+@section-padding: 10px;
+
+// service sections layout
+
+.main-panel .service-section {
+  display: -webkit-flex;
+  -webkit-flex-direction: row;
+  > .icon {
+    -webkit-flex: 0 0 auto;
+  }
+  > .body {
+    -webkit-flex: 1 1 auto;
+  }
+  > .buttons {
+    -webkit-flex: 0 0 auto;
+  }
+  > .status {
+    -webkit-flex: 0 0 auto;
+    display: -webkit-flex;
+    -webkit-align-items: center;
+  }
+
+}
+
+.main-panel .service-section div {
+  //outline: 1px solid rgba(0,0,0,0.1);
+}
+
+// service sections style
+
+.main-panel .service-section {
+  background: #f6f6f6;
+  border-radius: 4px;
+  padding: 10px;
+  margin-bottom: 10px;
+  &.wide-margin {
+    padding: 20px 20px 20px 10px; // arbitrary, looks nice
+  }
+  > .icon {
+    padding-right: @section-padding;
+    img {
+      width: @icon-size;
+      height: @icon-size;
+    }
+  }
+  > .body {
+    h1 {
+      margin: 0;
+      padding: 0;
+      font-size: @icon-size - 10;
+      line-height: @icon-size;
+    }
+  }
+  > .buttons {
+    padding-left: 10px;
+  }
+  > .status {
+    padding-left: @section-padding;
+    width: @section-padding + @status-size;
+    img {
+      width: @status-size;
+      height: @status-size;
+    }
+  }
+}
+
diff --git a/ui/app/components/main_panel/section_layout.js b/ui/app/components/main_panel/section_layout.js
new file mode 100644
index 00000000..e7c6f2ab
--- /dev/null
+++ b/ui/app/components/main_panel/section_layout.js
@@ -0,0 +1,59 @@
+//
+// This is the layout for a service section in the main window.
+// It does not do anything except for arrange items using css and html.
+//
+
+import React from 'react'
+
+export default class SectionLayout extends React.Component {
+
+  static get defaultProps() {return{
+    icon: null,
+    buttons: null,
+    status: null,
+    className: "",
+    style: {}
+  }}
+
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    let className = ["service-section", this.props.className].join(' ')
+    let status = null
+    let icon = null
+    let buttons = null
+
+    if (this.props.status) {
+      status = (
+        <div className="status">
+          <img src={'img/' + this.props.status + '.svg' } />
+        </div>
+      )
+    }
+    if (this.props.icon) {
+      icon = (
+        <div className="icon">
+          <img src={'img/' + this.props.icon + '.svg'} />
+        </div>
+      )
+    }
+    if (this.props.buttons)
+      buttons = (
+        <div className="buttons">
+          {this.props.buttons}
+        </div>
+      )
+    return(
+      <div className={className} style={this.props.style}>
+        {icon}
+        <div className="body">
+          {this.props.children}
+        </div>
+        {buttons}
+        {status}
+      </div>
+    )
+  }
+}
diff --git a/ui/app/components/main_panel/user_section.js b/ui/app/components/main_panel/user_section.js
new file mode 100644
index 00000000..0b4ba136
--- /dev/null
+++ b/ui/app/components/main_panel/user_section.js
@@ -0,0 +1,71 @@
+import React from 'react'
+import { Button, Glyphicon, Alert } from 'react-bootstrap'
+import SectionLayout from './section_layout'
+import Login from 'components/login'
+import Spinner from 'components/spinner'
+import Account from 'models/account'
+
+import bitmask from 'lib/bitmask'
+
+export default class UserSection extends React.Component {
+
+  static get defaultProps() {return{
+    account: null,
+    onLogout: null,
+    onLogin: null
+  }}
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      error: null,
+      loading: false
+    }
+    this.logout = this.logout.bind(this)
+  }
+
+  logout() {
+    this.setState({loading: true})
+    this.props.account.logout().then(
+      account => {
+        this.setState({error: null, loading: false})
+        if (this.props.onLogout) {
+          this.props.onLogout(account)
+        }
+      }, error => {
+        this.setState({error: error, loading: false})
+      }
+    )
+  }
+
+  render () {
+    let message = null
+    if (this.state.error) {
+      // style may be: success, warning, danger, info
+      message = (
+        <Alert bsStyle="danger">{this.state.error}</Alert>
+      )
+    }
+
+    if (this.props.account.authenticated) {
+      let button = null
+      if (this.state.loading) {
+        button = <Button disabled={true}><Spinner /></Button>
+      } else {
+        button = <Button onClick={this.logout}>Log Out</Button>
+      }
+      return (
+        <SectionLayout icon="user" buttons={button} status="on">
+          <h1>{this.props.account.address}</h1>
+          {message}
+        </SectionLayout>
+      )
+    } else {
+      return (
+        <SectionLayout icon="user" className="wide-margin">
+          <Login onLogin={this.props.onLogin} domain={this.props.account.domain} />
+        </SectionLayout>
+      )
+    }
+  }
+}
diff --git a/ui/app/components/main_panel/vpn_section.js b/ui/app/components/main_panel/vpn_section.js
new file mode 100644
index 00000000..e69de29b
diff --git a/ui/app/components/panel_switcher.js b/ui/app/components/panel_switcher.js
new file mode 100644
index 00000000..aaf2dc5b
--- /dev/null
+++ b/ui/app/components/panel_switcher.js
@@ -0,0 +1,58 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import DebugPanel from './debug_panel'
+import Splash from './splash'
+import GreeterPanel from './greeter_panel'
+import MainPanel from './main_panel'
+import Wizard from './wizard'
+
+import App from 'app'
+import 'lib/common'
+
+export default class PanelSwitcher extends React.Component {
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      panel: null,
+      panel_properties: null,
+      debug: false
+    }
+    App.switcher = this
+  }
+
+  show(component_name, properties={}) {
+    this.setState({panel: component_name, panel_properties: properties})
+  }
+
+  render() {
+    let elems = []
+    if (this.panelExist(this.state.panel)) {
+      elems.push(
+        this.panelRender(this.state.panel, this.state.panel_properties)
+      )
+    }
+    if (this.state.debug) {
+      elems.push(
+        elem(DebugPanel, {key: 'debug'})
+      )
+    }
+    return <div id="root">{elems}</div>
+  }
+
+  panelExist(panel) {
+    return panel && this['render_'+panel]
+  }
+
+  panelRender(panel_name, props) {
+    let panel = this['render_'+panel_name](props)
+    return elem('div', {key: 'panel'}, panel)
+  }
+
+  render_splash(props)  {return elem(Splash, props)}
+  render_wizard(props)  {return elem(Wizard, props)}
+  render_greeter(props) {return elem(GreeterPanel, props)}
+  render_main(props)    {return elem(MainPanel, props)}
+
+}
diff --git a/ui/app/components/spinner/index.js b/ui/app/components/spinner/index.js
new file mode 100644
index 00000000..ffc32850
--- /dev/null
+++ b/ui/app/components/spinner/index.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import './spinner.css';
+
+class Spinner extends React.Component {
+  render() {
+    let props = {}
+    return <div className="spinner">
+      <div className="spin1"></div>
+      <div className="spin2"></div>
+      <div className="spin3"></div>
+    </div>
+  }
+}
+
+export default Spinner
\ No newline at end of file
diff --git a/ui/app/components/spinner/spinner.css b/ui/app/components/spinner/spinner.css
new file mode 100644
index 00000000..5e8535c9
--- /dev/null
+++ b/ui/app/components/spinner/spinner.css
@@ -0,0 +1,42 @@
+.spinner {
+  height: 18px;
+  display: inline-block;
+}
+
+.spinner > div {
+  width: 18px;
+  height: 18px;
+  background-color: #000;
+  vertical-align: middle;
+  border-radius: 100%;
+  display: inline-block;
+  -webkit-animation: bouncedelay 1.5s infinite ease-in-out;
+  animation: bouncedelay 1.5s infinite ease-in-out;
+  -webkit-animation-fill-mode: both;
+  animation-fill-mode: both;
+}
+
+.spinner .spin1 {
+  -webkit-animation-delay: -.46s;
+  animation-delay: -.46s;
+}
+.spinner .spin2 {
+  -webkit-animation-delay: -.24s;
+  animation-delay: -.24s;
+}
+
+@-webkit-keyframes bouncedelay {
+  0%, 80%, 100% {
+    -webkit-transform: scale(0.5);
+  } 40% {
+    -webkit-transform: scale(0.9);
+  }
+}
+
+@keyframes bouncedelay {
+  0%, 80%, 100% {
+    transform: scale(0.5);
+  } 40% {
+    transform: scale(0.9);
+  }
+}
diff --git a/ui/app/components/splash.js b/ui/app/components/splash.js
new file mode 100644
index 00000000..46170d23
--- /dev/null
+++ b/ui/app/components/splash.js
@@ -0,0 +1,132 @@
+/*
+ * A simple animated splash screen
+ */
+
+import React from 'react'
+import * as COLOR from '../lib/colors'
+
+const colorList = [
+  COLOR.red200, COLOR.pink200, COLOR.purple200, COLOR.deepPurple200,
+  COLOR.indigo200, COLOR.blue200, COLOR.lightBlue200, COLOR.cyan200,
+  COLOR.teal200, COLOR.green200, COLOR.lightGreen200, COLOR.lime200,
+  COLOR.yellow200, COLOR.amber200, COLOR.orange200, COLOR.deepOrange200
+]
+
+export default class Splash extends React.Component {
+
+  static get defaultProps() {return{
+    speed: "fast",
+    mask: true,
+    onClick: null
+  }}
+
+  constructor(props) {
+    super(props)
+    this.counter = 0
+    this.interval = null
+    this.ctx = null
+    this.stepAngle = 0
+    this.resize = this.resize.bind(this)
+    this.click  = this.click.bind(this)
+    if (this.props.speed == "fast") {
+      this.fps = 30
+      this.stepAngle = 0.005
+    } else {
+      this.fps = 30
+      this.stepAngle = 0.0005
+    }
+  }
+
+  componentDidMount() {
+    this.interval = setInterval(this.tick.bind(this), 1000/this.fps)
+    this.canvas   = this.refs.canvas
+    this.ctx      = this.canvas.getContext('2d')
+    window.addEventListener('resize', this.resize)
+  }
+
+  componentWillUnmount() {
+    clearInterval(this.interval)
+    window.removeEventListener('resize', this.resize)
+  }
+
+  click() {
+    if (this.props.onClick) {
+      this.props.onClick()
+    }
+  }
+
+  tick() {
+    this.counter++
+    this.updateCanvas()
+  }
+
+  resize() {
+    this.canvas.width = window.innerWidth
+    this.canvas.height = window.innerHeight
+    this.updateCanvas()
+  }
+
+  updateCanvas() {
+    const arcCount = 16
+    const arcAngle = 1 / arcCount
+    const x = this.canvas.width / 2
+    const y = this.canvas.height / 2
+    const radius = screen.height + screen.width
+
+    for (let i = 0; i < arcCount; i++) {
+      let startAngle = Math.PI * 2 * i/arcCount + this.stepAngle*this.counter
+      let endAngle   = Math.PI * 2 * (i+1)/arcCount + this.stepAngle*this.counter
+
+      this.ctx.fillStyle = colorList[i % colorList.length]
+      this.ctx.strokeStyle = colorList[i % colorList.length]
+      this.ctx.beginPath()
+      this.ctx.moveTo(x, y)
+      this.ctx.arc(x, y, radius, startAngle, endAngle)
+      this.ctx.lineTo(x, y)
+      this.ctx.fill()
+      this.ctx.stroke()
+    }
+
+  }
+
+  render () {
+    let overlay = null
+    let mask = null
+    if (this.props.onClick) {
+      overlay = React.DOM.div({
+        style: {
+          position: 'absolute',
+          height: '100%',
+          width: '100%',
+          backgroundColor: 'transparent'
+        },
+        onClick: this.click
+      })
+    }
+    if (this.props.mask) {
+      mask = React.DOM.img({
+        src: 'img/mask.svg',
+        style: {
+          position: 'absolute',
+          left: '50%',
+          top: '50%',
+          marginLeft: -330/2 + 'px',
+          marginTop: -174/2 + 'px',
+        }
+      })
+    }
+    return React.DOM.div(
+      {style: {overflow: 'hidden'}},
+      React.DOM.canvas({
+        ref: 'canvas',
+        style: {position: 'absolute'},
+        width: window.innerWidth,
+        height: window.innerHeight,
+      }),
+      mask,
+      overlay
+    )
+  }
+
+}
+
diff --git a/ui/app/components/wizard/add_provider_modal.js b/ui/app/components/wizard/add_provider_modal.js
new file mode 100644
index 00000000..bc5e0236
--- /dev/null
+++ b/ui/app/components/wizard/add_provider_modal.js
@@ -0,0 +1,94 @@
+//
+// A modal popup to add a new provider.
+//
+
+import React from 'react'
+import { FormGroup, ControlLabel, FormControl, HelpBlock, Button, Modal } from 'react-bootstrap'
+import Spinner from '../spinner'
+import Validate from '../../lib/validate'
+import App from '../../app'
+
+class AddProviderModal extends React.Component {
+
+  static get defaultProps() {return{
+    title: 'Add a provider',
+    onClose: null
+  }}
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      validationState: null,
+      errorMsg: null,
+      domain: ""
+    }
+    this.accept   = this.accept.bind(this)
+    this.cancel   = this.cancel.bind(this)
+    this.changed  = this.changed.bind(this)
+  }
+
+  accept() {
+    if (this.state.domain) {
+      App.providers.add(this.state.domain)
+    }
+    this.props.onClose()
+  }
+
+  cancel() {
+    this.props.onClose()
+  }
+
+  changed(e) {
+    let domain = e.target.value
+    let newState = null
+    let newMsg   = null
+
+    if (domain.length > 0) {
+      let error = Validate.domain(domain)
+      newState = error ? 'error' : 'success'
+      newMsg   = error
+    }
+    this.setState({
+      domain: domain,
+      validationState: newState,
+      errorMsg: newMsg
+    })
+  }
+
+  render() {
+    let help = null
+    if (this.state.errorMsg) {
+      help = <HelpBlock>{this.state.errorMsg}</HelpBlock>
+    } else {
+      help = <HelpBlock>&nbsp;</HelpBlock>
+    }
+    let form = <form onSubmit={this.accept} autoComplete="off">
+      <FormGroup controlId="addprovider" validationState={this.state.validationState}>
+        <ControlLabel>Domain</ControlLabel>
+        <FormControl
+          type="text"
+          ref="domain"
+          autoFocus
+          value={this.state.domain}
+          onChange={this.changed}
+          onBlur={this.changed} />
+        <FormControl.Feedback/>
+        {help}
+      </FormGroup>
+      <Button onClick={this.accept}>Add</Button>
+    </form>
+
+    return(
+      <Modal show={true} onHide={this.cancel}>
+        <Modal.Header closeButton>
+          <Modal.Title>{this.props.title}</Modal.Title>
+        </Modal.Header>
+        <Modal.Body>
+          {form}
+        </Modal.Body>
+      </Modal>
+    )
+  }
+}
+
+export default AddProviderModal
\ No newline at end of file
diff --git a/ui/app/components/wizard/index.js b/ui/app/components/wizard/index.js
new file mode 100644
index 00000000..613b88fd
--- /dev/null
+++ b/ui/app/components/wizard/index.js
@@ -0,0 +1,38 @@
+//
+// The provider setup wizard
+//
+
+import React from 'react'
+import App from 'app'
+
+import ProviderSelectStage from './provider_select_stage'
+import './wizard.less'
+
+export default class Wizard extends React.Component {
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      stage: 'provider'
+    }
+  }
+
+  setStage(stage) {
+    this.setState({stage: stage})
+  }
+
+  render() {
+    let stage = null
+    switch(this.state.stage) {
+      case 'provider':
+        stage = <ProviderSelectStage />
+        break
+    }
+    return(
+      <div className="wizard">
+        {stage}
+      </div>
+    )
+  }
+
+}
diff --git a/ui/app/components/wizard/provider_select_stage.js b/ui/app/components/wizard/provider_select_stage.js
new file mode 100644
index 00000000..20674be1
--- /dev/null
+++ b/ui/app/components/wizard/provider_select_stage.js
@@ -0,0 +1,86 @@
+import React from 'react'
+import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap'
+
+import App from 'app'
+import ListEdit from 'components/list_edit'
+import StageLayout from './stage_layout'
+import AddProviderModal from './add_provider_modal'
+
+export default class ProviderSelectStage extends React.Component {
+
+  static get defaultProps() {return{
+    title: "Choose a provider",
+    subtitle: "This doesn't work yet"
+  }}
+
+  constructor(props) {
+    super(props)
+    let domains = this.currentDomains()
+    this.state = {
+      domains: domains,
+      showModal: false
+    }
+    this.add = this.add.bind(this)
+    this.remove = this.remove.bind(this)
+    this.close = this.close.bind(this)
+    this.previous = this.previous.bind(this)
+  }
+
+  currentDomains() {
+    // return(App.providers.domains().slice() || [])
+    return ['domain1', 'domain2', 'domain3']
+  }
+
+  add() {
+    this.setState({showModal: true})
+  }
+
+  remove(provider) {
+    // App.providers.remove(provider)
+    this.setState({domains: this.currentDomains()})
+  }
+
+  close() {
+    let domains = this.currentDomains()
+    if (domains.length != this.state.domains.length) {
+      // this is ugly, but i could not get selection working
+      // by passing it as a property
+      this.refs.list.setSelected(0)
+    }
+    this.setState({
+      domains: domains,
+      showModal: false
+    })
+  }
+
+  previous() {
+    App.start()
+  }
+
+  render() {
+    let modal = null
+    if (this.state.showModal) {
+      modal = <AddProviderModal onClose={this.close} />
+    }
+    let buttons = (
+      <ButtonToolbar className="pull-right">
+        <Button onClick={this.previous}>
+          <Glyphicon glyph="chevron-left" />
+          Previous
+        </Button>
+        <Button>
+          Next
+          <Glyphicon glyph="chevron-right" />
+        </Button>
+      </ButtonToolbar>
+    )
+    let select = <ListEdit ref="list" items={this.state.domains}
+      onRemove={this.remove} onAdd={this.add} />
+    return(
+      <StageLayout title={this.props.title} subtitle={this.props.subtitle} buttons={buttons}>
+        {select}
+        {modal}
+      </StageLayout>
+    )
+  }
+}
diff --git a/ui/app/components/wizard/stage_layout.js b/ui/app/components/wizard/stage_layout.js
new file mode 100644
index 00000000..31540221
--- /dev/null
+++ b/ui/app/components/wizard/stage_layout.js
@@ -0,0 +1,37 @@
+import React from 'react'
+
+class StageLayout extends React.Component {
+
+  static get defaultProps() {return{
+    title: 'untitled',
+    subtitle: null,
+    buttons: null
+  }}
+
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    let subtitle = null
+    if (this.props.subtitle) {
+      subtitle = <span>{this.props.subtitle}</span>
+    }
+    return(
+      <div className="stage">
+        <div className="header">
+          {this.props.title}
+          {subtitle}
+        </div>
+        <div className="body">
+          {this.props.children}
+        </div>
+        <div className="footer">
+          {this.props.buttons}
+        </div>
+      </div>
+    )
+  }
+}
+
+export default StageLayout
\ No newline at end of file
diff --git a/ui/app/components/wizard/wizard.less b/ui/app/components/wizard/wizard.less
new file mode 100644
index 00000000..29efc20e
--- /dev/null
+++ b/ui/app/components/wizard/wizard.less
@@ -0,0 +1,44 @@
+.wizard .stage {
+  position: absolute;
+  height: 100%;
+  width: 100%;
+
+  display: -webkit-flex;
+  display: flex;
+  -webkit-flex-direction: column;
+  flex-direction: column;
+  -webkit-flex: 1;
+  flex: 1;
+}
+
+.wizard .stage .footer {
+  -webkit-flex: 0 0 auto;
+  flex: 0 0 auto;
+  background-color: #ddd;
+  padding: 20px;
+  text-align: right;
+}
+
+.wizard .stage .header {
+  -webkit-flex: 0 0 auto;
+  flex: 0 0 auto;
+  padding: 20px;
+  background-color: #333;
+  color: white;
+  font-size: 2em;
+  span {
+    margin-left: 10px;
+    font-size: 0.5em;
+  }
+}
+
+.wizard .stage .body {
+  -webkit-flex: 1 1 auto;
+  flex: 1 1 auto;
+  padding: 20px;
+  overflow: auto;
+  display: -webkit-flex;
+  display: flex;
+  -webkit-flex-direction: column;
+  flex-direction: column;
+}
\ No newline at end of file
diff --git a/ui/app/css/bootstrap.less b/ui/app/css/bootstrap.less
new file mode 100644
index 00000000..3b772284
--- /dev/null
+++ b/ui/app/css/bootstrap.less
@@ -0,0 +1,5 @@
+//
+// require npm modules 'bootstrap'
+//
+@import "~bootstrap/less/bootstrap";
+
diff --git a/ui/app/css/common.css b/ui/app/css/common.css
new file mode 100644
index 00000000..acf164ee
--- /dev/null
+++ b/ui/app/css/common.css
@@ -0,0 +1,80 @@
+body {
+  padding: 0;
+  margin: 0;
+}
+
+.debug-panel {
+  background-color: rgba(0,0,0,0.1);
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  z-index: 1000;
+  padding: 20px;
+}
+.debug-panel a {
+  cursor: pointer;
+  margin: 10px;
+}
+
+
+.area-light {
+  background-color: #f7f7f7;
+}
+.area-dark {
+  background-color: #e7e7e7;
+}
+.area-clear {
+  background-color: #fff;
+}
+
+/*
+ * Greeter
+ */
+.greeter {
+  border-color: #555;
+  border-width: 8px;
+}
+
+/*
+ * bootstrap
+ */
+
+.help-block {
+  margin: 2px 0 0 0;
+  font-size: small;
+  opacity: 0.7;
+}
+.btn.btn-inverse {
+  color: white;
+  background-color: #333;
+}
+.btn.btn-flat {
+  border-color: transparent;
+  background-color: transparent;
+}
+
+/*.btn.btn-default {
+  background-color: #eee !important;
+}
+*/
+
+/*
+ * center component
+ */
+
+.center-container {
+  position: absolute;
+  display: -webkit-flex;
+  -webkit-flex-flow: row nowrap;
+  -webkit-justify-content: center;
+  -webkit-align-content: center;
+  -webkit-align-items: center;
+  top: 0px;
+  left: 0px;
+  height: 100%;
+  width: 100%;
+}
+
+.center-container .center-item {
+  -webkit-flex: 0 1 auto;
+}
diff --git a/ui/app/img/arrow-down.svg b/ui/app/img/arrow-down.svg
new file mode 100644
index 00000000..14c4b5d1
--- /dev/null
+++ b/ui/app/img/arrow-down.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg4986"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="arrow-down.svg">
+  <defs
+     id="defs4988" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="16"
+     inkscape:cy="16"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="869"
+     inkscape:window-height="648"
+     inkscape:window-x="1049"
+     inkscape:window-y="24"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata4991">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       transform="matrix(0.38339748,0,0,0.40552158,-78.363595,-91.56011)"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="text3021">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 277.42382,253.6675 c -7e-5,1.41967 -0.4956,2.62503 -1.48661,3.61607 l -26.15625,26.15625 c -1.0179,1.01786 -2.23664,1.52679 -3.65625,1.52679 -1.44646,0 -2.65182,-0.50893 -3.61607,-1.52679 l -26.15625,-26.15625 c -1.01786,-0.96426 -1.52679,-2.16961 -1.52679,-3.61607 0,-1.41961 0.50893,-2.63836 1.52679,-3.65625 l 2.97321,-3.01339 c 1.04464,-0.99104 2.26338,-1.48657 3.65625,-1.48661 1.41963,4e-5 2.62499,0.49557 3.61607,1.48661 l 19.52679,19.52678 19.52678,-19.52678 c 0.99102,-0.99104 2.19638,-1.48657 3.61608,-1.48661 1.39279,4e-5 2.61154,0.49557 3.65625,1.48661 l 3.01339,3.01339 c 0.99101,1.04468 1.48654,2.26343 1.48661,3.65625"
+         id="path3147" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/arrow-up.svg b/ui/app/img/arrow-up.svg
new file mode 100644
index 00000000..30ea8fde
--- /dev/null
+++ b/ui/app/img/arrow-up.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg4938"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="arrow-up.svg">
+  <defs
+     id="defs4940" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="16"
+     inkscape:cy="16"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="852"
+     inkscape:window-height="637"
+     inkscape:window-x="926"
+     inkscape:window-y="356"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata4943">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="text3017"
+       transform="matrix(0.38339753,0,0,0.40552158,-32.550528,-82.646856)">
+      <path
+         d="m 156.40467,251.17438 c 1.01779,1.01787 1.52672,2.23662 1.52678,3.65625 -6e-5,1.41966 -0.4956,2.62501 -1.48661,3.61607 l -3.01339,3.0134 c -1.01791,1.01786 -2.23666,1.52679 -3.65625,1.52678 -1.44648,1e-5 -2.65184,-0.50892 -3.61607,-1.52678 l -19.52679,-19.48661 -19.52678,19.48661 c -0.9643,1.01786 -2.16966,1.52679 -3.61607,1.52678 -1.44644,1e-5 -2.6518,-0.50892 -3.616074,-1.52678 l -3.013393,-3.0134 c -1.017859,-0.96427 -1.526787,-2.16963 -1.526785,-3.61607 -2e-6,-1.41963 0.508926,-2.63838 1.526785,-3.65625 l 26.156247,-26.15625 c 0.99104,-0.99103 2.1964,-1.48656 3.61607,-1.48661 1.39283,5e-5 2.61158,0.49558 3.65625,1.48661 l 26.11608,26.15625"
+         id="path3144"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/cloud.svg b/ui/app/img/cloud.svg
new file mode 100644
index 00000000..bf7a94af
--- /dev/null
+++ b/ui/app/img/cloud.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg4890"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="New document 26">
+  <defs
+     id="defs4892" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="16"
+     inkscape:cy="16"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="825"
+     inkscape:window-height="672"
+     inkscape:window-x="1093"
+     inkscape:window-y="24"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata4895">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       transform="matrix(0.36296297,0,0,0.35353541,-29.370372,-250.08767)"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="text3105">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 163.57143,765.50502 c -8e-5,4.25894 -1.51347,7.9018 -4.54018,10.92857 -3.00007,3.00001 -6.62953,4.5 -10.88839,4.5 l -43.71429,0 c -4.955367,0 -9.200898,-1.75446 -12.736604,-5.26339 -3.50893,-3.5357 -5.263392,-7.78124 -5.263392,-12.73661 0,-3.53569 0.950892,-6.76336 2.852678,-9.68303 1.901781,-2.9464 4.406243,-5.14283 7.513393,-6.58929 -0.05358,-0.74996 -0.08037,-1.32585 -0.08036,-1.72768 -1e-5,-5.67853 2.008916,-10.52674 6.026785,-14.54464 4.01784,-4.0178 8.86605,-6.02673 14.54465,-6.02678 4.2321,5e-5 8.06246,1.17862 11.49107,3.53571 3.45531,2.35719 5.97316,5.43755 7.55357,9.24107 1.87495,-1.66067 4.09816,-2.49102 6.66964,-2.49107 2.83923,5e-5 5.26333,1.00451 7.27232,3.01339 2.00887,2.00897 3.01333,4.43308 3.0134,7.27232 -7e-5,2.00897 -0.54918,3.85718 -1.64732,5.54465 3.45528,0.8036 6.30796,2.61163 8.55803,5.4241 2.24992,2.78574 3.37492,5.98663 3.375,9.60268"
+         id="path3210" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/disabled.svg b/ui/app/img/disabled.svg
new file mode 100644
index 00000000..90804d83
--- /dev/null
+++ b/ui/app/img/disabled.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3243"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="disabled.svg">
+  <defs
+     id="defs3245" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="17.941968"
+     inkscape:cy="7.7251942"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:snap-global="false"
+     inkscape:window-width="1879"
+     inkscape:window-height="1056"
+     inkscape:window-x="1961"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3273" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3248">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#808080;stroke:#000000;stroke-width:1.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
+       d="m 26.607098,9.8590516 c 1.095246,1.8715424 1.642877,3.9185224 1.642902,6.1409504 -2.5e-5,2.222447 -0.547656,4.274744 -1.642902,6.156901 -1.095289,1.871529 -2.584005,3.354927 -4.466145,4.450195 C 20.269406,27.702363 18.222425,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.843099,26.607098 7.9715637,25.51183 6.4881673,24.028432 5.3929019,22.156903 4.2976327,20.274746 3.75,18.222449 3.75,16.000002 3.75,13.777574 4.2976327,11.730594 5.3929019,9.8590516 6.4881673,7.9769072 7.9715637,6.4881927 9.843099,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222425,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.88214,1.0952865 3.370856,2.584001 4.466145,4.4661454"
+       id="path3275"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 8.0269717,14.182874 15.9772693,0.09375 1e-6,3.585262 -15.9772703,-0.09375 z"
+       id="rect4059"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+  </g>
+</svg>
diff --git a/ui/app/img/envelope.svg b/ui/app/img/envelope.svg
new file mode 100644
index 00000000..5775e097
--- /dev/null
+++ b/ui/app/img/envelope.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3145"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="envelope.svg">
+  <defs
+     id="defs3147" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.627417"
+     inkscape:cx="14.496338"
+     inkscape:cy="14.835533"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1879"
+     inkscape:window-height="1056"
+     inkscape:window-x="1961"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <metadata
+     id="metadata3150">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       inkscape:connector-curvature="0"
+       id="path4141"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 29.999988,11.952225 c -0.46857,0.485929 -0.999616,0.912958 -1.561898,1.281087 -2.608371,1.663939 -5.23236,3.342604 -7.778252,5.08017 -1.311994,0.912958 -2.93637,2.032068 -4.638839,2.032068 l -0.01561,0 -0.01561,0 c -1.702468,0 -3.326844,-1.11911 -4.638839,-2.032068 -2.5458975,-1.752291 -5.1698865,-3.416231 -7.7626395,-5.08017 -0.577899,-0.368129 -1.108945,-0.795158 -1.577515,-1.281087 l 0,12.644634 C 2.0107855,25.89267 2.4356205,26 3.8100905,26 l 24.4683525,0 c 1.37447,0 1.721558,-0.253927 1.721558,-1.549738 l 0,-12.498037 z m 0,-4.562107 C 29.999988,6.313336 29.739813,6 28.284765,6 L 3.4515205,6 c -1.67123,0 -1.440748,0.925932 -1.440748,2.368995 0,2.622709 1.095024,3.007381 2.686464,4.021712 2.420943,1.590314 4.873125,3.180628 7.2940645,4.785667 1.015235,0.662631 2.90163,2.017343 3.998462,2.017343 l 0.01561,0 0.01561,0 c 1.265137,0 2.983227,-1.354712 3.99846,-2.017343 2.42094,-1.605039 4.873122,-3.195353 7.309683,-4.785667 2.638916,-1.960744 2.670846,-1.966834 2.670846,-5.045368 z"
+       sodipodi:nodetypes="cccscscccsssscccsssccscscccc" />
+  </g>
+</svg>
diff --git a/ui/app/img/gear.svg b/ui/app/img/gear.svg
new file mode 100644
index 00000000..be5a5b33
--- /dev/null
+++ b/ui/app/img/gear.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg4177"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="gear.svg">
+  <defs
+     id="defs4179" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="11.400883"
+     inkscape:cy="17.428852"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="962"
+     inkscape:window-height="797"
+     inkscape:window-x="874"
+     inkscape:window-y="135"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata4182">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="path3123"
+       d="m 19.299476,19.29948 c 0.911441,-0.91145 1.367172,-2.011273 1.36719,-3.299478 -1.8e-5,-1.288183 -0.455749,-2.388006 -1.36719,-3.299479 -0.911473,-0.911445 -2.0113,-1.367172 -3.299478,-1.36719 -1.288206,1.8e-5 -2.388033,0.455745 -3.299479,1.36719 -0.911468,0.911473 -1.367195,2.011296 -1.367186,3.299479 -9e-6,1.288205 0.455718,2.388028 1.367186,3.299478 0.911446,0.911463 2.011273,1.367195 3.299479,1.367186 1.288178,9e-6 2.388005,-0.455723 3.299478,-1.367186 M 30,14.01302 l 0,4.046878 c -3.2e-5,0.145843 -0.04864,0.285597 -0.145834,0.419268 -0.09725,0.133692 -0.218781,0.212682 -0.364583,0.236978 l -3.372397,0.510417 c -0.230927,0.656259 -0.467905,1.209211 -0.710936,1.658854 0.42532,0.607645 1.075496,1.446189 1.950518,2.515628 0.121501,0.145838 0.182266,0.297747 0.182293,0.455727 -2.7e-5,0.157989 -0.05471,0.297747 -0.164064,0.419272 -0.32815,0.449657 -0.929711,1.105903 -1.804688,1.968747 -0.875022,0.862849 -1.446203,1.294272 -1.713539,1.294272 -0.145856,0 -0.30384,-0.05468 -0.473961,-0.16406 L 20.867185,25.40625 c -0.534739,0.279517 -1.087691,0.510416 -1.658854,0.69271 -0.194462,1.652774 -0.370676,2.782982 -0.528646,3.390623 -0.08508,0.340277 -0.303836,0.510416 -0.656251,0.510416 l -4.046873,0 c -0.170148,0 -0.322062,-0.05469 -0.455727,-0.164064 -0.121543,-0.09722 -0.188383,-0.224828 -0.200524,-0.382812 L 12.809894,26.09896 c -0.595496,-0.194444 -1.142372,-0.419272 -1.640625,-0.674481 l -2.5703126,1.950522 c -0.1215336,0.109375 -0.2734427,0.16406 -0.4557272,0.16406 -0.1701434,0 -0.322057,-0.06684 -0.4557317,-0.200519 C 6.1562431,25.953126 5.1536442,24.932293 4.6796872,24.276042 4.5946132,24.154517 4.5520785,24.014759 4.552083,23.85677 4.5520785,23.71094 4.6006928,23.571186 4.697917,23.437498 4.880206,23.182294 5.1900993,22.781256 5.6276057,22.234376 6.0650985,21.675354 6.3932261,21.243931 6.6119793,20.940104 6.2838472,20.332472 6.0347185,19.730911 5.8645842,19.135416 L 2.5286463,18.643229 C 2.3706576,18.618911 2.2430535,18.546014 2.1458338,18.424481 2.0486097,18.29081 1.9999999,18.144976 1.9999999,17.986979 l 0,-4.046874 c 0,-0.14582 0.04861,-0.285574 0.1458339,-0.419272 0.09722,-0.133665 0.2126736,-0.21266 0.3463529,-0.236978 L 5.882814,12.773438 C 6.0529484,12.214425 6.2899269,11.655399 6.5937495,11.096354 6.1076333,10.403662 5.4574623,9.5651225 4.6432275,8.5807307 4.5216985,8.4349149 4.4609339,8.2890809 4.4609385,8.1432288 4.4609339,8.0217224 4.5156234,7.8819635 4.6249977,7.7239612 4.9409706,7.286482 5.5364521,6.6363065 6.4114559,5.7734392 7.2986054,4.8984626 7.8758617,4.4609607 8.1432292,4.460938 c 0.1579796,2.27e-5 0.3159638,0.060787 0.473957,0.1822936 L 11.13281,6.5937491 c 0.534712,-0.2794906 1.087664,-0.510394 1.658854,-0.6927057 0.194434,-1.6527563 0.370648,-2.7829639 0.528646,-3.3906274 0.08506,-0.3402505 0.303809,-0.5103894 0.656251,-0.5104166 l 4.046873,0 c 0.170125,2.72e-5 0.315959,0.054717 0.437502,0.1640638 0.133666,0.097247 0.20658,0.224851 0.218749,0.3828125 l 0.510416,3.3541677 c 0.595468,0.1944665 1.142345,0.4192902 1.640625,0.6744804 l 2.588542,-1.950522 c 0.109357,-0.1093516 0.255186,-0.1640411 0.437502,-0.1640638 0.157962,2.27e-5 0.309871,0.060787 0.455728,0.1822936 1.567686,1.4462032 2.57029,2.4791866 3.007814,3.0989549 0.08504,0.097242 0.127577,0.2309261 0.127605,0.4010423 -2.8e-5,0.1458521 -0.04864,0.285611 -0.145834,0.4192721 -0.182317,0.2552264 -0.492214,0.6623438 -0.929689,1.2213521 -0.437525,0.546894 -0.765648,0.972242 -0.984374,1.276042 0.315946,0.607659 0.565079,1.20314 0.747395,1.786458 l 3.335938,0.510416 c 0.157957,0.02432 0.285561,0.103313 0.382813,0.236983 0.09719,0.121539 0.145802,0.261297 0.145834,0.419268"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/ui/app/img/mask.svg b/ui/app/img/mask.svg
new file mode 100644
index 00000000..f254c5e0
--- /dev/null
+++ b/ui/app/img/mask.svg
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="330"
+   height="174"
+   id="svg3088"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="mask.svg"
+   inkscape:export-filename="/home/elijah/dev/leap/leap_assets/svg/masks/favicon.png"
+   inkscape:export-xdpi="5.625"
+   inkscape:export-ydpi="5.625">
+  <defs
+     id="defs3090" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-53.46511"
+     inkscape:cy="109.89316"
+     inkscape:current-layer="outline"
+     showgrid="false"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:snap-global="false"
+     objecttolerance="20"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:snap-bbox-midpoints="true"
+     inkscape:object-paths="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-midpoints="true"
+     inkscape:snap-object-midpoints="true"
+     inkscape:snap-center="true"
+     borderlayer="true"
+     inkscape:window-width="1205"
+     inkscape:window-height="835"
+     inkscape:window-x="547"
+     inkscape:window-y="65"
+     inkscape:window-maximized="0"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3096"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       originx="93.682631"
+       originy="42.998542" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="1593.6826,42.998542"
+       id="guide3104" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3093">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="rainbow"
+     inkscape:groupmode="layer"
+     transform="translate(93.682631,67.00146)"
+     style="display:inline" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="mask"
+     style="display:inline"
+     transform="translate(93.682631,-124.99854)">
+    <g
+       style="display:inline"
+       id="mask_group"
+       transform="matrix(3.9239993,0,0,3.9239993,3.1838955,50.448418)">
+      <g
+         style="display:inline;fill:none;stroke:none"
+         id="eyes"
+         transform="matrix(0.09628644,0,0,0.09628644,-13.900612,-8.2957126)">
+        <path
+           inkscape:connector-curvature="0"
+           id="path3911"
+           d="m 290.22501,383.61826 c 20.26573,-0.36718 40.73803,7.19583 59.875,14.1875 16.78643,6.13291 31.49775,16.91475 46.90625,25.96875 10.4842,6.16049 30.96875,19.28125 30.96875,19.28125 0,0 -38.82581,37.01847 -66.03125,43.375 -32.32466,7.55263 -60.041,1.91037 -89.21875,-21.40625 -16.83219,-13.45099 -39.0625,-56.3125 -39.0625,-56.3125 0,0 30.15123,-21.96471 47.90625,-24.4375 2.88277,-0.40149 5.76115,-0.6038 8.65625,-0.65625 z"
+           style="fill:none;stroke:none" />
+        <path
+           inkscape:connector-curvature="0"
+           id="path3905"
+           d="m 662.31876,383.61826 c 3.62101,-0.0338 7.24028,0.15439 10.84375,0.65625 17.75502,2.47279 47.90625,24.4375 47.90625,24.4375 0,0 -22.23031,42.86151 -39.0625,56.3125 -29.17775,23.31662 -56.92534,28.95888 -89.25,21.40625 -27.20544,-6.35653 -66,-43.375 -66,-43.375 0,0 20.48455,-13.12076 30.96875,-19.28125 15.4085,-9.054 30.11982,-19.83584 46.90625,-25.96875 18.4535,-6.74197 38.13407,-14.00499 57.6875,-14.1875 z"
+           style="fill:none;stroke:none" />
+      </g>
+      <g
+         transform="matrix(0.09628644,0,0,0.09628644,-13.900612,-8.2957126)"
+         id="outline"
+         style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:34.52383041;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+        <path
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:34.52383041;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 15.279428,283.46896 c -25.395028,0.29417 -55.766384,5.34416 -78.180213,28.13341 -41.610535,42.30754 -51.078425,104.6937 -48.792595,157.09543 3.09273,70.89956 32.510261,141.31677 77.548216,194.99548 31.2063109,37.1934 76.153265,78.38052 133.88163,80.25276 47.504344,1.54066 79.677684,-32.88465 104.209624,-52.03872 20.47502,-15.98652 48.9415,-34.77084 73.785,-45.16092 23.14757,-9.6808 48.52598,-9.92084 46.79322,-9.92791 l 0.0516,0 0.18099,0 0.0374,0 c -0.66133,10e-4 23.72172,0.24775 46.86792,9.92791 24.83448,10.3863 53.25033,29.1637 73.73901,45.16092 24.54335,19.16296 56.75051,53.57941 104.25562,52.03872 57.72835,-1.87224 102.6753,-43.05936 133.88161,-80.25276 45.03797,-53.67871 74.4555,-124.09592 77.54823,-194.99548 2.28583,-52.40173 -7.18206,-114.78789 -48.7926,-157.09543 -23.93638,-24.33729 -56.72446,-28.56338 -83.25052,-28.08445 -48.19226,0.87016 -84.29897,26.42928 -118.66787,41.05093 -54.0567,22.99749 -87.02303,49.00558 -132.76414,63.00349 -21.77603,6.66401 -34.39981,5.08622 -52.7914,5.43773 0.056,0.003 -0.3257,-9.9e-4 -0.20684,0 l 0.0517,0.003 C 306.19197,392.6499 293.62549,394.2572 271.7817,387.57244 226.04064,373.57447 193.0743,347.56638 139.0176,324.56889 109.7526,312.1186 85.70157,293.58692 47.530399,287.26753 Z m 34.627384,178.97027 c 23.183949,-0.42116 50.684478,9.04508 78.892638,19.37771 20.17182,7.38892 40.67441,22.09527 64.84232,36.33321 4.96838,2.92699 8.62977,5.38188 15.53835,9.70038 -2.26657,1.82188 -3.6667,3.15806 -6.13031,5.0518 -18.1082,13.91961 -41.91482,28.10355 -53.89449,30.90989 C 107.47079,573.5771 77.906523,567.87377 39.556515,537.14768 33.193809,532.04987 18.009057,512.07472 7.2933506,494.69124 4.3472031,489.91186 2.3990912,486.35502 -0.00326604,482.19423 1.7100438,481.12731 2.8139795,480.32504 4.6648396,479.22192 17.902804,471.33198 35.387531,463.8423 40.217235,463.16791 c 3.107262,-0.43388 6.308905,-0.66727 9.689577,-0.72868 z m 547.057498,0.003 c 4.28838,-0.0401 8.3533,0.18703 12.2118,0.72581 4.82969,0.67438 22.31443,8.16408 35.5524,16.05399 1.85085,1.10314 2.95478,1.9054 4.6681,2.97234 -2.40237,4.16077 -4.35047,7.71762 -7.29663,12.49702 -10.71569,17.38346 -25.90044,37.3586 -32.26315,42.45641 -38.34309,30.72057 -67.96379,36.42857 -109.64478,26.66454 -11.97755,-2.80584 -35.76998,-16.98865 -53.86576,-30.907 -2.46056,-1.89253 -3.85789,-3.22816 -6.1217,-5.04893 6.91251,-4.32099 10.57578,-6.77746 15.54699,-9.70614 24.16791,-14.23793 44.67048,-28.94429 64.84232,-36.33321 27.19699,-9.96224 53.7382,-19.16304 76.37041,-19.37483 z"
+           id="path3871"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="cssssssccccssssssssscccssccssscsssscsssssscsssscsss" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/off.svg b/ui/app/img/off.svg
new file mode 100644
index 00000000..35c49a56
--- /dev/null
+++ b/ui/app/img/off.svg
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3243"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="off.svg">
+  <defs
+     id="defs3245" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16"
+     inkscape:cx="2.564547"
+     inkscape:cy="9.5625231"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:snap-global="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1056"
+     inkscape:window-x="1920"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3273" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3248">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f08080;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
+       d="m 26.607099,9.8590516 c 1.095245,1.8715414 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222445 -0.547656,4.274746 -1.642901,6.156901 -1.095291,1.87153 -2.584007,3.354927 -4.466146,4.450195 C 20.269405,27.702363 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.8430986,26.607098 7.9715637,25.51183 6.4881674,24.028433 5.3929018,22.156903 4.2976327,20.274748 3.75,18.222447 3.75,16.000002 3.75,13.777574 4.2976327,11.730593 5.3929018,9.8590516 6.4881674,7.9769071 7.9715637,6.4881926 9.8430986,5.392906 11.725251,4.2976563 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269405,0.5476563 6.140953,1.642906 1.882139,1.0952866 3.370855,2.5840011 4.466146,4.4661456"
+       id="path3275"
+       inkscape:connector-curvature="0" />
+    <g
+       id="g3758"
+       transform="matrix(0.865313,0,0,0.865313,2.163376,2.175981)">
+      <rect
+         transform="matrix(-0.70710678,-0.70710678,-0.70710678,0.70710678,0,0)"
+         style="fill:#000000;fill-opacity:1;stroke:none"
+         id="rect4057"
+         width="18"
+         height="3"
+         x="-31.781626"
+         y="-1.4338233"
+         ry="0" />
+      <rect
+         transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"
+         ry="0"
+         y="21.281626"
+         x="-9.0661755"
+         height="3"
+         width="18"
+         id="rect4059"
+         style="fill:#000000;fill-opacity:1;stroke:none" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/on.svg b/ui/app/img/on.svg
new file mode 100644
index 00000000..36938e0a
--- /dev/null
+++ b/ui/app/img/on.svg
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3243"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="on.svg">
+  <defs
+     id="defs3245" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="76.360017"
+     inkscape:cy="-4.203893"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:snap-global="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1056"
+     inkscape:window-x="1920"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3273" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3248">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#9acd32;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
+       d="m 26.607098,9.8590516 c 1.095246,1.8715424 1.642877,3.9185224 1.642902,6.1409504 -2.5e-5,2.222447 -0.547656,4.274744 -1.642902,6.156901 -1.095289,1.871529 -2.584005,3.354927 -4.466145,4.450195 C 20.269406,27.702363 18.222425,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.843099,26.607098 7.9715637,25.51183 6.4881673,24.028432 5.3929019,22.156903 4.2976327,20.274746 3.75,18.222449 3.75,16.000002 3.75,13.777574 4.2976327,11.730594 5.3929019,9.8590516 6.4881673,7.9769072 7.9715637,6.4881927 9.843099,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222425,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.88214,1.0952865 3.370856,2.584001 4.466145,4.4661454"
+       id="path3275"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="M 16.816995,19.463899 11.276562,13.923466 8.7414,16.458629 14.281832,21.999061 z"
+       id="rect4057"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 11.792505,19.521247 9.473539,-9.340956 2.535164,2.535163 -9.473539,9.340957 z"
+       id="rect4059"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc" />
+  </g>
+</svg>
diff --git a/ui/app/img/planet.svg b/ui/app/img/planet.svg
new file mode 100644
index 00000000..5697d5d6
--- /dev/null
+++ b/ui/app/img/planet.svg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3069"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="planet.svg">
+  <defs
+     id="defs3071" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="-15.657999"
+     inkscape:cy="16"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="1879"
+     inkscape:window-height="1056"
+     inkscape:window-x="1961"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata3074">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       inkscape:connector-curvature="0"
+       d="M 25.899531,6.1005234 C 28.543727,8.7447734 30,12.260523 30,16.000055 30,19.739586 28.543727,23.255281 25.899531,25.899531 23.255227,28.543727 19.739531,30 16,30 12.260469,30 8.7447734,28.543727 6.1004687,25.899531 3.4562734,23.255281 2,19.739586 2,16.000055 2,12.260523 3.4561641,8.7447734 6.1004687,6.1005234 8.7447734,3.4562734 12.260469,2 16,2 c 3.739531,0 7.255227,1.4562734 9.899531,4.1005234 z M 24.662062,24.662117 c 1.724243,-1.724297 2.870711,-3.872094 3.343375,-6.208726 -0.368429,0.54239 -0.720835,0.743586 -0.939093,-0.470477 -0.224766,-1.979414 -2.042797,-0.714984 -3.185985,-1.418047 -1.203234,0.810961 -3.907586,-1.576695 -3.447992,1.116281 0.709133,1.214664 3.828344,-1.625586 2.273578,0.944508 -0.991867,1.794188 -3.626929,5.767672 -3.284148,7.827367 0.04326,3.000813 -3.066164,0.625735 -4.137492,-0.369687 -0.720672,-1.993906 -0.245602,-5.478977 -2.130024,-6.455477 -2.045367,-0.08881 -3.8008904,-0.274695 -4.5935857,-2.561289 -0.477039,-1.635922 0.5076094,-4.07132 2.2607267,-4.447297 2.566211,-1.612351 3.482883,1.888196 5.88957,1.953274 0.74725,-0.781867 2.784031,-1.030477 2.952906,-1.907227 -1.579101,-0.278633 2.003422,-1.327758 -0.151156,-1.924508 -1.188633,0.139782 -1.954476,1.232493 -1.322562,2.159008 -2.303383,0.537086 -2.377157,-3.3333122 -4.591289,-2.112523 -0.05627,1.930195 -3.6154457,0.625789 -1.231454,0.23439 0.81911,-0.357875 -1.336015,-1.3949683 -0.171718,-1.2065151 0.571922,-0.031062 2.497359,-0.7057969 1.976297,-1.1594297 1.072148,-0.6655469 1.973125,1.5938668 3.022523,-0.051461 C 17.95218,7.3391953 16.876805,7.105625 15.927156,7.7468906 15.391766,7.1474062 16.87243,5.852625 18.178422,5.2931172 18.61368,5.1066328 19.029414,5.0050234 19.347258,5.0337891 20.005094,5.7937266 21.221672,5.9253594 21.285328,4.9424062 19.656133,4.1621797 17.859812,3.75 16,3.75 13.330648,3.75 10.791836,4.5983125 8.6907969,6.1669141 9.2554453,6.4255859 9.5759688,6.7476406 9.0319922,7.1593828 8.6093672,8.4186719 6.8945312,10.109117 5.3891484,9.8698047 4.6075,11.217742 4.0927266,12.702781 3.8726641,14.259242 c 1.260875,0.417156 1.5515937,1.242774 1.2806718,1.518946 -0.6424687,0.560218 -1.0373125,1.354335 -1.24075,2.223648 0.4104297,2.511141 1.5906407,4.825516 3.4253516,6.660281 C 9.6516563,26.975781 12.727883,28.25 16,28.25 c 3.272062,0 6.348344,-1.274219 8.662062,-3.587883 z"
+       id="path67" />
+  </g>
+</svg>
diff --git a/ui/app/img/unknown.svg b/ui/app/img/unknown.svg
new file mode 100644
index 00000000..60038638
--- /dev/null
+++ b/ui/app/img/unknown.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3243"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="question.svg">
+  <defs
+     id="defs3245" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="7.9999996"
+     inkscape:cx="12.965251"
+     inkscape:cy="9.8979426"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:snap-global="false"
+     inkscape:window-width="1328"
+     inkscape:window-height="840"
+     inkscape:window-x="1987"
+     inkscape:window-y="24"
+     inkscape:window-maximized="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3273" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3248">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f0e68c;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
+       d="m 26.607099,9.8590516 c 1.095245,1.8715424 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222446 -0.547656,4.274745 -1.642901,6.1569 -1.09529,1.871531 -2.584007,3.354928 -4.466146,4.450196 C 20.269406,27.702362 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702362 9.8430986,26.607098 7.9715638,25.51183 6.4881674,24.028433 5.392902,22.156902 4.2976328,20.274747 3.75,18.222448 3.75,16.000002 3.75,13.777574 4.2976328,11.730594 5.392902,9.8590516 6.4881674,7.9769072 7.9715638,6.4881927 9.8430986,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.882139,1.0952865 3.370856,2.584001 4.466146,4.4661454"
+       id="path3275"
+       inkscape:connector-curvature="0" />
+    <g
+       transform="matrix(0.93378097,0,0,0.80060225,2.1910082,2.2390946)"
+       style="font-size:24.20675278px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
+       id="text2987">
+      <path
+         style="font-weight:bold;font-family:Droid Sans;-inkscape-font-specification:Droid Sans Bold"
+         d="m 13.191315,20.187003 0,-0.859172 c -3e-6,-0.394747 0.03717,-0.750801 0.111525,-1.068159 0.08178,-0.325085 0.200741,-0.630826 0.35688,-0.917225 0.163567,-0.294121 0.371746,-0.576642 0.62454,-0.847561 0.252786,-0.27864 0.55762,-0.565031 0.914506,-0.859172 0.312264,-0.255419 0.576207,-0.483757 0.791827,-0.685016 0.21561,-0.201235 0.390332,-0.398613 0.524168,-0.592131 0.133823,-0.201235 0.230479,-0.410223 0.289965,-0.626963 0.05947,-0.216716 0.08921,-0.468275 0.08922,-0.754678 -7e-6,-0.441183 -0.144989,-0.804977 -0.434948,-1.09138 -0.282536,-0.286378 -0.717484,-0.429572 -1.304843,-0.429586 -0.513019,1.4e-5 -1.066927,0.112248 -1.661723,0.336702 -0.587368,0.224482 -1.197037,0.499262 -1.82901,0.82434 -1.453521,0.818948 -2.5794663,-1.530369 -1.137556,-2.565904 0.319705,-0.1934915 0.665432,-0.3753865 1.037183,-0.5456882 0.379184,-0.1780102 0.769521,-0.3328158 1.171013,-0.4644172 0.408922,-0.1393083 0.821564,-0.2476722 1.237927,-0.325092 0.416357,-0.077386 0.825282,-0.1160872 1.226776,-0.1161044 0.758365,1.72e-5 1.442385,0.096771 2.052061,0.2902608 0.609663,0.1857836 1.126395,0.4605635 1.550198,0.8243395 0.423786,0.3638075 0.747208,0.8050035 0.970268,1.3235875 0.230475,0.518614 0.345718,1.110745 0.345727,1.776396 -9e-6,0.48765 -0.05205,0.924976 -0.156135,1.311978 -0.104099,0.379285 -0.260234,0.735339 -0.468404,1.06816 -0.200755,0.325102 -0.457263,0.642454 -0.769524,0.952055 -0.304843,0.301881 -0.661723,0.623102 -1.070639,0.963666 -0.312278,0.255437 -0.568786,0.479906 -0.769524,0.673405 -0.193316,0.185774 -0.345734,0.363801 -0.457252,0.534079 -0.111531,0.170295 -0.189599,0.348321 -0.234203,0.534081 -0.03718,0.178033 -0.05576,0.390891 -0.05576,0.638574 l 0,0.696625 c -0.345863,2.197824 -2.53083,2.209243 -2.944261,0"
+         id="path2996"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="csccccccccccccccccccccccccccscscc" />
+      <path
+         style="font-weight:bold;font-family:Droid Sans;-inkscape-font-specification:Droid Sans Bold"
+         d="m 13.14689,25.354041 c -3e-6,-0.326839 0.03795,-0.601524 0.113851,-0.824056 0.08174,-0.229482 0.192668,-0.413764 0.332795,-0.552849 0.140122,-0.139078 0.303601,-0.239911 0.490435,-0.302501 0.192667,-0.06259 0.397016,-0.09388 0.613044,-0.09388 0.204344,3e-6 0.397016,0.03129 0.578014,0.09388 0.186828,0.06259 0.350305,0.163423 0.490434,0.302501 0.14012,0.139085 0.251052,0.323367 0.332796,0.552849 0.08174,0.222532 0.122603,0.497217 0.122609,0.824056 -6e-6,0.312934 -0.04088,0.580665 -0.122609,0.803194 -0.08175,0.222531 -0.192676,0.406813 -0.332796,0.552848 -0.140129,0.146036 -0.303606,0.250347 -0.490434,0.312933 -0.180998,0.06954 -0.37367,0.104311 -0.578014,0.104311 -0.216028,0 -0.420377,-0.03477 -0.613044,-0.104311 -0.186834,-0.06259 -0.350313,-0.166897 -0.490435,-0.312933 -0.140127,-0.146035 -0.251058,-0.330317 -0.332795,-0.552848 -0.07591,-0.222529 -0.113854,-0.49026 -0.113851,-0.803194"
+         id="path2992"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/user.svg b/ui/app/img/user.svg
new file mode 100644
index 00000000..ad86a757
--- /dev/null
+++ b/ui/app/img/user.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg4842"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="New document 23">
+  <defs
+     id="defs4844" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="11.197802"
+     inkscape:cx="16"
+     inkscape:cy="16"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:window-width="814"
+     inkscape:window-height="735"
+     inkscape:window-x="1104"
+     inkscape:window-y="24"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata4847">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       transform="matrix(0.42424241,0,0,0.45370367,-116.34456,-343.34663)"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="text3085">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 340.24077,812.48026 c -6e-5,3.21429 -0.97773,5.74553 -2.93304,7.59375 -1.9554,1.875 -4.55362,2.81249 -7.79464,2.8125 l -35.11607,0 c -3.24108,-1e-5 -5.83929,-0.9375 -7.79464,-2.8125 -1.95536,-1.84822 -2.93304,-4.37946 -2.93304,-7.59375 0,-1.41964 0.0402,-2.7991 0.12054,-4.13839 0.10714,-1.36606 0.29464,-2.82588 0.5625,-4.37947 0.29464,-1.58034 0.65625,-3.04016 1.08482,-4.37946 0.42857,-1.33927 1.00446,-2.63837 1.72768,-3.89732 0.72321,-1.28569 1.55356,-2.37051 2.49107,-3.25447 0.93749,-0.91069 2.07588,-1.6339 3.41518,-2.16964 1.36606,-0.53569 2.86606,-0.80354 4.5,-0.80357 0.24106,3e-5 0.80355,0.29467 1.6875,0.88393 0.88391,0.56252 1.87498,1.20538 2.97321,1.92857 1.12498,0.69645 2.57141,1.33931 4.33929,1.92857 1.79462,0.56252 3.58926,0.84377 5.38393,0.84375 1.79461,2e-5 3.57586,-0.28123 5.34375,-0.84375 1.7946,-0.58926 3.24103,-1.23212 4.33928,-1.92857 1.12496,-0.72319 2.12943,-1.36605 3.01339,-1.92857 0.88389,-0.58926 1.44639,-0.8839 1.6875,-0.88393 1.63389,3e-5 3.12049,0.26788 4.45983,0.80357 1.36602,0.53574 2.5178,1.25895 3.45535,2.16964 0.93745,0.88396 1.76781,1.96878 2.49107,3.25447 0.72317,1.25895 1.29906,2.55805 1.72768,3.89732 0.42852,1.3393 0.77673,2.79912 1.04465,4.37946 0.29458,1.55359 0.48208,3.01341 0.5625,4.37947 0.10708,1.33929 0.16065,2.71875 0.16071,4.13839 M 322.84345,765.7124 c 3.02674,3.00005 4.54013,6.62951 4.54018,10.8884 -5e-5,4.25896 -1.51344,7.90181 -4.54018,10.92857 -3.00004,3.00002 -6.6295,4.50002 -10.88839,4.5 -4.25896,2e-5 -7.90181,-1.49998 -10.92858,-4.5 -3.00001,-3.02676 -4.50001,-6.66961 -4.5,-10.92857 -10e-6,-4.25889 1.49999,-7.88835 4.5,-10.8884 3.02677,-3.02673 6.66962,-4.54012 10.92858,-4.54018 4.25889,6e-5 7.88835,1.51345 10.88839,4.54018"
+         id="path3195" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/img/wait.svg b/ui/app/img/wait.svg
new file mode 100644
index 00000000..2ae0113a
--- /dev/null
+++ b/ui/app/img/wait.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="32px"
+   height="32px"
+   id="svg3243"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="wait.svg">
+  <defs
+     id="defs3245" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8"
+     inkscape:cx="-30.255727"
+     inkscape:cy="10.507144"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     inkscape:snap-global="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1056"
+     inkscape:window-x="1920"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3273" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3248">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <path
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f0e68c;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
+       d="m 26.607099,9.8590516 c 1.095245,1.8715424 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222446 -0.547656,4.274745 -1.642901,6.1569 -1.09529,1.871531 -2.584007,3.354928 -4.466146,4.450196 C 20.269406,27.702362 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702362 9.8430986,26.607098 7.9715638,25.51183 6.4881674,24.028433 5.392902,22.156902 4.2976328,20.274747 3.75,18.222448 3.75,16.000002 3.75,13.777574 4.2976328,11.730594 5.392902,9.8590516 6.4881674,7.9769072 7.9715638,6.4881927 9.8430986,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.882139,1.0952865 3.370856,2.584001 4.466146,4.4661454"
+       id="path3275"
+       inkscape:connector-curvature="0" />
+    <g
+       transform="matrix(0.23090279,0,0,0.23090276,-12.029146,-19.708792)"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
+       id="text2993">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 151.24208,161.07719 c -6e-5,0.13394 -0.0135,0.22769 -0.0402,0.28125 -1.71434,7.17858 -5.30363,13.00447 -10.76786,17.47767 -5.46433,4.44643 -11.86611,6.66964 -19.20535,6.66965 -3.91075,-1e-5 -7.70092,-0.73661 -11.37054,-2.20982 -3.64287,-1.47322 -6.89733,-3.5759 -9.76339,-6.30804 l -5.183039,5.18304 c -0.508932,0.50892 -1.11161,0.76339 -1.808035,0.76339 -0.696431,0 -1.299109,-0.25447 -1.808036,-0.76339 -0.508929,-0.50893 -0.763393,-1.11161 -0.763393,-1.80804 l 0,-18 c 0,-0.69641 0.254464,-1.29909 0.763393,-1.80804 0.508927,-0.5089 1.111605,-0.76337 1.808036,-0.76339 l 18.000004,0 c 0.6964,2e-5 1.29908,0.25449 1.80803,0.76339 0.50891,0.50895 0.76337,1.11163 0.76339,1.80804 -2e-5,0.69645 -0.25448,1.29912 -0.76339,1.80804 l -5.50446,5.50446 c 1.90176,1.76787 4.05801,3.13394 6.46875,4.09821 2.41069,0.9643 4.91515,1.44644 7.51339,1.44643 3.58925,10e-6 6.93746,-0.87053 10.04464,-2.6116 3.1071,-1.74107 5.59817,-4.13839 7.47322,-7.19197 0.29459,-0.45534 1.00441,-2.0223 2.12946,-4.70089 0.21424,-0.61605 0.61602,-0.92409 1.20536,-0.92411 l 7.71428,0 c 0.34816,2e-5 0.6428,0.13395 0.88393,0.40179 0.2678,0.24109 0.40173,0.53573 0.40179,0.88393 m 1.00446,-32.14286 0,18 c -6e-5,0.69646 -0.25452,1.29914 -0.76339,1.80803 -0.50899,0.50896 -1.11167,0.76343 -1.80804,0.7634 l -18,0 c -0.69646,3e-5 -1.29914,-0.25444 -1.80803,-0.7634 -0.50897,-0.50889 -0.76343,-1.11157 -0.76339,-1.80803 -4e-5,-0.6964 0.25442,-1.29907 0.76339,-1.80804 l 5.54464,-5.54464 c -3.96433,-3.6696 -8.63843,-5.50442 -14.02232,-5.50446 -3.58931,4e-5 -6.93752,0.87058 -10.04464,2.6116 -3.10716,1.74112 -5.59823,4.13844 -7.47322,7.19197 -0.29465,0.45539 -1.00447,2.02235 -2.12946,4.70089 -0.2143,0.6161 -0.61608,0.92414 -1.20536,0.92411 l -7.995534,0 c -0.348216,3e-5 -0.656252,-0.12051 -0.924107,-0.36161 -0.241073,-0.26783 -0.361608,-0.57586 -0.361608,-0.92411 l 0,-0.28125 c 1.741069,-7.17853 5.357137,-12.99102 10.848219,-17.4375 5.49105,-4.47316 11.91962,-6.70976 19.28571,-6.70982 3.91068,6e-5 7.71425,0.75006 11.41071,2.25 3.69639,1.47327 6.97763,3.56255 9.84375,6.26786 l 5.22322,-5.18304 c 0.50887,-0.50887 1.11155,-0.76334 1.80803,-0.76339 0.69637,5e-5 1.29905,0.25452 1.80804,0.76339 0.50887,0.50898 0.76333,1.11166 0.76339,1.80804"
+         id="path3126" />
+    </g>
+  </g>
+</svg>
diff --git a/ui/app/index.html b/ui/app/index.html
new file mode 100644
index 00000000..440e0b6f
--- /dev/null
+++ b/ui/app/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Bitmask</title>
+    <link rel="stylesheet" href="css/bootstrap.min.css">
+    <link rel="stylesheet" href="css/common.css">
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="app.bundle.js"></script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/ui/app/lib/bitmask.js b/ui/app/lib/bitmask.js
new file mode 100644
index 00000000..fedd5fcd
--- /dev/null
+++ b/ui/app/lib/bitmask.js
@@ -0,0 +1,306 @@
+// bitmask.js
+// Copyright (C) 2016 LEAP
+//
+// This program 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 of the License, or
+// (at your option) any later version.
+//
+// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * bitmask object
+ *
+ * Contains all the bitmask API mapped by sections
+ * - user. User management like login, creation, ...
+ * - mail. Email service control.
+ * - keys. Keyring operations.
+ * - events. For registering to events.
+ *
+ * Every function returns a Promise that will be triggered once the request is
+ * finished or will fail if there was any error. Errors are always user readable
+ * strings.
+ */
+
+try {
+    // Use Promises in non-ES6 compliant engines.
+    eval('import "babel-polyfill";')
+}
+catch (err) {}
+
+var bitmask = function(){
+    var event_handlers = {};
+
+    var api_url = '/API/';
+    if (window.location.protocol === "file:") {
+        api_url = 'http://localhost:7070/API/';
+    }
+    
+    function call(command) {
+        var url = api_url  + command.slice(0, 2).join('/');
+        var data = JSON.stringify(command.slice(2));
+
+        return new Promise(function(resolve, reject) {
+            var req = new XMLHttpRequest();
+            req.open('POST', url);
+            
+            req.onload = function() {
+                if (req.status == 200) {
+                    parseResponse(req.response, resolve, reject);
+                }
+                else {
+                    reject(Error(req.statusText));
+                }
+            };
+            
+            req.onerror = function() {
+                reject(Error("Network Error"));
+            };
+            
+            req.send(data);
+        });
+    };
+
+    function parseResponse(raw_response, resolve, reject) {
+        var response = JSON.parse(raw_response);
+        if (response.error === null) {
+            resolve(response.result);
+        } else {
+            reject(response.error);
+        }
+    };
+
+    function event_polling() {
+        call(['events', 'poll']).then(function(response) {
+            if (response !== null) {
+                evnt = response[0];
+                content = response[1];
+                if (evnt in event_handlers) {
+                    event_handlers[evnt](evnt, content);
+                }
+            }
+            event_polling();
+        }, function(error) {
+            setTimeout(event_polling, 5000);
+        });
+    };
+    event_polling();
+
+    function private_str(priv) {
+        if (priv) {
+            return 'private'
+        }
+        return 'public'
+    };
+
+    return {
+        bonafide: {
+            provider: {
+                create: function(domain) {
+                    return call(['bonafide', 'provider', 'create', domain]);
+                },
+
+                read: function(domain) {
+                    return call(['bonafide', 'provider', 'read', domain]);
+                },
+
+                delete: function(domain) {
+                    return call(['bonafide', 'provider', 'delete', domain]);
+                },
+
+                list: function(seeded) {
+                    if (typeof seeded !== 'boolean') {
+                        seeded = false;
+                    }
+                    return call(['bonafide', 'provider', 'list', seeded]);
+                }
+            },
+
+            /**
+             * uids are of the form user@provider.net
+             */
+            user: {
+                /**
+                 * Check wich user is active
+                 *
+                 * @return {Promise<string>} The uid of the active user
+                 */
+                active: function() {
+                    return call(['bonafide', 'user', 'active']);
+                },
+
+                /**
+                 * Register a new user
+                 *
+                 * @param {string} uid The uid to be created
+                 * @param {string} password The user password
+                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known
+                 *                           If it's not provided it will default to false
+                 */
+                create: function(uid, password, autoconf) {
+                    if (typeof autoconf !== 'boolean') {
+                        autoconf = false;
+                    }
+                    return call(['bonafide', 'user', 'create', uid, password, autoconf]);
+                },
+
+                /**
+                 * Login
+                 *
+                 * @param {string} uid The uid to log in
+                 * @param {string} password The user password
+                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known
+                 *                           If it's not provided it will default to false
+                 */
+                auth: function(uid, password, autoconf) {
+                    if (typeof autoconf !== 'boolean') {
+                        autoconf = false;
+                    }
+                    return call(['bonafide', 'user', 'authenticate', uid, password, autoconf]);
+                },
+
+                /**
+                 * Logout
+                 *
+                 * @param {string} uid The uid to log out.
+                 *                     If no uid is provided the active user will be used
+                 */
+                logout: function(uid) {
+                    if (typeof uid !== 'string') {
+                        uid = "";
+                    }
+                    return call(['bonafide', 'user', 'logout', uid]);
+                }
+            }
+        },
+
+        mail: {
+            /**
+             * Check the status of the email service
+             *
+             * @return {Promise<string>} User readable status
+             */
+            status: function() {
+                return call(['mail', 'status']);
+            },
+
+            /**
+             * Get the token of the active user.
+             *
+             * This token is used as password to authenticate in the IMAP and SMTP services.
+             *
+             * @return {Promise<string>} The token
+             */
+            get_token: function() {
+                return call(['mail', 'get_token']);
+            }
+        },
+
+        /**
+         * A KeyObject have the following attributes:
+         *   - address {string} the email address for wich this key is active
+         *   - fingerprint {string} the fingerprint of the key
+         *   - length {number} the size of the key bits
+         *   - private {bool} if the key is private
+         *   - uids {[string]} the uids in the key
+         *   - key_data {string} the key content
+         *   - validation {string} the validation level which this key was found
+         *   - expiry_date {string} date when the key expires
+         *   - refreshed_at {string} date of the last refresh of the key
+         *   - audited_at {string} date of the last audit (unused for now)
+         *   - sign_used {bool} if has being used to checking signatures
+         *   - enc_used {bool} if has being used to encrypt
+         */
+        keys: {
+            /**
+             * List all the keys in the keyring
+             *
+             * @param {boolean} priv Should list private keys?
+             *                       If it's not provided the public ones will be listed.
+             *
+             * @return {Promise<[KeyObject]>} List of keys in the keyring
+             */
+            list: function(priv) {
+                return call(['keys', 'list', private_str(priv)]);
+            },
+
+            /**
+             * Export key
+             *
+             * @param {string} address The email address of the key
+             * @param {boolean} priv Should get the private key?
+             *                       If it's not provided the public one will be fetched.
+             *
+             * @return {Promise<KeyObject>} The key
+             */
+            exprt: function(address, priv) {
+                return call(['keys', 'export', address, private_str(priv)]);
+            },
+
+            /**
+             * Insert key
+             *
+             * @param {string} address The email address of the key
+             * @param {string} rawkey The key material
+             * @param {string} validation The validation level of the key
+             *                            If it's not provided 'Fingerprint' level will be used.
+             *
+             * @return {Promise<KeyObject>} The key
+             */
+            insert: function(address, rawkey, validation) {
+                if (typeof validation !== 'string') {
+                    validation = 'Fingerprint';
+                }
+                return call(['keys', 'insert', address, validation, rawkey]);
+            },
+
+            /**
+             * Delete a key
+             *
+             * @param {string} address The email address of the key
+             * @param {boolean} priv Should get the private key?
+             *                       If it's not provided the public one will be deleted.
+             *
+             * @return {Promise<KeyObject>} The key
+             */
+            del: function(address, priv) {
+                return call(['keys', 'delete', address, private_str(priv)]);
+            }
+        },
+
+        events: {
+            /**
+             * Register func for an event
+             *
+             * @param {string} evnt The event to register
+             * @param {function} func The function that will be called on each event.
+             *                        It has to be like: function(event, content) {}
+             *                        Where content will be a list of strings.
+             */
+            register: function(evnt, func) {
+                event_handlers[evnt] = func;
+                return call(['events', 'register', evnt])
+            },
+
+            /**
+             * Unregister from an event
+             *
+             * @param {string} evnt The event to unregister
+             */
+            unregister: function(evnt) {
+                delete event_handlers[evnt];
+                return call(['events', 'unregister', evnt])
+            }
+        }
+    };
+}();
+
+try {
+    module.exports = bitmask
+} catch(err) {}
diff --git a/ui/app/lib/color.js b/ui/app/lib/color.js
new file mode 100644
index 00000000..5b1dfee9
--- /dev/null
+++ b/ui/app/lib/color.js
@@ -0,0 +1,65 @@
+//
+// Color.hsv().css
+//
+// RGB values are 0..255
+// HSV values are 0..1
+//
+
+function compose(value)
+  return [
+    Math.round(value * 255),
+    Math.round(value * 255),
+    Math.round(value * 255)
+  }
+}
+
+class Color {
+
+  constructor(r, g, b, a) {
+    this.r = r
+    this.g = g
+    this.b = b
+    this.a = a
+  }
+
+  //
+  // alternate hsv factory
+  //
+  static hsv(h,s,v) {
+    let out = null
+    h = h % 360;
+    s = Math.max(0, Math.min(1, s))
+    v = Math.max(0, Math.min(1, v))
+
+    if (s == 0) {
+      let grey = Math.ceil(v*255)
+      out = [grey, grey, grey]
+    }
+
+    let b = ((1 - s) * v);
+    let vb = v - b;
+    let hm = h % 60;
+    switch((h/60)|0) {
+      case 0:
+        out = compose(v, vb * h / 60 + b, b); break
+      case 1:
+        out = compose(vb * (60 - hm) / 60 + b, v, b); break
+      case 2:
+        out = compose(b, v, vb * hm / 60 + b); break
+      case 3:
+        out = compose(b, vb * (60 - hm) / 60 + b, v); break
+      case 4:
+        out = compose(vb * hm / 60 + b, b, v); break
+      case 5:
+        out = compose(v, b, vb * (60 - hm) / 60 + b); break
+    }
+
+    return new Color(...out)
+  }
+
+  css() {
+    return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`
+  }
+}
+
+export default Color
\ No newline at end of file
diff --git a/ui/app/lib/colors.js b/ui/app/lib/colors.js
new file mode 100644
index 00000000..d86ca0a8
--- /dev/null
+++ b/ui/app/lib/colors.js
@@ -0,0 +1,289 @@
+//
+// a palette from google's material design
+//
+
+export const red50 = '#ffebee';
+export const red100 = '#ffcdd2';
+export const red200 = '#ef9a9a';
+export const red300 = '#e57373';
+export const red400 = '#ef5350';
+export const red500 = '#f44336';
+export const red600 = '#e53935';
+export const red700 = '#d32f2f';
+export const red800 = '#c62828';
+export const red900 = '#b71c1c';
+export const redA100 = '#ff8a80';
+export const redA200 = '#ff5252';
+export const redA400 = '#ff1744';
+export const redA700 = '#d50000';
+
+export const pink50 = '#fce4ec';
+export const pink100 = '#f8bbd0';
+export const pink200 = '#f48fb1';
+export const pink300 = '#f06292';
+export const pink400 = '#ec407a';
+export const pink500 = '#e91e63';
+export const pink600 = '#d81b60';
+export const pink700 = '#c2185b';
+export const pink800 = '#ad1457';
+export const pink900 = '#880e4f';
+export const pinkA100 = '#ff80ab';
+export const pinkA200 = '#ff4081';
+export const pinkA400 = '#f50057';
+export const pinkA700 = '#c51162';
+
+export const purple50 = '#f3e5f5';
+export const purple100 = '#e1bee7';
+export const purple200 = '#ce93d8';
+export const purple300 = '#ba68c8';
+export const purple400 = '#ab47bc';
+export const purple500 = '#9c27b0';
+export const purple600 = '#8e24aa';
+export const purple700 = '#7b1fa2';
+export const purple800 = '#6a1b9a';
+export const purple900 = '#4a148c';
+export const purpleA100 = '#ea80fc';
+export const purpleA200 = '#e040fb';
+export const purpleA400 = '#d500f9';
+export const purpleA700 = '#aa00ff';
+
+export const deepPurple50 = '#ede7f6';
+export const deepPurple100 = '#d1c4e9';
+export const deepPurple200 = '#b39ddb';
+export const deepPurple300 = '#9575cd';
+export const deepPurple400 = '#7e57c2';
+export const deepPurple500 = '#673ab7';
+export const deepPurple600 = '#5e35b1';
+export const deepPurple700 = '#512da8';
+export const deepPurple800 = '#4527a0';
+export const deepPurple900 = '#311b92';
+export const deepPurpleA100 = '#b388ff';
+export const deepPurpleA200 = '#7c4dff';
+export const deepPurpleA400 = '#651fff';
+export const deepPurpleA700 = '#6200ea';
+
+export const indigo50 = '#e8eaf6';
+export const indigo100 = '#c5cae9';
+export const indigo200 = '#9fa8da';
+export const indigo300 = '#7986cb';
+export const indigo400 = '#5c6bc0';
+export const indigo500 = '#3f51b5';
+export const indigo600 = '#3949ab';
+export const indigo700 = '#303f9f';
+export const indigo800 = '#283593';
+export const indigo900 = '#1a237e';
+export const indigoA100 = '#8c9eff';
+export const indigoA200 = '#536dfe';
+export const indigoA400 = '#3d5afe';
+export const indigoA700 = '#304ffe';
+
+export const blue50 = '#e3f2fd';
+export const blue100 = '#bbdefb';
+export const blue200 = '#90caf9';
+export const blue300 = '#64b5f6';
+export const blue400 = '#42a5f5';
+export const blue500 = '#2196f3';
+export const blue600 = '#1e88e5';
+export const blue700 = '#1976d2';
+export const blue800 = '#1565c0';
+export const blue900 = '#0d47a1';
+export const blueA100 = '#82b1ff';
+export const blueA200 = '#448aff';
+export const blueA400 = '#2979ff';
+export const blueA700 = '#2962ff';
+
+export const lightBlue50 = '#e1f5fe';
+export const lightBlue100 = '#b3e5fc';
+export const lightBlue200 = '#81d4fa';
+export const lightBlue300 = '#4fc3f7';
+export const lightBlue400 = '#29b6f6';
+export const lightBlue500 = '#03a9f4';
+export const lightBlue600 = '#039be5';
+export const lightBlue700 = '#0288d1';
+export const lightBlue800 = '#0277bd';
+export const lightBlue900 = '#01579b';
+export const lightBlueA100 = '#80d8ff';
+export const lightBlueA200 = '#40c4ff';
+export const lightBlueA400 = '#00b0ff';
+export const lightBlueA700 = '#0091ea';
+
+export const cyan50 = '#e0f7fa';
+export const cyan100 = '#b2ebf2';
+export const cyan200 = '#80deea';
+export const cyan300 = '#4dd0e1';
+export const cyan400 = '#26c6da';
+export const cyan500 = '#00bcd4';
+export const cyan600 = '#00acc1';
+export const cyan700 = '#0097a7';
+export const cyan800 = '#00838f';
+export const cyan900 = '#006064';
+export const cyanA100 = '#84ffff';
+export const cyanA200 = '#18ffff';
+export const cyanA400 = '#00e5ff';
+export const cyanA700 = '#00b8d4';
+
+export const teal50 = '#e0f2f1';
+export const teal100 = '#b2dfdb';
+export const teal200 = '#80cbc4';
+export const teal300 = '#4db6ac';
+export const teal400 = '#26a69a';
+export const teal500 = '#009688';
+export const teal600 = '#00897b';
+export const teal700 = '#00796b';
+export const teal800 = '#00695c';
+export const teal900 = '#004d40';
+export const tealA100 = '#a7ffeb';
+export const tealA200 = '#64ffda';
+export const tealA400 = '#1de9b6';
+export const tealA700 = '#00bfa5';
+
+export const green50 = '#e8f5e9';
+export const green100 = '#c8e6c9';
+export const green200 = '#a5d6a7';
+export const green300 = '#81c784';
+export const green400 = '#66bb6a';
+export const green500 = '#4caf50';
+export const green600 = '#43a047';
+export const green700 = '#388e3c';
+export const green800 = '#2e7d32';
+export const green900 = '#1b5e20';
+export const greenA100 = '#b9f6ca';
+export const greenA200 = '#69f0ae';
+export const greenA400 = '#00e676';
+export const greenA700 = '#00c853';
+
+export const lightGreen50 = '#f1f8e9';
+export const lightGreen100 = '#dcedc8';
+export const lightGreen200 = '#c5e1a5';
+export const lightGreen300 = '#aed581';
+export const lightGreen400 = '#9ccc65';
+export const lightGreen500 = '#8bc34a';
+export const lightGreen600 = '#7cb342';
+export const lightGreen700 = '#689f38';
+export const lightGreen800 = '#558b2f';
+export const lightGreen900 = '#33691e';
+export const lightGreenA100 = '#ccff90';
+export const lightGreenA200 = '#b2ff59';
+export const lightGreenA400 = '#76ff03';
+export const lightGreenA700 = '#64dd17';
+
+export const lime50 = '#f9fbe7';
+export const lime100 = '#f0f4c3';
+export const lime200 = '#e6ee9c';
+export const lime300 = '#dce775';
+export const lime400 = '#d4e157';
+export const lime500 = '#cddc39';
+export const lime600 = '#c0ca33';
+export const lime700 = '#afb42b';
+export const lime800 = '#9e9d24';
+export const lime900 = '#827717';
+export const limeA100 = '#f4ff81';
+export const limeA200 = '#eeff41';
+export const limeA400 = '#c6ff00';
+export const limeA700 = '#aeea00';
+
+export const yellow50 = '#fffde7';
+export const yellow100 = '#fff9c4';
+export const yellow200 = '#fff59d';
+export const yellow300 = '#fff176';
+export const yellow400 = '#ffee58';
+export const yellow500 = '#ffeb3b';
+export const yellow600 = '#fdd835';
+export const yellow700 = '#fbc02d';
+export const yellow800 = '#f9a825';
+export const yellow900 = '#f57f17';
+export const yellowA100 = '#ffff8d';
+export const yellowA200 = '#ffff00';
+export const yellowA400 = '#ffea00';
+export const yellowA700 = '#ffd600';
+
+export const amber50 = '#fff8e1';
+export const amber100 = '#ffecb3';
+export const amber200 = '#ffe082';
+export const amber300 = '#ffd54f';
+export const amber400 = '#ffca28';
+export const amber500 = '#ffc107';
+export const amber600 = '#ffb300';
+export const amber700 = '#ffa000';
+export const amber800 = '#ff8f00';
+export const amber900 = '#ff6f00';
+export const amberA100 = '#ffe57f';
+export const amberA200 = '#ffd740';
+export const amberA400 = '#ffc400';
+export const amberA700 = '#ffab00';
+
+export const orange50 = '#fff3e0';
+export const orange100 = '#ffe0b2';
+export const orange200 = '#ffcc80';
+export const orange300 = '#ffb74d';
+export const orange400 = '#ffa726';
+export const orange500 = '#ff9800';
+export const orange600 = '#fb8c00';
+export const orange700 = '#f57c00';
+export const orange800 = '#ef6c00';
+export const orange900 = '#e65100';
+export const orangeA100 = '#ffd180';
+export const orangeA200 = '#ffab40';
+export const orangeA400 = '#ff9100';
+export const orangeA700 = '#ff6d00';
+
+export const deepOrange50 = '#fbe9e7';
+export const deepOrange100 = '#ffccbc';
+export const deepOrange200 = '#ffab91';
+export const deepOrange300 = '#ff8a65';
+export const deepOrange400 = '#ff7043';
+export const deepOrange500 = '#ff5722';
+export const deepOrange600 = '#f4511e';
+export const deepOrange700 = '#e64a19';
+export const deepOrange800 = '#d84315';
+export const deepOrange900 = '#bf360c';
+export const deepOrangeA100 = '#ff9e80';
+export const deepOrangeA200 = '#ff6e40';
+export const deepOrangeA400 = '#ff3d00';
+export const deepOrangeA700 = '#dd2c00';
+
+export const brown50 = '#efebe9';
+export const brown100 = '#d7ccc8';
+export const brown200 = '#bcaaa4';
+export const brown300 = '#a1887f';
+export const brown400 = '#8d6e63';
+export const brown500 = '#795548';
+export const brown600 = '#6d4c41';
+export const brown700 = '#5d4037';
+export const brown800 = '#4e342e';
+export const brown900 = '#3e2723';
+
+export const blueGrey50 = '#eceff1';
+export const blueGrey100 = '#cfd8dc';
+export const blueGrey200 = '#b0bec5';
+export const blueGrey300 = '#90a4ae';
+export const blueGrey400 = '#78909c';
+export const blueGrey500 = '#607d8b';
+export const blueGrey600 = '#546e7a';
+export const blueGrey700 = '#455a64';
+export const blueGrey800 = '#37474f';
+export const blueGrey900 = '#263238';
+
+export const grey50 = '#fafafa';
+export const grey100 = '#f5f5f5';
+export const grey200 = '#eeeeee';
+export const grey300 = '#e0e0e0';
+export const grey400 = '#bdbdbd';
+export const grey500 = '#9e9e9e';
+export const grey600 = '#757575';
+export const grey700 = '#616161';
+export const grey800 = '#424242';
+export const grey900 = '#212121';
+
+export const black = '#000000';
+export const white = '#ffffff';
+
+export const transparent = 'rgba(0, 0, 0, 0)';
+export const fullBlack = 'rgba(0, 0, 0, 1)';
+export const darkBlack = 'rgba(0, 0, 0, 0.87)';
+export const lightBlack = 'rgba(0, 0, 0, 0.54)';
+export const minBlack = 'rgba(0, 0, 0, 0.26)';
+export const faintBlack = 'rgba(0, 0, 0, 0.12)';
+export const fullWhite = 'rgba(255, 255, 255, 1)';
+export const darkWhite = 'rgba(255, 255, 255, 0.87)';
+export const lightWhite = 'rgba(255, 255, 255, 0.54)';
\ No newline at end of file
diff --git a/ui/app/lib/common.js b/ui/app/lib/common.js
new file mode 100644
index 00000000..14a30c32
--- /dev/null
+++ b/ui/app/lib/common.js
@@ -0,0 +1,7 @@
+/*
+ * pollute the global namespace with some useful utils
+ */
+
+import React from 'react'
+
+window.elem = React.createElement
\ No newline at end of file
diff --git a/ui/app/lib/validate.js b/ui/app/lib/validate.js
new file mode 100644
index 00000000..4e672e8f
--- /dev/null
+++ b/ui/app/lib/validate.js
@@ -0,0 +1,82 @@
+// https://github.com/dropbox/zxcvbn
+
+var DOMAIN_RE = /^((?:(?:(?:\w[\.\-\+]?)*)\w)+)((?:(?:(?:\w[\.\-\+]?){0,62})\w)+)\.(\w{2,6})$/
+var USER_RE   = /^[a-z0-9_-]{1,200}$/
+var USER_INT_RE = /^[\.@a-z0-9_-]*$/
+
+//
+// Validations returns an error message or false if no errors
+//
+
+class Validations {
+
+  domain(input) {
+    if (!input.match(DOMAIN_RE)) {
+      return "Not a domain name"
+    } else {
+      return null
+    }
+  }
+
+  usernameInteractive(input) {
+    if (!input.match(USER_INT_RE)) {
+      return "Username contains an invalid character"
+    }
+    return false
+  }
+
+  username(input) {
+    if (!input.match(USER_INT_RE)) {
+      return "Username contains an invalid character"
+    }
+    if (!input.match('@')) {
+      return "Username must be in the form username@domain"
+    }
+    let parts      = input.split('@')
+    let userpart   = parts[0]
+    let domainpart = parts[1]
+    if (!userpart.match(USER_RE)) {
+      return "Username contains an invalid character"
+    } else if (!domainpart.match(DOMAIN_RE)) {
+      return "Username must include a valid domain name."
+    }
+    return false
+  }
+
+  passwordStrength(passwd) {
+    if (typeof(zxcvbn) == 'function') {
+      // zxcvbn performs very slow on long strings, so we cap
+      // the calculation at 30 characters
+      return zxcvbn(passwd.substring(0,30))
+    } else {
+      return null
+    }
+  }
+
+  //
+  // loads the zxcvbn library. because this library is big, we don't load it
+  // every time, just when needed.
+  //
+  // this is the webpack way to do this:
+  //
+  //    require.ensure([], function () {
+  //      var zxcvbn = require('zxcvbn');
+  //    });
+  //
+  // that works, but requires that we also process the original coffeescript
+  // source if we want to avoid warning messages.
+  //
+  loadPasswdLib(onload) {
+    var id = "zxcvbn-script"
+    if (!document.getElementById(id)) {
+      var script = document.createElement('script')
+      script.id = id
+      script.onload = onload
+      script.src = './js/zxcvbn.js'
+      document.getElementsByTagName('script')[0].appendChild(script)
+    }
+  }
+}
+
+var Validate = new Validations()
+export default Validate
diff --git a/ui/app/main.js b/ui/app/main.js
new file mode 100644
index 00000000..b1628953
--- /dev/null
+++ b/ui/app/main.js
@@ -0,0 +1,26 @@
+//
+// main entry point for app execution
+//
+// This is determined by the 'entry' option in webpack.config.js
+//
+
+import React from 'react'
+import ReactDOM from 'react-dom'
+
+import PanelSwitcher from 'components/panel_switcher'
+import App from 'app'
+
+class Main extends React.Component {
+  render() {
+    return React.createElement(PanelSwitcher)
+  }
+
+  componentDidMount() {
+    App.start()
+  }
+}
+
+ReactDOM.render(
+  React.createElement(Main),
+  document.getElementById('app')
+)
diff --git a/ui/app/models/account.js b/ui/app/models/account.js
new file mode 100644
index 00000000..52fea93d
--- /dev/null
+++ b/ui/app/models/account.js
@@ -0,0 +1,143 @@
+//
+// An account is an abstraction of a user and a provider.
+// The user part is optional, so an Account might just represent a provider.
+//
+
+import bitmask from 'lib/bitmask'
+
+export default class Account {
+
+  constructor(address, props={}) {
+    this.address = address
+    this._authenticated = props.authenticated
+  }
+
+  //
+  // currently, bitmask.js uses address for id, so we return address here too.
+  // also, we don't know uuid until after authentication.
+  //
+  // TODO: change to uuid when possible.
+  //
+  get id() {
+    return this._address
+  }
+
+  get domain() {
+    return this._address.split('@')[1]
+  }
+
+  get address() {
+    return this._address
+  }
+
+  set address(address) {
+    if (!address.match('@')) {
+      this._address = '@' + address
+    } else {
+      this._address = address
+    }
+  }
+
+  get userpart() {
+    return this._address.split('@')[0]
+  }
+
+  get authenticated() {
+    return this._authenticated
+  }
+
+  get hasEmail() {
+    return true
+  }
+
+  //
+  // returns a promise, fulfill is passed account object
+  //
+  login(password) {
+    return bitmask.bonafide.user.auth(this.address, password).then(
+      response => {
+        if (response.uuid) {
+          this._uuid = response.uuid
+          this._authenticated = true
+        }
+        return this
+      }
+    )
+  }
+
+  //
+  // returns a promise, fulfill is passed account object
+  //
+  logout() {
+    return bitmask.bonafide.user.logout(this.id).then(
+      response => {
+        this._authenticated = false
+        this._address = '@' + this.domain
+        return this
+      }
+    )
+  }
+
+  //
+  // returns the matching account in the list of accounts, or adds it
+  // if it is not already present.
+  //
+  static find(address) {
+    // search by full address
+    let account = Account.list.find(i => {
+      return i.address == address
+    })
+    // failing that, search by domain
+    if (!account) {
+      let domain = '@' + address.split('@')[1]
+      account = Account.list.find(i => {
+        return i.address == domain
+      })
+      if (account) {
+        account.address = address
+      }
+    }
+    // failing that, create new account
+    if (!account) {
+      account = new Account(address)
+      Account.list.push(account)
+    }
+    return account
+  }
+
+  //
+  // returns a promise, fullfill is passed account object
+  //
+  static active() {
+    return bitmask.bonafide.user.active().then(
+      response => {
+        if (response.user == '<none>') {
+          return null
+        } else {
+          return new Account(response.user, {authenticated: true})
+        }
+      }
+    )
+  }
+
+  static add(account) {
+    if (!Account.list.find(i => {return i.id == account.id})) {
+      Account.list.push(account)
+    }
+  }
+
+  static remove(account) {
+    Account.list = Account.list.filter(i => {
+      return i.id != account.id
+    })
+  }
+  //   return Account.list
+  //   return new Promise(function(resolve, reject) {
+  //     window.setTimeout(function() {
+  //       resolve(['@blah', '@lala'])
+  //     }, 1000)
+  //   })
+  // }
+}
+
+Account.list = []
diff --git a/ui/app/models/dummy_account.js b/ui/app/models/dummy_account.js
new file mode 100644
index 00000000..99fb6623
--- /dev/null
+++ b/ui/app/models/dummy_account.js
@@ -0,0 +1,33 @@
+//
+// A proxy of an account, but with a different ID. For testing.
+//
+
+import bitmask from 'lib/bitmask'
+
+export default class DummyAccount {
+
+  constructor(account) {
+    this.account = account
+  }
+
+  get id() {
+    return 'dummy--' + this.account.address
+  }
+
+  get domain() {return this.account.domain}
+  get address() {return this.account.address}
+  get userpart() {return this.account.userpart}
+  get authenticated() {return this.account.authenticated}
+  get hasEmail() {return this.account.hasEmail}
+  login(password) {return this.account.login(password)}
+
+  logout() {
+    return bitmask.bonafide.user.logout(this.address).then(
+      response => {
+        this._authenticated = false
+        this._address = '@' + this.domain
+        return this
+      }
+    )
+  }
+}
diff --git a/ui/package.json b/ui/package.json
new file mode 100644
index 00000000..d491edd8
--- /dev/null
+++ b/ui/package.json
@@ -0,0 +1,34 @@
+{
+  "name": "bitmask_js",
+  "version": "0.0.1",
+  "description": "bitmask user interface in javascript",
+  "license": "GPL-3.0",
+  "homepage": "https://bitmask.net",
+  "repository": "https://leap.se/git/bitmask_client.git",
+  "dependencies": {},
+  "devDependencies": {
+    "babel": "^6.5.2",
+    "babel-loader": "^6.2.4",
+    "babel-polyfill": "^6.13.0",
+    "babel-preset-es2015": "^6.9.0",
+    "babel-preset-react": "^6.11.1",
+    "babel-preset-stage-0": "^6.5.0",
+    "bootstrap": "^3.3.7",
+    "copy-webpack-plugin": "^3.0.1",
+    "css-loader": "^0.23.1",
+    "less": "^2.7.1",
+    "less-loader": "^2.2.3",
+    "react": "^15.2.1",
+    "react-bootstrap": "^0.30.2",
+    "react-dom": "^15.2.1",
+    "style-loader": "^0.13.1",
+    "webpack": "^1.13.1",
+    "zxcvbn": "^4.3.0"
+  },
+  "scripts": {
+    "open": "gnome-open http://localhost:7070",
+    "watch": "NODE_ENV=development webpack --watch",
+    "build": "NODE_ENV=development webpack",
+    "build:production": "NODE_ENV=production webpack"
+  }
+}
diff --git a/ui/pydist/README.md b/ui/pydist/README.md
new file mode 100644
index 00000000..6e2b11f6
--- /dev/null
+++ b/ui/pydist/README.md
@@ -0,0 +1,4 @@
+This directory holds the python package of the javascript app, called 'bitmask_js'.
+
+Why it it done this way? By creating a python package, it is easier for the
+javascript app to be distributed with the bitmask client.
diff --git a/ui/pydist/setup.py b/ui/pydist/setup.py
new file mode 100644
index 00000000..b5d3ffb1
--- /dev/null
+++ b/ui/pydist/setup.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# setup.py
+# Copyright (C) 2016 LEAP Encryption Acess Project
+#
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Setup file for bitmask_js
+"""
+
+from setuptools import setup
+import datetime
+import time
+
+long_description = \
+'''bitmask_js
+----------------- 
+This package contains the already compiled javascript resources for the bitmask UI.
+
+If you want to develop for this UI, please checkout the bitmask-dev [0] repo and follow the instructions in the ui/README.md file.
+
+[0] https://github.com/leapcode/bitmask-dev'''
+
+now       = datetime.datetime.now()
+timestamp = time.strftime('%Y%m%d%H%M', now.timetuple())
+
+setup(
+    name='bitmask_js',
+    version='0.1.%s' % timestamp,
+    description='Bitmask UI',
+    long_description=long_description,
+    author='LEAP Encrypted Access Project',
+    author_email='info@leap.se',
+    url='http://leap.se',
+    packages=['bitmask_js'],
+    package_data={
+        '': ['public/*',
+             'public/css/*',
+             'public/fonts/*',
+             'public/img/*',
+             'publlic/js/*',
+             ]
+    }
+)
diff --git a/ui/webpack.config.js b/ui/webpack.config.js
new file mode 100644
index 00000000..c0f8d191
--- /dev/null
+++ b/ui/webpack.config.js
@@ -0,0 +1,84 @@
+var path = require('path')
+var webpack = require('webpack')
+var CopyWebpackPlugin = require('copy-webpack-plugin');
+
+var config = {
+  context: path.join(__dirname, 'app'),
+  entry: ['babel-polyfill', './main.js'],
+  output: {
+    path: path.join(__dirname, 'pydist', 'bitmask_js', 'public'),
+    filename: 'app.bundle.js'
+  },
+  resolve: {
+    modulesDirectories: ['node_modules', './app'],
+    extensions: ['', '.js', '.jsx']
+  },
+  module: {
+    loaders: [
+      // babel transform
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        exclude: /node_modules/,
+        query: {
+          presets: ['react', 'es2015']
+        }
+      },
+      {
+        test: /\.css$/,
+        loader: "style!css"
+      },
+      {
+        test: /\.less$/,
+        loader: "style!css!less?noIeCompat"
+      }
+    ]
+  },
+  plugins: [
+    // don't bundle when there is an error:
+    new webpack.NoErrorsPlugin(),
+
+    // https://webpack.github.io/docs/code-splitting.html
+    // new webpack.optimize.CommonChunkPlugin('common.js')
+
+    //
+    // ASSETS
+    //
+    // If you make changes to the asset files, you will need to stop then rerun
+    // `npm run watch` for the changes to take effect.
+    //
+    // For more information: https://github.com/kevlened/copy-webpack-plugin
+    //
+    new CopyWebpackPlugin([
+      { from: 'css/*.css' },
+      { from: 'img/*'},
+      { from: 'index.html' },
+      { from: '../node_modules/bootstrap/dist/css/bootstrap.min.css', to: 'css' },
+      { from: '../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', to: 'fonts' },
+      { from: '../node_modules/zxcvbn/dist/zxcvbn.js', to: 'js' }
+    ])
+  ],
+  stats: {
+    colors: true
+  },
+  // source-map can be used in production or development
+  // but it creates a separate file.
+  devtool: 'source-map'
+}
+
+/*
+if (process.env.NODE_ENV == 'production') {
+  // see https://github.com/webpack/docs/wiki/optimization
+  config.plugins.push(
+    new webpack.optimize.UglifyJsPlugin({
+      compress: { warnings: false },
+      output: { comments: false }
+    }),
+    new webpack.optimize.DedupePlugin()
+  )
+} else {
+  config.devtool = 'inline-source-map';
+}
+*/
+
+module.exports = config
diff --git a/www/Makefile b/www/Makefile
deleted file mode 100644
index d67a02b3..00000000
--- a/www/Makefile
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# builds for development mode
-#
-
-dev-build: build-clean
-	npm install
-	npm run build
-	touch pydist/bitmask_js/__init__.py
-
-dev-install: dev-build
-	pip install -e pydist
-
-#
-# installs python package, but does not rebuild the js.
-# for usage when you don't want to install nodejs
-#
-dev-install-prebuilt:
-	pip install -e pydist
-
-
-#
-# distribution builds
-#
-
-dist-build: build-clean
-	npm install
-	npm run build:production
-	touch pydist/bitmask_js/__init__.py
-	cd pydist && python setup.py bdist_wheel
-
-dist-install: dist-build
-	pip install pydist/dist/*.whl
-
-#
-# cleaning up
-#
-
-build-clean:
-	rm -rf pydist/bitmask_js
-	rm -rf pydist/dist
-	rm -rf pydist/build
-
-clean: build-clean
-	rm -rf node_modules
-
-uninstall:
-	pip uninstall bitmask_js
diff --git a/www/README.md b/www/README.md
deleted file mode 100644
index 3f276c00..00000000
--- a/www/README.md
+++ /dev/null
@@ -1,112 +0,0 @@
-Bitmask Javascript UI
-=================================================================
-
-Here lies the user interface for Bitmask, written in Javascript.
-
-quick start:
-
-    sudo apt install nodejs npm nodejs-legacy
-    npm install       # installs development dependencies in "node_modules"
-    npm run watch     # continually rebuilds source .js into "pydist"
-
-build for deployment:
-
-    npm install
-    npm run build:production
-
-After 'build', 'build:production', or 'watch' is run, everything needed for
-Bitmask JS is contained in the 'pydist' directory. No additional files are
-needed. Open the pydist/bitmask_js/public/index.html file in a browser or web
-widget.
-
-However! Because of the single origin policy of browsers, you will need to
-open public/index.html through the webserver included with bitmaskd (e.g.
-http://localhost:7070)
-
-In order for this JS app to be loaded by bitmask, it must be packaged as a
-python package and installed in the virtualenv:
-
-    source path-to-virtualenv/bin/activate
-    make dev-install  # builds and installs JS app as python package
-    pkill bitmaskd    # make sure bitmaskd is not already running
-    bitmaskd          # launch backend
-    npm run open      # opens http://localhost:7070/ in a browser
-    npm run watch     # rebuild JS whenever source file is changed.
-
-In order to package for distribution:
-
-    make dist-build
-
-NOTE: If you make changes to the asset files, like add or modify an image, you
-      will need to stop then rerun `npm run watch` for the changes to take
-      effect.
-
-Development Dependencies
------------------------------------------------------------------
-
-This application has no "runtime" dependencies: all the javascript needed is
-bundled and included. However, there are many development dependencies.
-Run `npm ls` for a full list.
-
-**npm**
-
-Package management, controlled by the package.json file.
-
-**webpack**
-
-Asset bundling and transformation. It takes all your javascript and CSS and
-bundles it into one (or more) files, handling support for 'require' and scope
-separation.
-
-loaders & plugins:
-
-* babel-loader: use Babel with Webpack.
-
-* style-loader/css-loader: standard css loader.
-
-* less-loader: allows use the less stylesheet.
-
-* copy-webpack-plugin: allows us to specify what files to copy using Webpack.
-
-* extract-text-webpack-plugin: allows you to split js and css into separate
-  files.
-
-**babel**
-
-Babel is used to compile javascript into javascript, transforming along the
-way. We have enabled these plugins:
-
-* babel-presets-react: Adds support for React features, such as JSX (html
-  inlined in js files).
-
-* babel-presets-es2015: Allows the use of modern ES6 javascript.
-
-* babel-presets-stage-0: Allows the use of some ES7 proposals, even though
-  these are not standardized yet. Makes classes nicer.
-
-* babel-polyfill: This is not part of the babel transpiling, but is distributed by babel. This polyfill will give you a full ES2015 environment even if the browser is missing some javascript features. We include this in the 'entry' option of the webpack config. https://babeljs.io/docs/usage/polyfill/
-
-**react**
-
-React is an efficient way to generate HTML views with Javascript. It allows you
-to create interactive UIs without ever modifying the DOM by using "one way"
-data binding. This greatly simplifies the code and reduces errors.
-
-**bootstrap**
-
-The world's most popular CSS styles for UI elements. The npm package includes
-both pre-compiled css and less stylesheets. Even though Semantic UI is better,
-Bootstrap components for React are much more stable, I have found, and also are
-easy to theme.
-
-To integrate Bootstrap with React:
-
-* react-bootstrap: React components that use Bootstrap styles. These component
-  include all the needed javascript and don't require JQuery, although they do
-  require that the bootstrap CSS is loaded independently.
-
-**zxcvbn**
-
-A password strength checker that doesn't suck, but which is big. This JS is
-only loaded when we think we are about to need it.
-
diff --git a/www/app/app.js b/www/app/app.js
deleted file mode 100644
index 57120a4e..00000000
--- a/www/app/app.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import bitmask from 'lib/bitmask'
-import Account from 'models/account'
-
-class Application {
-  constructor() {
-  }
-
-  //
-  // main entry point for the application
-  //
-  start() {
-    Account.active().then(account => {
-      if (account == null) {
-        this.show('greeter', {onLogin: this.onLogin.bind(this)})
-      } else {
-        this.show('main', {initialAccount: account})
-      }
-    }, error => {
-      this.show('error', {error: error})
-    })
-  }
-
-  onLogin(account) {
-    this.show('main', {initialAccount: account})
-  }
-
-  show(panel, properties) {
-    this.switcher.show(panel, properties)
-  }
-}
-
-var App = new Application
-export default App
\ No newline at end of file
diff --git a/www/app/components/area.js b/www/app/components/area.js
deleted file mode 100644
index e903e5f5..00000000
--- a/www/app/components/area.js
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// A bootstrap panel, but with some extra options
-//
-
-import React from 'react'
-// import {Panel} from 'react-bootstrap'
-
-class Area extends React.Component {
-
-  static get defaultProps() {return{
-     position: null,  // top or bottom
-     size: 'small',   // small or big
-     type: null,      // light or dark
-     className: null
-  }}
-
-  constructor(props) {
-    super(props)
-  }
-
-  render() {
-    let style = {}
-    let innerstyle = {}
-    if (this.props.position == 'top') {
-      style.borderBottomRightRadius = '0px'
-      style.borderBottomLeftRadius = '0px'
-      style.marginBottom = '0px'
-      style.borderBottom = '0px'
-      if (this.props.size == 'big') {
-        innerstyle.padding = '25px'
-      }
-    } else if (this.props.position == 'bottom') {
-      style.borderTopRightRadius = '0px'
-      style.borderTopLeftRadius = '0px'
-      style.borderTop = '0px'
-      if (this.props.size == 'big') {
-        innerstyle.padding = '15px 25px'
-      }
-    }
-
-    let type = this.props.type ? "area-" + this.props.type : ""
-    let className = ['panel', 'panel-default', type, this.props.className].join(' ')
-    return(
-      <div className={className} style={style}>
-        <div className="panel-body" style={innerstyle}>
-          {this.props.children}
-        </div>
-      </div>
-    )
-  }
-
-}
-
-// Area.propTypes = {
-//   children: React.PropTypes.oneOfType([
-//     React.PropTypes.element,
-//     React.PropTypes.arrayOf(React.PropTypes.element)
-//   ])
-// }
-
-//Area.propTypes = {
-//  children: React.PropTypes.element.isRequired
-//}
-
-export default Area
diff --git a/www/app/components/center.js b/www/app/components/center.js
deleted file mode 100644
index 6fa62128..00000000
--- a/www/app/components/center.js
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// puts a block right in the center of the window
-//
-
-import React from 'react'
-
-class Center extends React.Component {
-
-  static get defaultProps() {return{
-    width: null
-  }}
-
-  constructor(props) {
-    super(props)
-  }
-
-  render() {
-    let style = null
-    if (this.props.width) {
-      style = {width: this.props.width + 'px'}
-    }
-    return (
-      <div className="center-container">
-        <div className="center-item" style={style}>
-          {this.props.children}
-        </div>
-      </div>
-    )
-  }
-}
-
-Center.propTypes = {
-  children: React.PropTypes.oneOfType([
-    React.PropTypes.element,
-    React.PropTypes.arrayOf(React.PropTypes.element)
-  ])
-}
-
-export default Center
diff --git a/www/app/components/debug_panel.js b/www/app/components/debug_panel.js
deleted file mode 100644
index 7515ba84..00000000
--- a/www/app/components/debug_panel.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react'
-import App from '../app'
-
-
-class DebugPanel extends React.Component {
-
-  constructor(props) {
-    super(props)
-    this.click = this.click.bind(this)
-  }
-
-  componentDidMount() {
-    this.click(window.location.hash.replace('#', ''))
-  }
-
-  click(panel_name) {
-    window.location.hash = panel_name
-    App.show(panel_name)
-  }
-
-  panel(panel_name) {
-    return elem(
-      'a',
-      { onClick: () => this.click(panel_name), key: panel_name },
-      panel_name
-    )
-  }
-
-  render() {
-    return elem('div', {className: 'debug-panel'},
-      this.panel('splash'),
-      this.panel('greeter'),
-      this.panel('wizard'),
-      this.panel('main')
-    )
-  }
-
-}
-
-export default DebugPanel
\ No newline at end of file
diff --git a/www/app/components/error_panel.js b/www/app/components/error_panel.js
deleted file mode 100644
index fc88d459..00000000
--- a/www/app/components/error_panel.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react'
-import Center from './center'
-import Area from './area'
-
-export default class ErrorPanel extends React.Component {
-
-  constructor(props) {
-    super(props)
-  }
-
-  render () {
-    return (
-      <Center width="400">
-        <Area>
-          <h1>Error</h1>
-          {this.props.error}
-        </Area>
-      </Center>
-    )
-  }
-}
diff --git a/www/app/components/greeter_panel.js b/www/app/components/greeter_panel.js
deleted file mode 100644
index 4552db18..00000000
--- a/www/app/components/greeter_panel.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react'
-import Login from './login'
-import Center from './center'
-import Splash from './splash'
-import Area from './area'
-import { Glyphicon } from 'react-bootstrap'
-import App from 'app'
-
-export default class GreeterPanel extends React.Component {
-
-  constructor(props) {
-    super(props)
-  }
-
-  newAccount() {
-    App.show('wizard')
-  }
-
-  render () {
-    return <div>
-      <Splash speed="slow" mask={false} />
-      <Center width="400">
-        <Area position="top" type="light" className="greeter">
-          <Login {...this.props} rememberAllowed={false}/>
-        </Area>
-        <Area position="bottom" type="dark" className="greeter">
-          <Glyphicon glyph="user" />
-          &nbsp;
-          <a href="#" onClick={this.newAccount.bind(this)}>Create a new account...</a>
-        </Area>
-      </Center>
-    </div>
-  }
-}
diff --git a/www/app/components/list_edit.js b/www/app/components/list_edit.js
deleted file mode 100644
index 0d557d22..00000000
--- a/www/app/components/list_edit.js
+++ /dev/null
@@ -1,122 +0,0 @@
-//
-// A simple list of items, with minus and plus buttons to add and remove
-// items.
-//
-
-import React from 'react'
-import {Button, ButtonGroup, ButtonToolbar, Glyphicon, FormControl} from 'react-bootstrap'
-
-const CONTAINER_CSS = {
-  display: "flex",
-  flexDirection: "column"
-}
-const SELECT_CSS = {
-  padding: "0px",
-  flex: "1 1 1000px",
-  overflowY: "scroll"
-}
-const OPTION_CSS = {
-  padding: "10px"
-}
-const TOOLBAR_CSS = {
-  paddingTop: "10px",
-  flex: "0 0 auto"
-}
-
-class ListEdit extends React.Component {
-
-  static get defaultProps() {return{
-    width: null,
-    items: [
-      'aaaaaaa',
-      'bbbbbbb',
-      'ccccccc'
-    ],
-    selected: null,
-    onRemove: null,
-    onAdd: null,
-  }}
-
-  constructor(props) {
-    super(props)
-    let index = 0
-    if (props.selected) {
-      index = props.items.indexOf(props.selected)
-    }
-    this.state = {
-      selected: index
-    }
-    this.click  = this.click.bind(this)
-    this.add    = this.add.bind(this)
-    this.remove = this.remove.bind(this)
-  }
-
-  setSelected(index) {
-    this.setState({
-      selected: index
-    })
-  }
-
-  click(e) {
-    let row = parseInt(e.target.value)
-    if (row >= 0) {
-      this.setState({selected: row})
-    }
-  }
-
-  add() {
-    if (this.props.onAdd) {
-      this.props.onAdd()
-    }
-  }
-
-  remove() {
-    if (this.state.selected >= 0 && this.props.onRemove) {
-      if (this.props.items.length == this.state.selected + 1) {
-        // if we remove the last item, set the selected item
-        // to the one right before it.
-        this.setState({selected: (this.state.selected - 1)})
-      }
-      this.props.onRemove(this.props.items[this.state.selected])
-    }
-  }
-
-  render() {
-    let options = null
-    if (this.props.items) {
-      options = this.props.items.map((item, i) => {
-        return <option style={OPTION_CSS} key={i} value={i}>{item}</option>
-      }, this)
-    }
-    return(
-      <div style={CONTAINER_CSS}>
-        <FormControl
-          value={this.state.selected}
-          style={SELECT_CSS} className="select-list"
-          componentClass="select" size="5" onChange={this.click}>
-          {options}
-        </FormControl>
-        <ButtonToolbar className="pull-right" style={TOOLBAR_CSS}>
-          <ButtonGroup>
-            <Button onClick={this.add}>
-              <Glyphicon glyph="plus" />
-            </Button>
-            <Button disabled={this.state.selected < 0} onClick={this.remove}>
-              <Glyphicon glyph="minus" />
-            </Button>
-          </ButtonGroup>
-        </ButtonToolbar>
-      </div>
-    )
-  }
-
-}
-
-ListEdit.propTypes = {
-  children: React.PropTypes.oneOfType([
-    React.PropTypes.element,
-    React.PropTypes.arrayOf(React.PropTypes.element)
-  ])
-}
-
-export default ListEdit
diff --git a/www/app/components/login.js b/www/app/components/login.js
deleted file mode 100644
index fe4ef5b2..00000000
--- a/www/app/components/login.js
+++ /dev/null
@@ -1,302 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom'
-
-import { FormGroup, ControlLabel, FormControl, HelpBlock, Button,
-  Checkbox, Glyphicon, Overlay, Tooltip, Alert } from 'react-bootstrap'
-import Spinner from './spinner'
-
-import Validate from 'lib/validate'
-import App from 'app'
-import Account from 'models/account'
-
-class Login extends React.Component {
-
-  static get defaultProps() {return{
-    rememberAllowed: false,   // if set, show remember password checkbox
-    domain: null,             // if set, only allow this domain
-    onLogin: null
-  }}
-
-  constructor(props) {
-    super(props)
-
-    // validation states can be null, 'success', 'warning', or 'error'
-
-    this.state = {
-      loading: false,
-
-      authError: false,     // authentication error message
-
-      username: "etest1@riseup.net",
-      usernameState: null,  // username validation state
-      usernameError: false, // username help message
-
-      password: "whatever",
-      passwordState: null,  // password validation state
-      passwordError: false, // password help message
-
-      disabled: false,
-      remember: false       // remember is checked?
-    }
-
-    // prebind:
-    this.onUsernameChange = this.onUsernameChange.bind(this)
-    this.onUsernameBlur   = this.onUsernameBlur.bind(this)
-    this.onPassword = this.onPassword.bind(this)
-    this.onSubmit   = this.onSubmit.bind(this)
-    this.onRemember = this.onRemember.bind(this)
-  }
-
-  componentDidMount() {
-    Validate.loadPasswdLib()
-  }
-
-  render () {
-    let rememberCheck = ""
-    let submitButton  = ""
-    let usernameHelp  = null
-    let passwordHelp  = null
-    let message = null
-
-    if (this.props.rememberAllowed) {
-      let props = {
-        style: {marginTop: "0px"},
-        onChange: this.onRemember
-      }
-
-      if (this.state.remember) {
-        rememberCheck = <Checkbox {...props} checked>
-          Remember username and password
-        </Checkbox>
-      } else {
-        rememberCheck = <Checkbox {...props}>
-          Remember username and password
-        </Checkbox>
-      }
-    }
-
-    if (this.state.authError) {
-      // style may be: success, warning, danger, info
-      message = (
-        <Alert bsStyle="danger">{this.state.authError}</Alert>
-      )
-    }
-
-    if (this.state.usernameError) {
-      usernameHelp = <HelpBlock>{this.state.usernameError}</HelpBlock>
-      // let props = {shouldUpdatePosition: true, show:true, placement:"right",
-      //              target:this.refs.username}
-      // usernameHelp = (
-      //   <Overlay {...props}>
-      //     <Tooltip id="username-tooltip">{this.state.usernameError}</Tooltip>
-      //   </Overlay>
-      // )
-    } else {
-      //usernameHelp = <HelpBlock>&nbsp;</HelpBlock>
-    }
-
-    if (this.state.passwordError) {
-      passwordHelp = <HelpBlock>{this.state.passwordError}</HelpBlock>
-      // let props = {shouldUpdatePosition: true, show:true, placement:"right",
-      //              target:this.refs.password, component: {this}}
-      // passwordHelp = (
-      //   <Overlay {...props}>
-      //     <Tooltip id="password-tooltip">{this.state.passwordError}</Tooltip>
-      //   </Overlay>
-      // )
-    } else {
-      //passwordHelp = <HelpBlock>&nbsp;</HelpBlock>
-    }
-
-    let buttonProps = {
-      type: "button",
-      onClick: this.onSubmit,
-      disabled: !this.maySubmit()
-    }
-    if (this.state.loading) {
-       submitButton = <Button block {...buttonProps}><Spinner /></Button>
-    } else {
-       submitButton = <Button block {...buttonProps}>Log In</Button>
-    }
-
-    let usernameref = null
-    if (this.props.domain) {
-      usernameref = function(c) {
-        if (c != null) {
-          let textarea = ReactDOM.findDOMNode(c)
-          let start = textarea.value.indexOf('@')
-          if (textarea.selectionStart > start) {
-            textarea.setSelectionRange(start, start)
-          }
-        }
-      }
-    }
-
-    let form = <form onSubmit={this.onSubmit}>
-      {message}
-      <FormGroup style={{marginBottom: '10px' }} controlId="loginUsername" validationState={this.state.usernameState}>
-        <ControlLabel>Username</ControlLabel>
-        <FormControl
-          componentClass="textarea"
-          style={{resize: "none"}}
-          rows="1"
-          ref={usernameref}
-          autoFocus
-          value={this.state.username}
-          onChange={this.onUsernameChange}
-          onBlur={this.onUsernameBlur} />
-        {this.state.usernameState == 'success' ? null : <FormControl.Feedback/>}
-        {usernameHelp}
-      </FormGroup>
-
-      <FormGroup controlId="loginPassword" validationState={this.state.passwordState}>
-        <ControlLabel>Password</ControlLabel>
-        <FormControl
-          type="password"
-          ref="password"
-          value={this.state.password}
-          onChange={this.onPassword} />
-        {this.state.passwordState == 'success' ? null : <FormControl.Feedback/>}
-        {passwordHelp}
-      </FormGroup>
-
-      {submitButton}
-      {rememberCheck}
-    </form>
-
-    return form
-  }
-
-  //
-  // Here we do a partial validation, because the user has not stopped typing.
-  //
-  onUsernameChange(e) {
-    let username = e.target.value.toLowerCase().replace("\n", "")
-    if (this.props.domain) {
-      let [userpart, domainpart] = username.split(
-        new RegExp('@|' + this.props.domain.replace(".", "\\.") + '$')
-      )
-      username = [userpart, this.props.domain].join('@')
-    }
-    let error = Validate.usernameInteractive(username, this.props.domain)
-    let state = null
-    if (error) {
-      state = 'error'
-    } else {
-      if (username && username.length > 0) {
-        let finalError = Validate.username(username)
-        state = finalError ? null : 'success'
-      }
-    }
-    this.setState({
-      username: username,
-      usernameState: state,
-      usernameError: error ? error : null
-    })
-  }
-
-  //
-  // Here we do a more complete validation, since the user have left the field.
-  //
-  onUsernameBlur(e) {
-    let username = e.target.value.toLowerCase()
-    this.setState({
-      username: username
-    })
-    if (username.length > 0) {
-      this.validateUsername(username)
-    } else {
-      this.setState({
-        usernameState: null,
-        usernameError: null
-      })
-    }
-  }
-
-  onPassword(e) {
-    let password = e.target.value
-    this.setState({password: password})
-    if (password.length > 0) {
-      this.validatePassword(password)
-    } else {
-      this.setState({
-        passwordState: null,
-        passwordError: null
-      })
-    }
-  }
-
-  onRemember(e) {
-    let currentValue = e.target.value == 'on' ? true : false
-    let value = !currentValue
-    this.setState({remember: value})
-  }
-
-  validateUsername(username) {
-    let error = Validate.username(username, this.props.domain)
-    this.setState({
-      usernameState: error ? 'error' : 'success',
-      usernameError: error ? error : null
-    })
-  }
-
-  validatePassword(password) {
-    let state = null
-    let message = null
-    let result = Validate.passwordStrength(password)
-    if (result) {
-      message = "Time to crack: " + result.crack_times_display.offline_slow_hashing_1e4_per_second
-      if (result.score == 0) {
-        state = 'error'
-      } else if (result.score == 1 || result.score == 2) {
-        state = 'warning'
-      } else {
-        state = 'success'
-      }
-    }
-    this.setState({
-      passwordState: state,
-      passwordError: message
-    })
-  }
-
-  maySubmit() {
-    return(
-      !this.stateLoading &&
-      !this.state.usernameError &&
-      this.state.username != "" &&
-      this.state.password != ""
-    )
-  }
-
-  onSubmit(e) {
-    e.preventDefault() // don't reload the page please!
-    if (!this.maySubmit()) { return }
-    this.setState({loading: true})
-
-    let account = Account.find(this.state.username)
-    account.login(this.state.password).then(
-      account => {
-        this.setState({loading: false})
-        if (this.props.onLogin) {
-          this.props.onLogin(account)
-        }
-      },
-      error => {
-        console.log(error)
-        if (error == "") {
-          error = 'Something failed, but we did not get a message'
-        }
-        this.setState({
-          loading: false,
-          usernameState: 'error',
-          passwordState: 'error',
-          authError: error
-        })
-      }
-    )
-  }
-
-}
-
-export default Login
\ No newline at end of file
diff --git a/www/app/components/main_panel/account_list.js b/www/app/components/main_panel/account_list.js
deleted file mode 100644
index d0ef092f..00000000
--- a/www/app/components/main_panel/account_list.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import React from 'react'
-import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap'
-
-import App from 'app'
-import Account from 'models/account'
-
-export default class AccountList extends React.Component {
-
-  static get defaultProps() {return{
-    account: null,
-    accounts: [],
-    onAdd: null,
-    onRemove: null,
-    onSelect: null
-  }}
-
-  constructor(props) {
-    super(props)
-
-    this.state = {
-      mode: 'expanded'
-    }
-
-    // prebind:
-    this.select = this.select.bind(this)
-    this.add    = this.add.bind(this)
-    this.remove = this.remove.bind(this)
-    this.expand = this.expand.bind(this)
-    this.collapse = this.collapse.bind(this)
-  }
-
-  select(e) {
-    let account = this.props.accounts.find(
-      account => account.id == e.currentTarget.dataset.id
-    )
-    if (this.props.onSelect) {
-      this.props.onSelect(account)
-    }
-  }
-
-  add() {
-    App.show('wizard')
-  }
-
-  remove() {
-  }
-
-  expand() {
-    this.setState({mode: 'expanded'})
-  }
-
-  collapse() {
-    this.setState({mode: 'collapsed'})
-  }
-
-  render() {
-    let style = {}
-    let expandButton = null
-    let plusminusButtons = null
-
-    if (this.state.mode == 'expanded') {
-      expandButton = (
-        <Button onClick={this.collapse} className="expander btn-inverse btn-flat pull-right">
-          <Glyphicon glyph="triangle-left" />
-        </Button>
-      )
-      plusminusButtons = (
-        <ButtonGroup style={style}>
-          <Button onClick={this.add} className="btn-inverse">
-            <Glyphicon glyph="plus" />
-          </Button>
-          <Button disabled={this.props.account == null} onClick={this.remove} className="btn-inverse">
-            <Glyphicon glyph="minus" />
-          </Button>
-        </ButtonGroup>
-      )
-    } else {
-      style.width = '60px'
-      expandButton = (
-        <Button onClick={this.expand} className="expander btn-inverse btn-flat pull-right">
-          <Glyphicon glyph="triangle-right" />
-        </Button>
-      )
-    }
-
-    let items = this.props.accounts.map((account, i) => {
-      let className = account == this.props.account ? 'active' : 'inactive'
-      return (
-        <li key={i} className={className} onClick={this.select} data-id={account.id}>
-          <span className="username">{account.userpart}</span>
-          <span className="domain">{account.domain}</span>
-          <span className="arc top"></span>
-          <span className="arc bottom"></span>
-        </li>
-      )
-    })
-
-
-    return (
-      <div className="accounts" style={style}>
-        <ul>
-          {items}
-        </ul>
-        <ButtonToolbar>
-          {plusminusButtons}
-          {expandButton}
-        </ButtonToolbar>
-      </div>
-    )
-  }
-
-
-}
diff --git a/www/app/components/main_panel/email_section.js b/www/app/components/main_panel/email_section.js
deleted file mode 100644
index a6525d92..00000000
--- a/www/app/components/main_panel/email_section.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react'
-//import { Button, Glyphicon, Alert } from 'react-bootstrap'
-import SectionLayout from './section_layout'
-import Account from 'models/account'
-import Spinner from 'components/spinner'
-import bitmask from 'lib/bitmask'
-
-export default class EmailSection extends React.Component {
-
-  static get defaultProps() {return{
-    account: null
-  }}
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      status: null
-    }
-    this.openKeys  = this.openKeys.bind(this)
-    this.openApp   = this.openApp.bind(this)
-    this.openPrefs = this.openPrefs.bind(this)
-
-    console.log('email constructor')
-  }
-
-  openKeys() {}
-  openApp() {}
-  openPrefs() {}
-
-  render () {
-    //let message = null
-    //if (this.state.error) {
-    //  // style may be: success, warning, danger, info
-    //  message = (
-    //    <Alert bsStyle="danger">{this.state.error}</Alert>
-    //  )
-    //}
-    let button = null
-    if (this.state.status == 'ready') {
-      button = <Button onClick={this.openApp}>Open Email</Button>
-    }
-    return (
-      <SectionLayout icon="envelope" status="on" button={button}>
-        <h1>inbox: </h1>
-      </SectionLayout>
-    )
-  }
-}
diff --git a/www/app/components/main_panel/index.js b/www/app/components/main_panel/index.js
deleted file mode 100644
index 3cc6c11f..00000000
--- a/www/app/components/main_panel/index.js
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// The main panel manages the current account and the list of available accounts
-//
-// It displays multiple sections, one for each service.
-//
-
-import React from 'react'
-import App from 'app'
-import Login from 'components/login'
-import Account from 'models/account'
-import DummyAccount from 'models/dummy_account'
-
-import './main_panel.less'
-import AccountList from './account_list'
-import UserSection from './user_section'
-import EmailSection from './email_section'
-
-export default class MainPanel extends React.Component {
-
-  static get defaultProps() {return{
-    initialAccount: null
-  }}
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      account: null,
-      accounts: []
-    }
-    this.activateAccount = this.activateAccount.bind(this)
-  }
-
-  componentWillMount() {
-    if (this.props.initialAccount) {
-      console.log(Account.list)
-      Account.add(this.props.initialAccount)
-      Account.add(new DummyAccount(this.props.initialAccount))
-      this.setState({
-        account: this.props.initialAccount,
-        accounts: Account.list
-      })
-    }
-  }
-
-  activateAccount(account) {
-    this.setState({
-      account: account,
-      accounts: Account.list
-    })
-  }
-
-  //setAccounts(accounts) {
-  //  this.setState({
-  //    accounts: accounts
-  //  })
-  //}
-
-  render() {
-    let emailSection = null
-    let vpnSection = null
-
-    if (this.state.account.authenticated) {
-      if (this.state.account.hasEmail) {
-        emailSection = <EmailSection account={this.state.account} />
-      }
-    }
-
-    return (
-      <div className="main-panel">
-        <AccountList account={this.state.account} accounts={this.state.accounts} onSelect={this.activateAccount} />
-        <div className="body">
-          <UserSection account={this.state.account} onLogin={this.activateAccount} onLogout={this.activateAccount}/>
-          {vpnSection}
-          {emailSection}
-        </div>
-      </div>
-    )
-  }
-
-}
diff --git a/www/app/components/main_panel/main_panel.less b/www/app/components/main_panel/main_panel.less
deleted file mode 100644
index 4e0ecb05..00000000
--- a/www/app/components/main_panel/main_panel.less
+++ /dev/null
@@ -1,212 +0,0 @@
-// The space around account entries:
-@accounts-padding: 8px;
-@accounts-corner: 6px;
-@accounts-width: 200px;
-
-//
-// LAYOUT
-//
-
-.main-panel {
-  position: absolute;
-  height: 100%;
-  width: 100%;
-  display: -webkit-flex;
-  -webkit-flex-direction: row;
-
-  > .body {
-    -webkit-flex: 1 1 auto;
-    overflow: auto;
-  }
-
-  .accounts {
-    -webkit-flex: 0 0 auto;
-    overflow-y: auto;
-    overflow-x: hidden;
-
-    display: -webkit-flex;
-    -webkit-flex-direction: column;
-    ul {
-      -webkit-flex: 1 1 1000px;
-    }
-    .btn-toolbar {
-      -webkit-flex: 0 0 auto;
-    }
-
-  }
-
-}
-
-//
-// Style
-//
-
-
-.main-panel > .body {
-  padding: 20px;
-}
-
-.main-panel .accounts {
-  background-color: #333;
-  width: @accounts-width;
-  padding: @accounts-padding;
-  padding-right: 0px;
-}
-
-.main-panel .accounts ul {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-}
-
-.main-panel .accounts li {
-  position: relative;
-  cursor: pointer;
-  color: white;
-  padding: 15px;
-  background-color: #444;
-  margin-bottom: @accounts-padding;
-  border-top-left-radius: @accounts-corner - 1;
-  border-bottom-left-radius: @accounts-corner - 1;
-  z-index: 100;
-}
-
-.main-panel .accounts li span.domain {
-  display: block;
-  font-weight: bold;
-  //margin-left: 40px;
-}
-
-.main-panel .accounts li span.username {
-  display: block;
-  //margin-left: 40px;
-  //line-height: 7px;
-  //margin-bottom: 4px;
-}
-
-/*.main-panel .accounts li span.icon {
-  display: block;
-  height: 32px;
-  width: 32px;
-  background-color: #999;
-  float: left;
-}
-*/
-
-.main-panel .accounts li.active {
-  background-color: white;
-  color: #333;
-}
-
-.main-panel .accounts li.active span.arc {
-  display: block;
-  height: @accounts-corner;
-  width: @accounts-corner;
-  background-color: white;
-  position: absolute;
-  right: 0;
-}
-
-.main-panel .accounts li.active span.arc.top {
-  top: 0;
-  margin-top: -@accounts-corner;
-}
-.main-panel .accounts li.active span.arc.bottom {
-  bottom: 0;
-  margin-bottom: -@accounts-corner;
-}
-.main-panel .accounts li.active span.arc:after {
-  display: block;
-  content: "";
-  border-radius: 100%;
-  height: 0px;
-  width: 0px;
-  margin-left: -@accounts-corner;
-}
-.main-panel .accounts li.active span.arc.top:after {
-  border: @accounts-corner solid transparent;
-  border-right: @accounts-corner solid #333;
-  margin-top: -@accounts-corner;
-  -webkit-transform: rotate(45deg);
-  transform: rotate(45deg);
-}
-.main-panel .accounts li.active span.arc.bottom:after {
-  border: @accounts-corner solid #333;
-}
-
-.main-panel .accounts .btn.expander {
-   margin-right: @accounts-padding;
-}
-
-//
-// SECTIONS
-//
-
-@icon-size: 32px;
-@status-size: 24px;
-@section-padding: 10px;
-
-// service sections layout
-
-.main-panel .service-section {
-  display: -webkit-flex;
-  -webkit-flex-direction: row;
-  > .icon {
-    -webkit-flex: 0 0 auto;
-  }
-  > .body {
-    -webkit-flex: 1 1 auto;
-  }
-  > .buttons {
-    -webkit-flex: 0 0 auto;
-  }
-  > .status {
-    -webkit-flex: 0 0 auto;
-    display: -webkit-flex;
-    -webkit-align-items: center;
-  }
-
-}
-
-.main-panel .service-section div {
-  //outline: 1px solid rgba(0,0,0,0.1);
-}
-
-// service sections style
-
-.main-panel .service-section {
-  background: #f6f6f6;
-  border-radius: 4px;
-  padding: 10px;
-  margin-bottom: 10px;
-  &.wide-margin {
-    padding: 20px 20px 20px 10px; // arbitrary, looks nice
-  }
-  > .icon {
-    padding-right: @section-padding;
-    img {
-      width: @icon-size;
-      height: @icon-size;
-    }
-  }
-  > .body {
-    h1 {
-      margin: 0;
-      padding: 0;
-      font-size: @icon-size - 10;
-      line-height: @icon-size;
-    }
-  }
-  > .buttons {
-    padding-left: 10px;
-  }
-  > .status {
-    padding-left: @section-padding;
-    width: @section-padding + @status-size;
-    img {
-      width: @status-size;
-      height: @status-size;
-    }
-  }
-}
-
diff --git a/www/app/components/main_panel/section_layout.js b/www/app/components/main_panel/section_layout.js
deleted file mode 100644
index e7c6f2ab..00000000
--- a/www/app/components/main_panel/section_layout.js
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// This is the layout for a service section in the main window.
-// It does not do anything except for arrange items using css and html.
-//
-
-import React from 'react'
-
-export default class SectionLayout extends React.Component {
-
-  static get defaultProps() {return{
-    icon: null,
-    buttons: null,
-    status: null,
-    className: "",
-    style: {}
-  }}
-
-  constructor(props) {
-    super(props)
-  }
-
-  render() {
-    let className = ["service-section", this.props.className].join(' ')
-    let status = null
-    let icon = null
-    let buttons = null
-
-    if (this.props.status) {
-      status = (
-        <div className="status">
-          <img src={'img/' + this.props.status + '.svg' } />
-        </div>
-      )
-    }
-    if (this.props.icon) {
-      icon = (
-        <div className="icon">
-          <img src={'img/' + this.props.icon + '.svg'} />
-        </div>
-      )
-    }
-    if (this.props.buttons)
-      buttons = (
-        <div className="buttons">
-          {this.props.buttons}
-        </div>
-      )
-    return(
-      <div className={className} style={this.props.style}>
-        {icon}
-        <div className="body">
-          {this.props.children}
-        </div>
-        {buttons}
-        {status}
-      </div>
-    )
-  }
-}
diff --git a/www/app/components/main_panel/user_section.js b/www/app/components/main_panel/user_section.js
deleted file mode 100644
index 0b4ba136..00000000
--- a/www/app/components/main_panel/user_section.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react'
-import { Button, Glyphicon, Alert } from 'react-bootstrap'
-import SectionLayout from './section_layout'
-import Login from 'components/login'
-import Spinner from 'components/spinner'
-import Account from 'models/account'
-
-import bitmask from 'lib/bitmask'
-
-export default class UserSection extends React.Component {
-
-  static get defaultProps() {return{
-    account: null,
-    onLogout: null,
-    onLogin: null
-  }}
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      error: null,
-      loading: false
-    }
-    this.logout = this.logout.bind(this)
-  }
-
-  logout() {
-    this.setState({loading: true})
-    this.props.account.logout().then(
-      account => {
-        this.setState({error: null, loading: false})
-        if (this.props.onLogout) {
-          this.props.onLogout(account)
-        }
-      }, error => {
-        this.setState({error: error, loading: false})
-      }
-    )
-  }
-
-  render () {
-    let message = null
-    if (this.state.error) {
-      // style may be: success, warning, danger, info
-      message = (
-        <Alert bsStyle="danger">{this.state.error}</Alert>
-      )
-    }
-
-    if (this.props.account.authenticated) {
-      let button = null
-      if (this.state.loading) {
-        button = <Button disabled={true}><Spinner /></Button>
-      } else {
-        button = <Button onClick={this.logout}>Log Out</Button>
-      }
-      return (
-        <SectionLayout icon="user" buttons={button} status="on">
-          <h1>{this.props.account.address}</h1>
-          {message}
-        </SectionLayout>
-      )
-    } else {
-      return (
-        <SectionLayout icon="user" className="wide-margin">
-          <Login onLogin={this.props.onLogin} domain={this.props.account.domain} />
-        </SectionLayout>
-      )
-    }
-  }
-}
diff --git a/www/app/components/main_panel/vpn_section.js b/www/app/components/main_panel/vpn_section.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/www/app/components/panel_switcher.js b/www/app/components/panel_switcher.js
deleted file mode 100644
index aaf2dc5b..00000000
--- a/www/app/components/panel_switcher.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom'
-
-import DebugPanel from './debug_panel'
-import Splash from './splash'
-import GreeterPanel from './greeter_panel'
-import MainPanel from './main_panel'
-import Wizard from './wizard'
-
-import App from 'app'
-import 'lib/common'
-
-export default class PanelSwitcher extends React.Component {
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      panel: null,
-      panel_properties: null,
-      debug: false
-    }
-    App.switcher = this
-  }
-
-  show(component_name, properties={}) {
-    this.setState({panel: component_name, panel_properties: properties})
-  }
-
-  render() {
-    let elems = []
-    if (this.panelExist(this.state.panel)) {
-      elems.push(
-        this.panelRender(this.state.panel, this.state.panel_properties)
-      )
-    }
-    if (this.state.debug) {
-      elems.push(
-        elem(DebugPanel, {key: 'debug'})
-      )
-    }
-    return <div id="root">{elems}</div>
-  }
-
-  panelExist(panel) {
-    return panel && this['render_'+panel]
-  }
-
-  panelRender(panel_name, props) {
-    let panel = this['render_'+panel_name](props)
-    return elem('div', {key: 'panel'}, panel)
-  }
-
-  render_splash(props)  {return elem(Splash, props)}
-  render_wizard(props)  {return elem(Wizard, props)}
-  render_greeter(props) {return elem(GreeterPanel, props)}
-  render_main(props)    {return elem(MainPanel, props)}
-
-}
diff --git a/www/app/components/spinner/index.js b/www/app/components/spinner/index.js
deleted file mode 100644
index ffc32850..00000000
--- a/www/app/components/spinner/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import './spinner.css';
-
-class Spinner extends React.Component {
-  render() {
-    let props = {}
-    return <div className="spinner">
-      <div className="spin1"></div>
-      <div className="spin2"></div>
-      <div className="spin3"></div>
-    </div>
-  }
-}
-
-export default Spinner
\ No newline at end of file
diff --git a/www/app/components/spinner/spinner.css b/www/app/components/spinner/spinner.css
deleted file mode 100644
index 5e8535c9..00000000
--- a/www/app/components/spinner/spinner.css
+++ /dev/null
@@ -1,42 +0,0 @@
-.spinner {
-  height: 18px;
-  display: inline-block;
-}
-
-.spinner > div {
-  width: 18px;
-  height: 18px;
-  background-color: #000;
-  vertical-align: middle;
-  border-radius: 100%;
-  display: inline-block;
-  -webkit-animation: bouncedelay 1.5s infinite ease-in-out;
-  animation: bouncedelay 1.5s infinite ease-in-out;
-  -webkit-animation-fill-mode: both;
-  animation-fill-mode: both;
-}
-
-.spinner .spin1 {
-  -webkit-animation-delay: -.46s;
-  animation-delay: -.46s;
-}
-.spinner .spin2 {
-  -webkit-animation-delay: -.24s;
-  animation-delay: -.24s;
-}
-
-@-webkit-keyframes bouncedelay {
-  0%, 80%, 100% {
-    -webkit-transform: scale(0.5);
-  } 40% {
-    -webkit-transform: scale(0.9);
-  }
-}
-
-@keyframes bouncedelay {
-  0%, 80%, 100% {
-    transform: scale(0.5);
-  } 40% {
-    transform: scale(0.9);
-  }
-}
diff --git a/www/app/components/splash.js b/www/app/components/splash.js
deleted file mode 100644
index 46170d23..00000000
--- a/www/app/components/splash.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * A simple animated splash screen
- */
-
-import React from 'react'
-import * as COLOR from '../lib/colors'
-
-const colorList = [
-  COLOR.red200, COLOR.pink200, COLOR.purple200, COLOR.deepPurple200,
-  COLOR.indigo200, COLOR.blue200, COLOR.lightBlue200, COLOR.cyan200,
-  COLOR.teal200, COLOR.green200, COLOR.lightGreen200, COLOR.lime200,
-  COLOR.yellow200, COLOR.amber200, COLOR.orange200, COLOR.deepOrange200
-]
-
-export default class Splash extends React.Component {
-
-  static get defaultProps() {return{
-    speed: "fast",
-    mask: true,
-    onClick: null
-  }}
-
-  constructor(props) {
-    super(props)
-    this.counter = 0
-    this.interval = null
-    this.ctx = null
-    this.stepAngle = 0
-    this.resize = this.resize.bind(this)
-    this.click  = this.click.bind(this)
-    if (this.props.speed == "fast") {
-      this.fps = 30
-      this.stepAngle = 0.005
-    } else {
-      this.fps = 30
-      this.stepAngle = 0.0005
-    }
-  }
-
-  componentDidMount() {
-    this.interval = setInterval(this.tick.bind(this), 1000/this.fps)
-    this.canvas   = this.refs.canvas
-    this.ctx      = this.canvas.getContext('2d')
-    window.addEventListener('resize', this.resize)
-  }
-
-  componentWillUnmount() {
-    clearInterval(this.interval)
-    window.removeEventListener('resize', this.resize)
-  }
-
-  click() {
-    if (this.props.onClick) {
-      this.props.onClick()
-    }
-  }
-
-  tick() {
-    this.counter++
-    this.updateCanvas()
-  }
-
-  resize() {
-    this.canvas.width = window.innerWidth
-    this.canvas.height = window.innerHeight
-    this.updateCanvas()
-  }
-
-  updateCanvas() {
-    const arcCount = 16
-    const arcAngle = 1 / arcCount
-    const x = this.canvas.width / 2
-    const y = this.canvas.height / 2
-    const radius = screen.height + screen.width
-
-    for (let i = 0; i < arcCount; i++) {
-      let startAngle = Math.PI * 2 * i/arcCount + this.stepAngle*this.counter
-      let endAngle   = Math.PI * 2 * (i+1)/arcCount + this.stepAngle*this.counter
-
-      this.ctx.fillStyle = colorList[i % colorList.length]
-      this.ctx.strokeStyle = colorList[i % colorList.length]
-      this.ctx.beginPath()
-      this.ctx.moveTo(x, y)
-      this.ctx.arc(x, y, radius, startAngle, endAngle)
-      this.ctx.lineTo(x, y)
-      this.ctx.fill()
-      this.ctx.stroke()
-    }
-
-  }
-
-  render () {
-    let overlay = null
-    let mask = null
-    if (this.props.onClick) {
-      overlay = React.DOM.div({
-        style: {
-          position: 'absolute',
-          height: '100%',
-          width: '100%',
-          backgroundColor: 'transparent'
-        },
-        onClick: this.click
-      })
-    }
-    if (this.props.mask) {
-      mask = React.DOM.img({
-        src: 'img/mask.svg',
-        style: {
-          position: 'absolute',
-          left: '50%',
-          top: '50%',
-          marginLeft: -330/2 + 'px',
-          marginTop: -174/2 + 'px',
-        }
-      })
-    }
-    return React.DOM.div(
-      {style: {overflow: 'hidden'}},
-      React.DOM.canvas({
-        ref: 'canvas',
-        style: {position: 'absolute'},
-        width: window.innerWidth,
-        height: window.innerHeight,
-      }),
-      mask,
-      overlay
-    )
-  }
-
-}
-
diff --git a/www/app/components/wizard/add_provider_modal.js b/www/app/components/wizard/add_provider_modal.js
deleted file mode 100644
index bc5e0236..00000000
--- a/www/app/components/wizard/add_provider_modal.js
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// A modal popup to add a new provider.
-//
-
-import React from 'react'
-import { FormGroup, ControlLabel, FormControl, HelpBlock, Button, Modal } from 'react-bootstrap'
-import Spinner from '../spinner'
-import Validate from '../../lib/validate'
-import App from '../../app'
-
-class AddProviderModal extends React.Component {
-
-  static get defaultProps() {return{
-    title: 'Add a provider',
-    onClose: null
-  }}
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      validationState: null,
-      errorMsg: null,
-      domain: ""
-    }
-    this.accept   = this.accept.bind(this)
-    this.cancel   = this.cancel.bind(this)
-    this.changed  = this.changed.bind(this)
-  }
-
-  accept() {
-    if (this.state.domain) {
-      App.providers.add(this.state.domain)
-    }
-    this.props.onClose()
-  }
-
-  cancel() {
-    this.props.onClose()
-  }
-
-  changed(e) {
-    let domain = e.target.value
-    let newState = null
-    let newMsg   = null
-
-    if (domain.length > 0) {
-      let error = Validate.domain(domain)
-      newState = error ? 'error' : 'success'
-      newMsg   = error
-    }
-    this.setState({
-      domain: domain,
-      validationState: newState,
-      errorMsg: newMsg
-    })
-  }
-
-  render() {
-    let help = null
-    if (this.state.errorMsg) {
-      help = <HelpBlock>{this.state.errorMsg}</HelpBlock>
-    } else {
-      help = <HelpBlock>&nbsp;</HelpBlock>
-    }
-    let form = <form onSubmit={this.accept} autoComplete="off">
-      <FormGroup controlId="addprovider" validationState={this.state.validationState}>
-        <ControlLabel>Domain</ControlLabel>
-        <FormControl
-          type="text"
-          ref="domain"
-          autoFocus
-          value={this.state.domain}
-          onChange={this.changed}
-          onBlur={this.changed} />
-        <FormControl.Feedback/>
-        {help}
-      </FormGroup>
-      <Button onClick={this.accept}>Add</Button>
-    </form>
-
-    return(
-      <Modal show={true} onHide={this.cancel}>
-        <Modal.Header closeButton>
-          <Modal.Title>{this.props.title}</Modal.Title>
-        </Modal.Header>
-        <Modal.Body>
-          {form}
-        </Modal.Body>
-      </Modal>
-    )
-  }
-}
-
-export default AddProviderModal
\ No newline at end of file
diff --git a/www/app/components/wizard/index.js b/www/app/components/wizard/index.js
deleted file mode 100644
index 613b88fd..00000000
--- a/www/app/components/wizard/index.js
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// The provider setup wizard
-//
-
-import React from 'react'
-import App from 'app'
-
-import ProviderSelectStage from './provider_select_stage'
-import './wizard.less'
-
-export default class Wizard extends React.Component {
-
-  constructor(props) {
-    super(props)
-    this.state = {
-      stage: 'provider'
-    }
-  }
-
-  setStage(stage) {
-    this.setState({stage: stage})
-  }
-
-  render() {
-    let stage = null
-    switch(this.state.stage) {
-      case 'provider':
-        stage = <ProviderSelectStage />
-        break
-    }
-    return(
-      <div className="wizard">
-        {stage}
-      </div>
-    )
-  }
-
-}
diff --git a/www/app/components/wizard/provider_select_stage.js b/www/app/components/wizard/provider_select_stage.js
deleted file mode 100644
index 20674be1..00000000
--- a/www/app/components/wizard/provider_select_stage.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react'
-import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap'
-
-import App from 'app'
-import ListEdit from 'components/list_edit'
-import StageLayout from './stage_layout'
-import AddProviderModal from './add_provider_modal'
-
-export default class ProviderSelectStage extends React.Component {
-
-  static get defaultProps() {return{
-    title: "Choose a provider",
-    subtitle: "This doesn't work yet"
-  }}
-
-  constructor(props) {
-    super(props)
-    let domains = this.currentDomains()
-    this.state = {
-      domains: domains,
-      showModal: false
-    }
-    this.add = this.add.bind(this)
-    this.remove = this.remove.bind(this)
-    this.close = this.close.bind(this)
-    this.previous = this.previous.bind(this)
-  }
-
-  currentDomains() {
-    // return(App.providers.domains().slice() || [])
-    return ['domain1', 'domain2', 'domain3']
-  }
-
-  add() {
-    this.setState({showModal: true})
-  }
-
-  remove(provider) {
-    // App.providers.remove(provider)
-    this.setState({domains: this.currentDomains()})
-  }
-
-  close() {
-    let domains = this.currentDomains()
-    if (domains.length != this.state.domains.length) {
-      // this is ugly, but i could not get selection working
-      // by passing it as a property
-      this.refs.list.setSelected(0)
-    }
-    this.setState({
-      domains: domains,
-      showModal: false
-    })
-  }
-
-  previous() {
-    App.start()
-  }
-
-  render() {
-    let modal = null
-    if (this.state.showModal) {
-      modal = <AddProviderModal onClose={this.close} />
-    }
-    let buttons = (
-      <ButtonToolbar className="pull-right">
-        <Button onClick={this.previous}>
-          <Glyphicon glyph="chevron-left" />
-          Previous
-        </Button>
-        <Button>
-          Next
-          <Glyphicon glyph="chevron-right" />
-        </Button>
-      </ButtonToolbar>
-    )
-    let select = <ListEdit ref="list" items={this.state.domains}
-      onRemove={this.remove} onAdd={this.add} />
-    return(
-      <StageLayout title={this.props.title} subtitle={this.props.subtitle} buttons={buttons}>
-        {select}
-        {modal}
-      </StageLayout>
-    )
-  }
-}
diff --git a/www/app/components/wizard/stage_layout.js b/www/app/components/wizard/stage_layout.js
deleted file mode 100644
index 31540221..00000000
--- a/www/app/components/wizard/stage_layout.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react'
-
-class StageLayout extends React.Component {
-
-  static get defaultProps() {return{
-    title: 'untitled',
-    subtitle: null,
-    buttons: null
-  }}
-
-  constructor(props) {
-    super(props)
-  }
-
-  render() {
-    let subtitle = null
-    if (this.props.subtitle) {
-      subtitle = <span>{this.props.subtitle}</span>
-    }
-    return(
-      <div className="stage">
-        <div className="header">
-          {this.props.title}
-          {subtitle}
-        </div>
-        <div className="body">
-          {this.props.children}
-        </div>
-        <div className="footer">
-          {this.props.buttons}
-        </div>
-      </div>
-    )
-  }
-}
-
-export default StageLayout
\ No newline at end of file
diff --git a/www/app/components/wizard/wizard.less b/www/app/components/wizard/wizard.less
deleted file mode 100644
index 29efc20e..00000000
--- a/www/app/components/wizard/wizard.less
+++ /dev/null
@@ -1,44 +0,0 @@
-.wizard .stage {
-  position: absolute;
-  height: 100%;
-  width: 100%;
-
-  display: -webkit-flex;
-  display: flex;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-  -webkit-flex: 1;
-  flex: 1;
-}
-
-.wizard .stage .footer {
-  -webkit-flex: 0 0 auto;
-  flex: 0 0 auto;
-  background-color: #ddd;
-  padding: 20px;
-  text-align: right;
-}
-
-.wizard .stage .header {
-  -webkit-flex: 0 0 auto;
-  flex: 0 0 auto;
-  padding: 20px;
-  background-color: #333;
-  color: white;
-  font-size: 2em;
-  span {
-    margin-left: 10px;
-    font-size: 0.5em;
-  }
-}
-
-.wizard .stage .body {
-  -webkit-flex: 1 1 auto;
-  flex: 1 1 auto;
-  padding: 20px;
-  overflow: auto;
-  display: -webkit-flex;
-  display: flex;
-  -webkit-flex-direction: column;
-  flex-direction: column;
-}
\ No newline at end of file
diff --git a/www/app/css/bootstrap.less b/www/app/css/bootstrap.less
deleted file mode 100644
index 3b772284..00000000
--- a/www/app/css/bootstrap.less
+++ /dev/null
@@ -1,5 +0,0 @@
-//
-// require npm modules 'bootstrap'
-//
-@import "~bootstrap/less/bootstrap";
-
diff --git a/www/app/css/common.css b/www/app/css/common.css
deleted file mode 100644
index acf164ee..00000000
--- a/www/app/css/common.css
+++ /dev/null
@@ -1,80 +0,0 @@
-body {
-  padding: 0;
-  margin: 0;
-}
-
-.debug-panel {
-  background-color: rgba(0,0,0,0.1);
-  position: absolute;
-  bottom: 0;
-  left: 50%;
-  z-index: 1000;
-  padding: 20px;
-}
-.debug-panel a {
-  cursor: pointer;
-  margin: 10px;
-}
-
-
-.area-light {
-  background-color: #f7f7f7;
-}
-.area-dark {
-  background-color: #e7e7e7;
-}
-.area-clear {
-  background-color: #fff;
-}
-
-/*
- * Greeter
- */
-.greeter {
-  border-color: #555;
-  border-width: 8px;
-}
-
-/*
- * bootstrap
- */
-
-.help-block {
-  margin: 2px 0 0 0;
-  font-size: small;
-  opacity: 0.7;
-}
-.btn.btn-inverse {
-  color: white;
-  background-color: #333;
-}
-.btn.btn-flat {
-  border-color: transparent;
-  background-color: transparent;
-}
-
-/*.btn.btn-default {
-  background-color: #eee !important;
-}
-*/
-
-/*
- * center component
- */
-
-.center-container {
-  position: absolute;
-  display: -webkit-flex;
-  -webkit-flex-flow: row nowrap;
-  -webkit-justify-content: center;
-  -webkit-align-content: center;
-  -webkit-align-items: center;
-  top: 0px;
-  left: 0px;
-  height: 100%;
-  width: 100%;
-}
-
-.center-container .center-item {
-  -webkit-flex: 0 1 auto;
-}
diff --git a/www/app/img/arrow-down.svg b/www/app/img/arrow-down.svg
deleted file mode 100644
index 14c4b5d1..00000000
--- a/www/app/img/arrow-down.svg
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg4986"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="arrow-down.svg">
-  <defs
-     id="defs4988" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="16"
-     inkscape:cy="16"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="869"
-     inkscape:window-height="648"
-     inkscape:window-x="1049"
-     inkscape:window-y="24"
-     inkscape:window-maximized="0" />
-  <metadata
-     id="metadata4991">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <g
-       transform="matrix(0.38339748,0,0,0.40552158,-78.363595,-91.56011)"
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="text3021">
-      <path
-         inkscape:connector-curvature="0"
-         d="m 277.42382,253.6675 c -7e-5,1.41967 -0.4956,2.62503 -1.48661,3.61607 l -26.15625,26.15625 c -1.0179,1.01786 -2.23664,1.52679 -3.65625,1.52679 -1.44646,0 -2.65182,-0.50893 -3.61607,-1.52679 l -26.15625,-26.15625 c -1.01786,-0.96426 -1.52679,-2.16961 -1.52679,-3.61607 0,-1.41961 0.50893,-2.63836 1.52679,-3.65625 l 2.97321,-3.01339 c 1.04464,-0.99104 2.26338,-1.48657 3.65625,-1.48661 1.41963,4e-5 2.62499,0.49557 3.61607,1.48661 l 19.52679,19.52678 19.52678,-19.52678 c 0.99102,-0.99104 2.19638,-1.48657 3.61608,-1.48661 1.39279,4e-5 2.61154,0.49557 3.65625,1.48661 l 3.01339,3.01339 c 0.99101,1.04468 1.48654,2.26343 1.48661,3.65625"
-         id="path3147" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/arrow-up.svg b/www/app/img/arrow-up.svg
deleted file mode 100644
index 30ea8fde..00000000
--- a/www/app/img/arrow-up.svg
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg4938"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="arrow-up.svg">
-  <defs
-     id="defs4940" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="16"
-     inkscape:cy="16"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="852"
-     inkscape:window-height="637"
-     inkscape:window-x="926"
-     inkscape:window-y="356"
-     inkscape:window-maximized="0" />
-  <metadata
-     id="metadata4943">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <g
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="text3017"
-       transform="matrix(0.38339753,0,0,0.40552158,-32.550528,-82.646856)">
-      <path
-         d="m 156.40467,251.17438 c 1.01779,1.01787 1.52672,2.23662 1.52678,3.65625 -6e-5,1.41966 -0.4956,2.62501 -1.48661,3.61607 l -3.01339,3.0134 c -1.01791,1.01786 -2.23666,1.52679 -3.65625,1.52678 -1.44648,1e-5 -2.65184,-0.50892 -3.61607,-1.52678 l -19.52679,-19.48661 -19.52678,19.48661 c -0.9643,1.01786 -2.16966,1.52679 -3.61607,1.52678 -1.44644,1e-5 -2.6518,-0.50892 -3.616074,-1.52678 l -3.013393,-3.0134 c -1.017859,-0.96427 -1.526787,-2.16963 -1.526785,-3.61607 -2e-6,-1.41963 0.508926,-2.63838 1.526785,-3.65625 l 26.156247,-26.15625 c 0.99104,-0.99103 2.1964,-1.48656 3.61607,-1.48661 1.39283,5e-5 2.61158,0.49558 3.65625,1.48661 l 26.11608,26.15625"
-         id="path3144"
-         inkscape:connector-curvature="0" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/cloud.svg b/www/app/img/cloud.svg
deleted file mode 100644
index bf7a94af..00000000
--- a/www/app/img/cloud.svg
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg4890"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="New document 26">
-  <defs
-     id="defs4892" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="16"
-     inkscape:cy="16"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="825"
-     inkscape:window-height="672"
-     inkscape:window-x="1093"
-     inkscape:window-y="24"
-     inkscape:window-maximized="0" />
-  <metadata
-     id="metadata4895">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <g
-       transform="matrix(0.36296297,0,0,0.35353541,-29.370372,-250.08767)"
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="text3105">
-      <path
-         inkscape:connector-curvature="0"
-         d="m 163.57143,765.50502 c -8e-5,4.25894 -1.51347,7.9018 -4.54018,10.92857 -3.00007,3.00001 -6.62953,4.5 -10.88839,4.5 l -43.71429,0 c -4.955367,0 -9.200898,-1.75446 -12.736604,-5.26339 -3.50893,-3.5357 -5.263392,-7.78124 -5.263392,-12.73661 0,-3.53569 0.950892,-6.76336 2.852678,-9.68303 1.901781,-2.9464 4.406243,-5.14283 7.513393,-6.58929 -0.05358,-0.74996 -0.08037,-1.32585 -0.08036,-1.72768 -1e-5,-5.67853 2.008916,-10.52674 6.026785,-14.54464 4.01784,-4.0178 8.86605,-6.02673 14.54465,-6.02678 4.2321,5e-5 8.06246,1.17862 11.49107,3.53571 3.45531,2.35719 5.97316,5.43755 7.55357,9.24107 1.87495,-1.66067 4.09816,-2.49102 6.66964,-2.49107 2.83923,5e-5 5.26333,1.00451 7.27232,3.01339 2.00887,2.00897 3.01333,4.43308 3.0134,7.27232 -7e-5,2.00897 -0.54918,3.85718 -1.64732,5.54465 3.45528,0.8036 6.30796,2.61163 8.55803,5.4241 2.24992,2.78574 3.37492,5.98663 3.375,9.60268"
-         id="path3210" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/disabled.svg b/www/app/img/disabled.svg
deleted file mode 100644
index 90804d83..00000000
--- a/www/app/img/disabled.svg
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3243"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="disabled.svg">
-  <defs
-     id="defs3245" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="17.941968"
-     inkscape:cy="7.7251942"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:snap-global="false"
-     inkscape:window-width="1879"
-     inkscape:window-height="1056"
-     inkscape:window-x="1961"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3273" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3248">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#808080;stroke:#000000;stroke-width:1.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
-       d="m 26.607098,9.8590516 c 1.095246,1.8715424 1.642877,3.9185224 1.642902,6.1409504 -2.5e-5,2.222447 -0.547656,4.274744 -1.642902,6.156901 -1.095289,1.871529 -2.584005,3.354927 -4.466145,4.450195 C 20.269406,27.702363 18.222425,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.843099,26.607098 7.9715637,25.51183 6.4881673,24.028432 5.3929019,22.156903 4.2976327,20.274746 3.75,18.222449 3.75,16.000002 3.75,13.777574 4.2976327,11.730594 5.3929019,9.8590516 6.4881673,7.9769072 7.9715637,6.4881927 9.843099,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222425,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.88214,1.0952865 3.370856,2.584001 4.466145,4.4661454"
-       id="path3275"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#000000;fill-opacity:1;stroke:none"
-       d="m 8.0269717,14.182874 15.9772693,0.09375 1e-6,3.585262 -15.9772703,-0.09375 z"
-       id="rect4059"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-  </g>
-</svg>
diff --git a/www/app/img/envelope.svg b/www/app/img/envelope.svg
deleted file mode 100644
index 5775e097..00000000
--- a/www/app/img/envelope.svg
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3145"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="envelope.svg">
-  <defs
-     id="defs3147" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="22.627417"
-     inkscape:cx="14.496338"
-     inkscape:cy="14.835533"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="1879"
-     inkscape:window-height="1056"
-     inkscape:window-x="1961"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1"
-     showguides="true"
-     inkscape:guide-bbox="true" />
-  <metadata
-     id="metadata3150">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       inkscape:connector-curvature="0"
-       id="path4141"
-       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       d="m 29.999988,11.952225 c -0.46857,0.485929 -0.999616,0.912958 -1.561898,1.281087 -2.608371,1.663939 -5.23236,3.342604 -7.778252,5.08017 -1.311994,0.912958 -2.93637,2.032068 -4.638839,2.032068 l -0.01561,0 -0.01561,0 c -1.702468,0 -3.326844,-1.11911 -4.638839,-2.032068 -2.5458975,-1.752291 -5.1698865,-3.416231 -7.7626395,-5.08017 -0.577899,-0.368129 -1.108945,-0.795158 -1.577515,-1.281087 l 0,12.644634 C 2.0107855,25.89267 2.4356205,26 3.8100905,26 l 24.4683525,0 c 1.37447,0 1.721558,-0.253927 1.721558,-1.549738 l 0,-12.498037 z m 0,-4.562107 C 29.999988,6.313336 29.739813,6 28.284765,6 L 3.4515205,6 c -1.67123,0 -1.440748,0.925932 -1.440748,2.368995 0,2.622709 1.095024,3.007381 2.686464,4.021712 2.420943,1.590314 4.873125,3.180628 7.2940645,4.785667 1.015235,0.662631 2.90163,2.017343 3.998462,2.017343 l 0.01561,0 0.01561,0 c 1.265137,0 2.983227,-1.354712 3.99846,-2.017343 2.42094,-1.605039 4.873122,-3.195353 7.309683,-4.785667 2.638916,-1.960744 2.670846,-1.966834 2.670846,-5.045368 z"
-       sodipodi:nodetypes="cccscscccsssscccsssccscscccc" />
-  </g>
-</svg>
diff --git a/www/app/img/gear.svg b/www/app/img/gear.svg
deleted file mode 100644
index be5a5b33..00000000
--- a/www/app/img/gear.svg
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg4177"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="gear.svg">
-  <defs
-     id="defs4179" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="11.400883"
-     inkscape:cy="17.428852"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="962"
-     inkscape:window-height="797"
-     inkscape:window-x="874"
-     inkscape:window-y="135"
-     inkscape:window-maximized="0" />
-  <metadata
-     id="metadata4182">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="path3123"
-       d="m 19.299476,19.29948 c 0.911441,-0.91145 1.367172,-2.011273 1.36719,-3.299478 -1.8e-5,-1.288183 -0.455749,-2.388006 -1.36719,-3.299479 -0.911473,-0.911445 -2.0113,-1.367172 -3.299478,-1.36719 -1.288206,1.8e-5 -2.388033,0.455745 -3.299479,1.36719 -0.911468,0.911473 -1.367195,2.011296 -1.367186,3.299479 -9e-6,1.288205 0.455718,2.388028 1.367186,3.299478 0.911446,0.911463 2.011273,1.367195 3.299479,1.367186 1.288178,9e-6 2.388005,-0.455723 3.299478,-1.367186 M 30,14.01302 l 0,4.046878 c -3.2e-5,0.145843 -0.04864,0.285597 -0.145834,0.419268 -0.09725,0.133692 -0.218781,0.212682 -0.364583,0.236978 l -3.372397,0.510417 c -0.230927,0.656259 -0.467905,1.209211 -0.710936,1.658854 0.42532,0.607645 1.075496,1.446189 1.950518,2.515628 0.121501,0.145838 0.182266,0.297747 0.182293,0.455727 -2.7e-5,0.157989 -0.05471,0.297747 -0.164064,0.419272 -0.32815,0.449657 -0.929711,1.105903 -1.804688,1.968747 -0.875022,0.862849 -1.446203,1.294272 -1.713539,1.294272 -0.145856,0 -0.30384,-0.05468 -0.473961,-0.16406 L 20.867185,25.40625 c -0.534739,0.279517 -1.087691,0.510416 -1.658854,0.69271 -0.194462,1.652774 -0.370676,2.782982 -0.528646,3.390623 -0.08508,0.340277 -0.303836,0.510416 -0.656251,0.510416 l -4.046873,0 c -0.170148,0 -0.322062,-0.05469 -0.455727,-0.164064 -0.121543,-0.09722 -0.188383,-0.224828 -0.200524,-0.382812 L 12.809894,26.09896 c -0.595496,-0.194444 -1.142372,-0.419272 -1.640625,-0.674481 l -2.5703126,1.950522 c -0.1215336,0.109375 -0.2734427,0.16406 -0.4557272,0.16406 -0.1701434,0 -0.322057,-0.06684 -0.4557317,-0.200519 C 6.1562431,25.953126 5.1536442,24.932293 4.6796872,24.276042 4.5946132,24.154517 4.5520785,24.014759 4.552083,23.85677 4.5520785,23.71094 4.6006928,23.571186 4.697917,23.437498 4.880206,23.182294 5.1900993,22.781256 5.6276057,22.234376 6.0650985,21.675354 6.3932261,21.243931 6.6119793,20.940104 6.2838472,20.332472 6.0347185,19.730911 5.8645842,19.135416 L 2.5286463,18.643229 C 2.3706576,18.618911 2.2430535,18.546014 2.1458338,18.424481 2.0486097,18.29081 1.9999999,18.144976 1.9999999,17.986979 l 0,-4.046874 c 0,-0.14582 0.04861,-0.285574 0.1458339,-0.419272 0.09722,-0.133665 0.2126736,-0.21266 0.3463529,-0.236978 L 5.882814,12.773438 C 6.0529484,12.214425 6.2899269,11.655399 6.5937495,11.096354 6.1076333,10.403662 5.4574623,9.5651225 4.6432275,8.5807307 4.5216985,8.4349149 4.4609339,8.2890809 4.4609385,8.1432288 4.4609339,8.0217224 4.5156234,7.8819635 4.6249977,7.7239612 4.9409706,7.286482 5.5364521,6.6363065 6.4114559,5.7734392 7.2986054,4.8984626 7.8758617,4.4609607 8.1432292,4.460938 c 0.1579796,2.27e-5 0.3159638,0.060787 0.473957,0.1822936 L 11.13281,6.5937491 c 0.534712,-0.2794906 1.087664,-0.510394 1.658854,-0.6927057 0.194434,-1.6527563 0.370648,-2.7829639 0.528646,-3.3906274 0.08506,-0.3402505 0.303809,-0.5103894 0.656251,-0.5104166 l 4.046873,0 c 0.170125,2.72e-5 0.315959,0.054717 0.437502,0.1640638 0.133666,0.097247 0.20658,0.224851 0.218749,0.3828125 l 0.510416,3.3541677 c 0.595468,0.1944665 1.142345,0.4192902 1.640625,0.6744804 l 2.588542,-1.950522 c 0.109357,-0.1093516 0.255186,-0.1640411 0.437502,-0.1640638 0.157962,2.27e-5 0.309871,0.060787 0.455728,0.1822936 1.567686,1.4462032 2.57029,2.4791866 3.007814,3.0989549 0.08504,0.097242 0.127577,0.2309261 0.127605,0.4010423 -2.8e-5,0.1458521 -0.04864,0.285611 -0.145834,0.4192721 -0.182317,0.2552264 -0.492214,0.6623438 -0.929689,1.2213521 -0.437525,0.546894 -0.765648,0.972242 -0.984374,1.276042 0.315946,0.607659 0.565079,1.20314 0.747395,1.786458 l 3.335938,0.510416 c 0.157957,0.02432 0.285561,0.103313 0.382813,0.236983 0.09719,0.121539 0.145802,0.261297 0.145834,0.419268"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
diff --git a/www/app/img/mask.svg b/www/app/img/mask.svg
deleted file mode 100644
index f254c5e0..00000000
--- a/www/app/img/mask.svg
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="330"
-   height="174"
-   id="svg3088"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="mask.svg"
-   inkscape:export-filename="/home/elijah/dev/leap/leap_assets/svg/masks/favicon.png"
-   inkscape:export-xdpi="5.625"
-   inkscape:export-ydpi="5.625">
-  <defs
-     id="defs3090" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="-53.46511"
-     inkscape:cy="109.89316"
-     inkscape:current-layer="outline"
-     showgrid="false"
-     inkscape:document-units="px"
-     inkscape:grid-bbox="true"
-     showguides="true"
-     inkscape:guide-bbox="true"
-     inkscape:snap-global="false"
-     objecttolerance="20"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:snap-bbox-midpoints="true"
-     inkscape:object-paths="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:snap-midpoints="true"
-     inkscape:snap-object-midpoints="true"
-     inkscape:snap-center="true"
-     borderlayer="true"
-     inkscape:window-width="1205"
-     inkscape:window-height="835"
-     inkscape:window-x="547"
-     inkscape:window-y="65"
-     inkscape:window-maximized="0"
-     fit-margin-top="0"
-     fit-margin-left="0"
-     fit-margin-right="0"
-     fit-margin-bottom="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3096"
-       empspacing="5"
-       visible="true"
-       enabled="true"
-       snapvisiblegridlinesonly="true"
-       originx="93.682631"
-       originy="42.998542" />
-    <sodipodi:guide
-       orientation="1,0"
-       position="1593.6826,42.998542"
-       id="guide3104" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3093">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="rainbow"
-     inkscape:groupmode="layer"
-     transform="translate(93.682631,67.00146)"
-     style="display:inline" />
-  <g
-     inkscape:groupmode="layer"
-     id="layer3"
-     inkscape:label="mask"
-     style="display:inline"
-     transform="translate(93.682631,-124.99854)">
-    <g
-       style="display:inline"
-       id="mask_group"
-       transform="matrix(3.9239993,0,0,3.9239993,3.1838955,50.448418)">
-      <g
-         style="display:inline;fill:none;stroke:none"
-         id="eyes"
-         transform="matrix(0.09628644,0,0,0.09628644,-13.900612,-8.2957126)">
-        <path
-           inkscape:connector-curvature="0"
-           id="path3911"
-           d="m 290.22501,383.61826 c 20.26573,-0.36718 40.73803,7.19583 59.875,14.1875 16.78643,6.13291 31.49775,16.91475 46.90625,25.96875 10.4842,6.16049 30.96875,19.28125 30.96875,19.28125 0,0 -38.82581,37.01847 -66.03125,43.375 -32.32466,7.55263 -60.041,1.91037 -89.21875,-21.40625 -16.83219,-13.45099 -39.0625,-56.3125 -39.0625,-56.3125 0,0 30.15123,-21.96471 47.90625,-24.4375 2.88277,-0.40149 5.76115,-0.6038 8.65625,-0.65625 z"
-           style="fill:none;stroke:none" />
-        <path
-           inkscape:connector-curvature="0"
-           id="path3905"
-           d="m 662.31876,383.61826 c 3.62101,-0.0338 7.24028,0.15439 10.84375,0.65625 17.75502,2.47279 47.90625,24.4375 47.90625,24.4375 0,0 -22.23031,42.86151 -39.0625,56.3125 -29.17775,23.31662 -56.92534,28.95888 -89.25,21.40625 -27.20544,-6.35653 -66,-43.375 -66,-43.375 0,0 20.48455,-13.12076 30.96875,-19.28125 15.4085,-9.054 30.11982,-19.83584 46.90625,-25.96875 18.4535,-6.74197 38.13407,-14.00499 57.6875,-14.1875 z"
-           style="fill:none;stroke:none" />
-      </g>
-      <g
-         transform="matrix(0.09628644,0,0,0.09628644,-13.900612,-8.2957126)"
-         id="outline"
-         style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:34.52383041;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
-        <path
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:34.52383041;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-           d="m 15.279428,283.46896 c -25.395028,0.29417 -55.766384,5.34416 -78.180213,28.13341 -41.610535,42.30754 -51.078425,104.6937 -48.792595,157.09543 3.09273,70.89956 32.510261,141.31677 77.548216,194.99548 31.2063109,37.1934 76.153265,78.38052 133.88163,80.25276 47.504344,1.54066 79.677684,-32.88465 104.209624,-52.03872 20.47502,-15.98652 48.9415,-34.77084 73.785,-45.16092 23.14757,-9.6808 48.52598,-9.92084 46.79322,-9.92791 l 0.0516,0 0.18099,0 0.0374,0 c -0.66133,10e-4 23.72172,0.24775 46.86792,9.92791 24.83448,10.3863 53.25033,29.1637 73.73901,45.16092 24.54335,19.16296 56.75051,53.57941 104.25562,52.03872 57.72835,-1.87224 102.6753,-43.05936 133.88161,-80.25276 45.03797,-53.67871 74.4555,-124.09592 77.54823,-194.99548 2.28583,-52.40173 -7.18206,-114.78789 -48.7926,-157.09543 -23.93638,-24.33729 -56.72446,-28.56338 -83.25052,-28.08445 -48.19226,0.87016 -84.29897,26.42928 -118.66787,41.05093 -54.0567,22.99749 -87.02303,49.00558 -132.76414,63.00349 -21.77603,6.66401 -34.39981,5.08622 -52.7914,5.43773 0.056,0.003 -0.3257,-9.9e-4 -0.20684,0 l 0.0517,0.003 C 306.19197,392.6499 293.62549,394.2572 271.7817,387.57244 226.04064,373.57447 193.0743,347.56638 139.0176,324.56889 109.7526,312.1186 85.70157,293.58692 47.530399,287.26753 Z m 34.627384,178.97027 c 23.183949,-0.42116 50.684478,9.04508 78.892638,19.37771 20.17182,7.38892 40.67441,22.09527 64.84232,36.33321 4.96838,2.92699 8.62977,5.38188 15.53835,9.70038 -2.26657,1.82188 -3.6667,3.15806 -6.13031,5.0518 -18.1082,13.91961 -41.91482,28.10355 -53.89449,30.90989 C 107.47079,573.5771 77.906523,567.87377 39.556515,537.14768 33.193809,532.04987 18.009057,512.07472 7.2933506,494.69124 4.3472031,489.91186 2.3990912,486.35502 -0.00326604,482.19423 1.7100438,481.12731 2.8139795,480.32504 4.6648396,479.22192 17.902804,471.33198 35.387531,463.8423 40.217235,463.16791 c 3.107262,-0.43388 6.308905,-0.66727 9.689577,-0.72868 z m 547.057498,0.003 c 4.28838,-0.0401 8.3533,0.18703 12.2118,0.72581 4.82969,0.67438 22.31443,8.16408 35.5524,16.05399 1.85085,1.10314 2.95478,1.9054 4.6681,2.97234 -2.40237,4.16077 -4.35047,7.71762 -7.29663,12.49702 -10.71569,17.38346 -25.90044,37.3586 -32.26315,42.45641 -38.34309,30.72057 -67.96379,36.42857 -109.64478,26.66454 -11.97755,-2.80584 -35.76998,-16.98865 -53.86576,-30.907 -2.46056,-1.89253 -3.85789,-3.22816 -6.1217,-5.04893 6.91251,-4.32099 10.57578,-6.77746 15.54699,-9.70614 24.16791,-14.23793 44.67048,-28.94429 64.84232,-36.33321 27.19699,-9.96224 53.7382,-19.16304 76.37041,-19.37483 z"
-           id="path3871"
-           inkscape:connector-curvature="0"
-           sodipodi:nodetypes="cssssssccccssssssssscccssccssscsssscsssssscsssscsss" />
-      </g>
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/off.svg b/www/app/img/off.svg
deleted file mode 100644
index 35c49a56..00000000
--- a/www/app/img/off.svg
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3243"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="off.svg">
-  <defs
-     id="defs3245" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="2.564547"
-     inkscape:cy="9.5625231"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:snap-global="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1056"
-     inkscape:window-x="1920"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3273" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3248">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f08080;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
-       d="m 26.607099,9.8590516 c 1.095245,1.8715414 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222445 -0.547656,4.274746 -1.642901,6.156901 -1.095291,1.87153 -2.584007,3.354927 -4.466146,4.450195 C 20.269405,27.702363 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.8430986,26.607098 7.9715637,25.51183 6.4881674,24.028433 5.3929018,22.156903 4.2976327,20.274748 3.75,18.222447 3.75,16.000002 3.75,13.777574 4.2976327,11.730593 5.3929018,9.8590516 6.4881674,7.9769071 7.9715637,6.4881926 9.8430986,5.392906 11.725251,4.2976563 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269405,0.5476563 6.140953,1.642906 1.882139,1.0952866 3.370855,2.5840011 4.466146,4.4661456"
-       id="path3275"
-       inkscape:connector-curvature="0" />
-    <g
-       id="g3758"
-       transform="matrix(0.865313,0,0,0.865313,2.163376,2.175981)">
-      <rect
-         transform="matrix(-0.70710678,-0.70710678,-0.70710678,0.70710678,0,0)"
-         style="fill:#000000;fill-opacity:1;stroke:none"
-         id="rect4057"
-         width="18"
-         height="3"
-         x="-31.781626"
-         y="-1.4338233"
-         ry="0" />
-      <rect
-         transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"
-         ry="0"
-         y="21.281626"
-         x="-9.0661755"
-         height="3"
-         width="18"
-         id="rect4059"
-         style="fill:#000000;fill-opacity:1;stroke:none" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/on.svg b/www/app/img/on.svg
deleted file mode 100644
index 36938e0a..00000000
--- a/www/app/img/on.svg
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3243"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="on.svg">
-  <defs
-     id="defs3245" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="76.360017"
-     inkscape:cy="-4.203893"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:snap-global="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1056"
-     inkscape:window-x="1920"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3273" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3248">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#9acd32;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
-       d="m 26.607098,9.8590516 c 1.095246,1.8715424 1.642877,3.9185224 1.642902,6.1409504 -2.5e-5,2.222447 -0.547656,4.274744 -1.642902,6.156901 -1.095289,1.871529 -2.584005,3.354927 -4.466145,4.450195 C 20.269406,27.702363 18.222425,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702363 9.843099,26.607098 7.9715637,25.51183 6.4881673,24.028432 5.3929019,22.156903 4.2976327,20.274746 3.75,18.222449 3.75,16.000002 3.75,13.777574 4.2976327,11.730594 5.3929019,9.8590516 6.4881673,7.9769072 7.9715637,6.4881927 9.843099,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222425,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.88214,1.0952865 3.370856,2.584001 4.466145,4.4661454"
-       id="path3275"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:#000000;fill-opacity:1;stroke:none"
-       d="M 16.816995,19.463899 11.276562,13.923466 8.7414,16.458629 14.281832,21.999061 z"
-       id="rect4057"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-    <path
-       style="fill:#000000;fill-opacity:1;stroke:none"
-       d="m 11.792505,19.521247 9.473539,-9.340956 2.535164,2.535163 -9.473539,9.340957 z"
-       id="rect4059"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccccc" />
-  </g>
-</svg>
diff --git a/www/app/img/planet.svg b/www/app/img/planet.svg
deleted file mode 100644
index 5697d5d6..00000000
--- a/www/app/img/planet.svg
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3069"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="planet.svg">
-  <defs
-     id="defs3071" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="-15.657999"
-     inkscape:cy="16"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="1879"
-     inkscape:window-height="1056"
-     inkscape:window-x="1961"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1" />
-  <metadata
-     id="metadata3074">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       inkscape:connector-curvature="0"
-       d="M 25.899531,6.1005234 C 28.543727,8.7447734 30,12.260523 30,16.000055 30,19.739586 28.543727,23.255281 25.899531,25.899531 23.255227,28.543727 19.739531,30 16,30 12.260469,30 8.7447734,28.543727 6.1004687,25.899531 3.4562734,23.255281 2,19.739586 2,16.000055 2,12.260523 3.4561641,8.7447734 6.1004687,6.1005234 8.7447734,3.4562734 12.260469,2 16,2 c 3.739531,0 7.255227,1.4562734 9.899531,4.1005234 z M 24.662062,24.662117 c 1.724243,-1.724297 2.870711,-3.872094 3.343375,-6.208726 -0.368429,0.54239 -0.720835,0.743586 -0.939093,-0.470477 -0.224766,-1.979414 -2.042797,-0.714984 -3.185985,-1.418047 -1.203234,0.810961 -3.907586,-1.576695 -3.447992,1.116281 0.709133,1.214664 3.828344,-1.625586 2.273578,0.944508 -0.991867,1.794188 -3.626929,5.767672 -3.284148,7.827367 0.04326,3.000813 -3.066164,0.625735 -4.137492,-0.369687 -0.720672,-1.993906 -0.245602,-5.478977 -2.130024,-6.455477 -2.045367,-0.08881 -3.8008904,-0.274695 -4.5935857,-2.561289 -0.477039,-1.635922 0.5076094,-4.07132 2.2607267,-4.447297 2.566211,-1.612351 3.482883,1.888196 5.88957,1.953274 0.74725,-0.781867 2.784031,-1.030477 2.952906,-1.907227 -1.579101,-0.278633 2.003422,-1.327758 -0.151156,-1.924508 -1.188633,0.139782 -1.954476,1.232493 -1.322562,2.159008 -2.303383,0.537086 -2.377157,-3.3333122 -4.591289,-2.112523 -0.05627,1.930195 -3.6154457,0.625789 -1.231454,0.23439 0.81911,-0.357875 -1.336015,-1.3949683 -0.171718,-1.2065151 0.571922,-0.031062 2.497359,-0.7057969 1.976297,-1.1594297 1.072148,-0.6655469 1.973125,1.5938668 3.022523,-0.051461 C 17.95218,7.3391953 16.876805,7.105625 15.927156,7.7468906 15.391766,7.1474062 16.87243,5.852625 18.178422,5.2931172 18.61368,5.1066328 19.029414,5.0050234 19.347258,5.0337891 20.005094,5.7937266 21.221672,5.9253594 21.285328,4.9424062 19.656133,4.1621797 17.859812,3.75 16,3.75 13.330648,3.75 10.791836,4.5983125 8.6907969,6.1669141 9.2554453,6.4255859 9.5759688,6.7476406 9.0319922,7.1593828 8.6093672,8.4186719 6.8945312,10.109117 5.3891484,9.8698047 4.6075,11.217742 4.0927266,12.702781 3.8726641,14.259242 c 1.260875,0.417156 1.5515937,1.242774 1.2806718,1.518946 -0.6424687,0.560218 -1.0373125,1.354335 -1.24075,2.223648 0.4104297,2.511141 1.5906407,4.825516 3.4253516,6.660281 C 9.6516563,26.975781 12.727883,28.25 16,28.25 c 3.272062,0 6.348344,-1.274219 8.662062,-3.587883 z"
-       id="path67" />
-  </g>
-</svg>
diff --git a/www/app/img/unknown.svg b/www/app/img/unknown.svg
deleted file mode 100644
index 60038638..00000000
--- a/www/app/img/unknown.svg
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3243"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="question.svg">
-  <defs
-     id="defs3245" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="7.9999996"
-     inkscape:cx="12.965251"
-     inkscape:cy="9.8979426"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:snap-global="false"
-     inkscape:window-width="1328"
-     inkscape:window-height="840"
-     inkscape:window-x="1987"
-     inkscape:window-y="24"
-     inkscape:window-maximized="0">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3273" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3248">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f0e68c;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
-       d="m 26.607099,9.8590516 c 1.095245,1.8715424 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222446 -0.547656,4.274745 -1.642901,6.1569 -1.09529,1.871531 -2.584007,3.354928 -4.466146,4.450196 C 20.269406,27.702362 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702362 9.8430986,26.607098 7.9715638,25.51183 6.4881674,24.028433 5.392902,22.156902 4.2976328,20.274747 3.75,18.222448 3.75,16.000002 3.75,13.777574 4.2976328,11.730594 5.392902,9.8590516 6.4881674,7.9769072 7.9715638,6.4881927 9.8430986,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.882139,1.0952865 3.370856,2.584001 4.466146,4.4661454"
-       id="path3275"
-       inkscape:connector-curvature="0" />
-    <g
-       transform="matrix(0.93378097,0,0,0.80060225,2.1910082,2.2390946)"
-       style="font-size:24.20675278px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FreeSans;-inkscape-font-specification:FreeSans"
-       id="text2987">
-      <path
-         style="font-weight:bold;font-family:Droid Sans;-inkscape-font-specification:Droid Sans Bold"
-         d="m 13.191315,20.187003 0,-0.859172 c -3e-6,-0.394747 0.03717,-0.750801 0.111525,-1.068159 0.08178,-0.325085 0.200741,-0.630826 0.35688,-0.917225 0.163567,-0.294121 0.371746,-0.576642 0.62454,-0.847561 0.252786,-0.27864 0.55762,-0.565031 0.914506,-0.859172 0.312264,-0.255419 0.576207,-0.483757 0.791827,-0.685016 0.21561,-0.201235 0.390332,-0.398613 0.524168,-0.592131 0.133823,-0.201235 0.230479,-0.410223 0.289965,-0.626963 0.05947,-0.216716 0.08921,-0.468275 0.08922,-0.754678 -7e-6,-0.441183 -0.144989,-0.804977 -0.434948,-1.09138 -0.282536,-0.286378 -0.717484,-0.429572 -1.304843,-0.429586 -0.513019,1.4e-5 -1.066927,0.112248 -1.661723,0.336702 -0.587368,0.224482 -1.197037,0.499262 -1.82901,0.82434 -1.453521,0.818948 -2.5794663,-1.530369 -1.137556,-2.565904 0.319705,-0.1934915 0.665432,-0.3753865 1.037183,-0.5456882 0.379184,-0.1780102 0.769521,-0.3328158 1.171013,-0.4644172 0.408922,-0.1393083 0.821564,-0.2476722 1.237927,-0.325092 0.416357,-0.077386 0.825282,-0.1160872 1.226776,-0.1161044 0.758365,1.72e-5 1.442385,0.096771 2.052061,0.2902608 0.609663,0.1857836 1.126395,0.4605635 1.550198,0.8243395 0.423786,0.3638075 0.747208,0.8050035 0.970268,1.3235875 0.230475,0.518614 0.345718,1.110745 0.345727,1.776396 -9e-6,0.48765 -0.05205,0.924976 -0.156135,1.311978 -0.104099,0.379285 -0.260234,0.735339 -0.468404,1.06816 -0.200755,0.325102 -0.457263,0.642454 -0.769524,0.952055 -0.304843,0.301881 -0.661723,0.623102 -1.070639,0.963666 -0.312278,0.255437 -0.568786,0.479906 -0.769524,0.673405 -0.193316,0.185774 -0.345734,0.363801 -0.457252,0.534079 -0.111531,0.170295 -0.189599,0.348321 -0.234203,0.534081 -0.03718,0.178033 -0.05576,0.390891 -0.05576,0.638574 l 0,0.696625 c -0.345863,2.197824 -2.53083,2.209243 -2.944261,0"
-         id="path2996"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="csccccccccccccccccccccccccccscscc" />
-      <path
-         style="font-weight:bold;font-family:Droid Sans;-inkscape-font-specification:Droid Sans Bold"
-         d="m 13.14689,25.354041 c -3e-6,-0.326839 0.03795,-0.601524 0.113851,-0.824056 0.08174,-0.229482 0.192668,-0.413764 0.332795,-0.552849 0.140122,-0.139078 0.303601,-0.239911 0.490435,-0.302501 0.192667,-0.06259 0.397016,-0.09388 0.613044,-0.09388 0.204344,3e-6 0.397016,0.03129 0.578014,0.09388 0.186828,0.06259 0.350305,0.163423 0.490434,0.302501 0.14012,0.139085 0.251052,0.323367 0.332796,0.552849 0.08174,0.222532 0.122603,0.497217 0.122609,0.824056 -6e-6,0.312934 -0.04088,0.580665 -0.122609,0.803194 -0.08175,0.222531 -0.192676,0.406813 -0.332796,0.552848 -0.140129,0.146036 -0.303606,0.250347 -0.490434,0.312933 -0.180998,0.06954 -0.37367,0.104311 -0.578014,0.104311 -0.216028,0 -0.420377,-0.03477 -0.613044,-0.104311 -0.186834,-0.06259 -0.350313,-0.166897 -0.490435,-0.312933 -0.140127,-0.146035 -0.251058,-0.330317 -0.332795,-0.552848 -0.07591,-0.222529 -0.113854,-0.49026 -0.113851,-0.803194"
-         id="path2992"
-         inkscape:connector-curvature="0" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/user.svg b/www/app/img/user.svg
deleted file mode 100644
index ad86a757..00000000
--- a/www/app/img/user.svg
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg4842"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="New document 23">
-  <defs
-     id="defs4844" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.197802"
-     inkscape:cx="16"
-     inkscape:cy="16"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:window-width="814"
-     inkscape:window-height="735"
-     inkscape:window-x="1104"
-     inkscape:window-y="24"
-     inkscape:window-maximized="0" />
-  <metadata
-     id="metadata4847">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <g
-       transform="matrix(0.42424241,0,0,0.45370367,-116.34456,-343.34663)"
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="text3085">
-      <path
-         inkscape:connector-curvature="0"
-         d="m 340.24077,812.48026 c -6e-5,3.21429 -0.97773,5.74553 -2.93304,7.59375 -1.9554,1.875 -4.55362,2.81249 -7.79464,2.8125 l -35.11607,0 c -3.24108,-1e-5 -5.83929,-0.9375 -7.79464,-2.8125 -1.95536,-1.84822 -2.93304,-4.37946 -2.93304,-7.59375 0,-1.41964 0.0402,-2.7991 0.12054,-4.13839 0.10714,-1.36606 0.29464,-2.82588 0.5625,-4.37947 0.29464,-1.58034 0.65625,-3.04016 1.08482,-4.37946 0.42857,-1.33927 1.00446,-2.63837 1.72768,-3.89732 0.72321,-1.28569 1.55356,-2.37051 2.49107,-3.25447 0.93749,-0.91069 2.07588,-1.6339 3.41518,-2.16964 1.36606,-0.53569 2.86606,-0.80354 4.5,-0.80357 0.24106,3e-5 0.80355,0.29467 1.6875,0.88393 0.88391,0.56252 1.87498,1.20538 2.97321,1.92857 1.12498,0.69645 2.57141,1.33931 4.33929,1.92857 1.79462,0.56252 3.58926,0.84377 5.38393,0.84375 1.79461,2e-5 3.57586,-0.28123 5.34375,-0.84375 1.7946,-0.58926 3.24103,-1.23212 4.33928,-1.92857 1.12496,-0.72319 2.12943,-1.36605 3.01339,-1.92857 0.88389,-0.58926 1.44639,-0.8839 1.6875,-0.88393 1.63389,3e-5 3.12049,0.26788 4.45983,0.80357 1.36602,0.53574 2.5178,1.25895 3.45535,2.16964 0.93745,0.88396 1.76781,1.96878 2.49107,3.25447 0.72317,1.25895 1.29906,2.55805 1.72768,3.89732 0.42852,1.3393 0.77673,2.79912 1.04465,4.37946 0.29458,1.55359 0.48208,3.01341 0.5625,4.37947 0.10708,1.33929 0.16065,2.71875 0.16071,4.13839 M 322.84345,765.7124 c 3.02674,3.00005 4.54013,6.62951 4.54018,10.8884 -5e-5,4.25896 -1.51344,7.90181 -4.54018,10.92857 -3.00004,3.00002 -6.6295,4.50002 -10.88839,4.5 -4.25896,2e-5 -7.90181,-1.49998 -10.92858,-4.5 -3.00001,-3.02676 -4.50001,-6.66961 -4.5,-10.92857 -10e-6,-4.25889 1.49999,-7.88835 4.5,-10.8884 3.02677,-3.02673 6.66962,-4.54012 10.92858,-4.54018 4.25889,6e-5 7.88835,1.51345 10.88839,4.54018"
-         id="path3195" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/img/wait.svg b/www/app/img/wait.svg
deleted file mode 100644
index 2ae0113a..00000000
--- a/www/app/img/wait.svg
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="32px"
-   height="32px"
-   id="svg3243"
-   version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="wait.svg">
-  <defs
-     id="defs3245" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="8"
-     inkscape:cx="-30.255727"
-     inkscape:cy="10.507144"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     inkscape:grid-bbox="true"
-     inkscape:document-units="px"
-     inkscape:snap-global="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1056"
-     inkscape:window-x="1920"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid3273" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata3248">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     id="layer1"
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer">
-    <path
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#f0e68c;stroke:#000000;stroke-width:1.50000000000000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;fill-opacity:1"
-       d="m 26.607099,9.8590516 c 1.095245,1.8715424 1.642877,3.9185224 1.642901,6.1409504 -2.4e-5,2.222446 -0.547656,4.274745 -1.642901,6.1569 -1.09529,1.871531 -2.584007,3.354928 -4.466146,4.450196 C 20.269406,27.702362 18.222426,28.249996 16,28.25 13.77755,28.249996 11.725251,27.702362 9.8430986,26.607098 7.9715638,25.51183 6.4881674,24.028433 5.392902,22.156902 4.2976328,20.274747 3.75,18.222448 3.75,16.000002 3.75,13.777574 4.2976328,11.730594 5.392902,9.8590516 6.4881674,7.9769072 7.9715638,6.4881927 9.8430986,5.3929062 11.725251,4.2976564 13.77755,3.7500236 16,3.75 c 2.222426,2.36e-5 4.269406,0.5476564 6.140953,1.6429062 1.882139,1.0952865 3.370856,2.584001 4.466146,4.4661454"
-       id="path3275"
-       inkscape:connector-curvature="0" />
-    <g
-       transform="matrix(0.23090279,0,0,0.23090276,-12.029146,-19.708792)"
-       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:FontAwesome;-inkscape-font-specification:FontAwesome"
-       id="text2993">
-      <path
-         inkscape:connector-curvature="0"
-         d="m 151.24208,161.07719 c -6e-5,0.13394 -0.0135,0.22769 -0.0402,0.28125 -1.71434,7.17858 -5.30363,13.00447 -10.76786,17.47767 -5.46433,4.44643 -11.86611,6.66964 -19.20535,6.66965 -3.91075,-1e-5 -7.70092,-0.73661 -11.37054,-2.20982 -3.64287,-1.47322 -6.89733,-3.5759 -9.76339,-6.30804 l -5.183039,5.18304 c -0.508932,0.50892 -1.11161,0.76339 -1.808035,0.76339 -0.696431,0 -1.299109,-0.25447 -1.808036,-0.76339 -0.508929,-0.50893 -0.763393,-1.11161 -0.763393,-1.80804 l 0,-18 c 0,-0.69641 0.254464,-1.29909 0.763393,-1.80804 0.508927,-0.5089 1.111605,-0.76337 1.808036,-0.76339 l 18.000004,0 c 0.6964,2e-5 1.29908,0.25449 1.80803,0.76339 0.50891,0.50895 0.76337,1.11163 0.76339,1.80804 -2e-5,0.69645 -0.25448,1.29912 -0.76339,1.80804 l -5.50446,5.50446 c 1.90176,1.76787 4.05801,3.13394 6.46875,4.09821 2.41069,0.9643 4.91515,1.44644 7.51339,1.44643 3.58925,10e-6 6.93746,-0.87053 10.04464,-2.6116 3.1071,-1.74107 5.59817,-4.13839 7.47322,-7.19197 0.29459,-0.45534 1.00441,-2.0223 2.12946,-4.70089 0.21424,-0.61605 0.61602,-0.92409 1.20536,-0.92411 l 7.71428,0 c 0.34816,2e-5 0.6428,0.13395 0.88393,0.40179 0.2678,0.24109 0.40173,0.53573 0.40179,0.88393 m 1.00446,-32.14286 0,18 c -6e-5,0.69646 -0.25452,1.29914 -0.76339,1.80803 -0.50899,0.50896 -1.11167,0.76343 -1.80804,0.7634 l -18,0 c -0.69646,3e-5 -1.29914,-0.25444 -1.80803,-0.7634 -0.50897,-0.50889 -0.76343,-1.11157 -0.76339,-1.80803 -4e-5,-0.6964 0.25442,-1.29907 0.76339,-1.80804 l 5.54464,-5.54464 c -3.96433,-3.6696 -8.63843,-5.50442 -14.02232,-5.50446 -3.58931,4e-5 -6.93752,0.87058 -10.04464,2.6116 -3.10716,1.74112 -5.59823,4.13844 -7.47322,7.19197 -0.29465,0.45539 -1.00447,2.02235 -2.12946,4.70089 -0.2143,0.6161 -0.61608,0.92414 -1.20536,0.92411 l -7.995534,0 c -0.348216,3e-5 -0.656252,-0.12051 -0.924107,-0.36161 -0.241073,-0.26783 -0.361608,-0.57586 -0.361608,-0.92411 l 0,-0.28125 c 1.741069,-7.17853 5.357137,-12.99102 10.848219,-17.4375 5.49105,-4.47316 11.91962,-6.70976 19.28571,-6.70982 3.91068,6e-5 7.71425,0.75006 11.41071,2.25 3.69639,1.47327 6.97763,3.56255 9.84375,6.26786 l 5.22322,-5.18304 c 0.50887,-0.50887 1.11155,-0.76334 1.80803,-0.76339 0.69637,5e-5 1.29905,0.25452 1.80804,0.76339 0.50887,0.50898 0.76333,1.11166 0.76339,1.80804"
-         id="path3126" />
-    </g>
-  </g>
-</svg>
diff --git a/www/app/index.html b/www/app/index.html
deleted file mode 100644
index 440e0b6f..00000000
--- a/www/app/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <meta charset="UTF-8">
-    <title>Bitmask</title>
-    <link rel="stylesheet" href="css/bootstrap.min.css">
-    <link rel="stylesheet" href="css/common.css">
-  </head>
-  <body>
-    <div id="app"></div>
-    <script src="app.bundle.js"></script>
-  </body>
-</html>
\ No newline at end of file
diff --git a/www/app/lib/bitmask.js b/www/app/lib/bitmask.js
deleted file mode 100644
index fedd5fcd..00000000
--- a/www/app/lib/bitmask.js
+++ /dev/null
@@ -1,306 +0,0 @@
-// bitmask.js
-// Copyright (C) 2016 LEAP
-//
-// This program 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 of the License, or
-// (at your option) any later version.
-//
-// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * bitmask object
- *
- * Contains all the bitmask API mapped by sections
- * - user. User management like login, creation, ...
- * - mail. Email service control.
- * - keys. Keyring operations.
- * - events. For registering to events.
- *
- * Every function returns a Promise that will be triggered once the request is
- * finished or will fail if there was any error. Errors are always user readable
- * strings.
- */
-
-try {
-    // Use Promises in non-ES6 compliant engines.
-    eval('import "babel-polyfill";')
-}
-catch (err) {}
-
-var bitmask = function(){
-    var event_handlers = {};
-
-    var api_url = '/API/';
-    if (window.location.protocol === "file:") {
-        api_url = 'http://localhost:7070/API/';
-    }
-    
-    function call(command) {
-        var url = api_url  + command.slice(0, 2).join('/');
-        var data = JSON.stringify(command.slice(2));
-
-        return new Promise(function(resolve, reject) {
-            var req = new XMLHttpRequest();
-            req.open('POST', url);
-            
-            req.onload = function() {
-                if (req.status == 200) {
-                    parseResponse(req.response, resolve, reject);
-                }
-                else {
-                    reject(Error(req.statusText));
-                }
-            };
-            
-            req.onerror = function() {
-                reject(Error("Network Error"));
-            };
-            
-            req.send(data);
-        });
-    };
-
-    function parseResponse(raw_response, resolve, reject) {
-        var response = JSON.parse(raw_response);
-        if (response.error === null) {
-            resolve(response.result);
-        } else {
-            reject(response.error);
-        }
-    };
-
-    function event_polling() {
-        call(['events', 'poll']).then(function(response) {
-            if (response !== null) {
-                evnt = response[0];
-                content = response[1];
-                if (evnt in event_handlers) {
-                    event_handlers[evnt](evnt, content);
-                }
-            }
-            event_polling();
-        }, function(error) {
-            setTimeout(event_polling, 5000);
-        });
-    };
-    event_polling();
-
-    function private_str(priv) {
-        if (priv) {
-            return 'private'
-        }
-        return 'public'
-    };
-
-    return {
-        bonafide: {
-            provider: {
-                create: function(domain) {
-                    return call(['bonafide', 'provider', 'create', domain]);
-                },
-
-                read: function(domain) {
-                    return call(['bonafide', 'provider', 'read', domain]);
-                },
-
-                delete: function(domain) {
-                    return call(['bonafide', 'provider', 'delete', domain]);
-                },
-
-                list: function(seeded) {
-                    if (typeof seeded !== 'boolean') {
-                        seeded = false;
-                    }
-                    return call(['bonafide', 'provider', 'list', seeded]);
-                }
-            },
-
-            /**
-             * uids are of the form user@provider.net
-             */
-            user: {
-                /**
-                 * Check wich user is active
-                 *
-                 * @return {Promise<string>} The uid of the active user
-                 */
-                active: function() {
-                    return call(['bonafide', 'user', 'active']);
-                },
-
-                /**
-                 * Register a new user
-                 *
-                 * @param {string} uid The uid to be created
-                 * @param {string} password The user password
-                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known
-                 *                           If it's not provided it will default to false
-                 */
-                create: function(uid, password, autoconf) {
-                    if (typeof autoconf !== 'boolean') {
-                        autoconf = false;
-                    }
-                    return call(['bonafide', 'user', 'create', uid, password, autoconf]);
-                },
-
-                /**
-                 * Login
-                 *
-                 * @param {string} uid The uid to log in
-                 * @param {string} password The user password
-                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known
-                 *                           If it's not provided it will default to false
-                 */
-                auth: function(uid, password, autoconf) {
-                    if (typeof autoconf !== 'boolean') {
-                        autoconf = false;
-                    }
-                    return call(['bonafide', 'user', 'authenticate', uid, password, autoconf]);
-                },
-
-                /**
-                 * Logout
-                 *
-                 * @param {string} uid The uid to log out.
-                 *                     If no uid is provided the active user will be used
-                 */
-                logout: function(uid) {
-                    if (typeof uid !== 'string') {
-                        uid = "";
-                    }
-                    return call(['bonafide', 'user', 'logout', uid]);
-                }
-            }
-        },
-
-        mail: {
-            /**
-             * Check the status of the email service
-             *
-             * @return {Promise<string>} User readable status
-             */
-            status: function() {
-                return call(['mail', 'status']);
-            },
-
-            /**
-             * Get the token of the active user.
-             *
-             * This token is used as password to authenticate in the IMAP and SMTP services.
-             *
-             * @return {Promise<string>} The token
-             */
-            get_token: function() {
-                return call(['mail', 'get_token']);
-            }
-        },
-
-        /**
-         * A KeyObject have the following attributes:
-         *   - address {string} the email address for wich this key is active
-         *   - fingerprint {string} the fingerprint of the key
-         *   - length {number} the size of the key bits
-         *   - private {bool} if the key is private
-         *   - uids {[string]} the uids in the key
-         *   - key_data {string} the key content
-         *   - validation {string} the validation level which this key was found
-         *   - expiry_date {string} date when the key expires
-         *   - refreshed_at {string} date of the last refresh of the key
-         *   - audited_at {string} date of the last audit (unused for now)
-         *   - sign_used {bool} if has being used to checking signatures
-         *   - enc_used {bool} if has being used to encrypt
-         */
-        keys: {
-            /**
-             * List all the keys in the keyring
-             *
-             * @param {boolean} priv Should list private keys?
-             *                       If it's not provided the public ones will be listed.
-             *
-             * @return {Promise<[KeyObject]>} List of keys in the keyring
-             */
-            list: function(priv) {
-                return call(['keys', 'list', private_str(priv)]);
-            },
-
-            /**
-             * Export key
-             *
-             * @param {string} address The email address of the key
-             * @param {boolean} priv Should get the private key?
-             *                       If it's not provided the public one will be fetched.
-             *
-             * @return {Promise<KeyObject>} The key
-             */
-            exprt: function(address, priv) {
-                return call(['keys', 'export', address, private_str(priv)]);
-            },
-
-            /**
-             * Insert key
-             *
-             * @param {string} address The email address of the key
-             * @param {string} rawkey The key material
-             * @param {string} validation The validation level of the key
-             *                            If it's not provided 'Fingerprint' level will be used.
-             *
-             * @return {Promise<KeyObject>} The key
-             */
-            insert: function(address, rawkey, validation) {
-                if (typeof validation !== 'string') {
-                    validation = 'Fingerprint';
-                }
-                return call(['keys', 'insert', address, validation, rawkey]);
-            },
-
-            /**
-             * Delete a key
-             *
-             * @param {string} address The email address of the key
-             * @param {boolean} priv Should get the private key?
-             *                       If it's not provided the public one will be deleted.
-             *
-             * @return {Promise<KeyObject>} The key
-             */
-            del: function(address, priv) {
-                return call(['keys', 'delete', address, private_str(priv)]);
-            }
-        },
-
-        events: {
-            /**
-             * Register func for an event
-             *
-             * @param {string} evnt The event to register
-             * @param {function} func The function that will be called on each event.
-             *                        It has to be like: function(event, content) {}
-             *                        Where content will be a list of strings.
-             */
-            register: function(evnt, func) {
-                event_handlers[evnt] = func;
-                return call(['events', 'register', evnt])
-            },
-
-            /**
-             * Unregister from an event
-             *
-             * @param {string} evnt The event to unregister
-             */
-            unregister: function(evnt) {
-                delete event_handlers[evnt];
-                return call(['events', 'unregister', evnt])
-            }
-        }
-    };
-}();
-
-try {
-    module.exports = bitmask
-} catch(err) {}
diff --git a/www/app/lib/color.js b/www/app/lib/color.js
deleted file mode 100644
index 5b1dfee9..00000000
--- a/www/app/lib/color.js
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Color.hsv().css
-//
-// RGB values are 0..255
-// HSV values are 0..1
-//
-
-function compose(value)
-  return [
-    Math.round(value * 255),
-    Math.round(value * 255),
-    Math.round(value * 255)
-  }
-}
-
-class Color {
-
-  constructor(r, g, b, a) {
-    this.r = r
-    this.g = g
-    this.b = b
-    this.a = a
-  }
-
-  //
-  // alternate hsv factory
-  //
-  static hsv(h,s,v) {
-    let out = null
-    h = h % 360;
-    s = Math.max(0, Math.min(1, s))
-    v = Math.max(0, Math.min(1, v))
-
-    if (s == 0) {
-      let grey = Math.ceil(v*255)
-      out = [grey, grey, grey]
-    }
-
-    let b = ((1 - s) * v);
-    let vb = v - b;
-    let hm = h % 60;
-    switch((h/60)|0) {
-      case 0:
-        out = compose(v, vb * h / 60 + b, b); break
-      case 1:
-        out = compose(vb * (60 - hm) / 60 + b, v, b); break
-      case 2:
-        out = compose(b, v, vb * hm / 60 + b); break
-      case 3:
-        out = compose(b, vb * (60 - hm) / 60 + b, v); break
-      case 4:
-        out = compose(vb * hm / 60 + b, b, v); break
-      case 5:
-        out = compose(v, b, vb * (60 - hm) / 60 + b); break
-    }
-
-    return new Color(...out)
-  }
-
-  css() {
-    return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`
-  }
-}
-
-export default Color
\ No newline at end of file
diff --git a/www/app/lib/colors.js b/www/app/lib/colors.js
deleted file mode 100644
index d86ca0a8..00000000
--- a/www/app/lib/colors.js
+++ /dev/null
@@ -1,289 +0,0 @@
-//
-// a palette from google's material design
-//
-
-export const red50 = '#ffebee';
-export const red100 = '#ffcdd2';
-export const red200 = '#ef9a9a';
-export const red300 = '#e57373';
-export const red400 = '#ef5350';
-export const red500 = '#f44336';
-export const red600 = '#e53935';
-export const red700 = '#d32f2f';
-export const red800 = '#c62828';
-export const red900 = '#b71c1c';
-export const redA100 = '#ff8a80';
-export const redA200 = '#ff5252';
-export const redA400 = '#ff1744';
-export const redA700 = '#d50000';
-
-export const pink50 = '#fce4ec';
-export const pink100 = '#f8bbd0';
-export const pink200 = '#f48fb1';
-export const pink300 = '#f06292';
-export const pink400 = '#ec407a';
-export const pink500 = '#e91e63';
-export const pink600 = '#d81b60';
-export const pink700 = '#c2185b';
-export const pink800 = '#ad1457';
-export const pink900 = '#880e4f';
-export const pinkA100 = '#ff80ab';
-export const pinkA200 = '#ff4081';
-export const pinkA400 = '#f50057';
-export const pinkA700 = '#c51162';
-
-export const purple50 = '#f3e5f5';
-export const purple100 = '#e1bee7';
-export const purple200 = '#ce93d8';
-export const purple300 = '#ba68c8';
-export const purple400 = '#ab47bc';
-export const purple500 = '#9c27b0';
-export const purple600 = '#8e24aa';
-export const purple700 = '#7b1fa2';
-export const purple800 = '#6a1b9a';
-export const purple900 = '#4a148c';
-export const purpleA100 = '#ea80fc';
-export const purpleA200 = '#e040fb';
-export const purpleA400 = '#d500f9';
-export const purpleA700 = '#aa00ff';
-
-export const deepPurple50 = '#ede7f6';
-export const deepPurple100 = '#d1c4e9';
-export const deepPurple200 = '#b39ddb';
-export const deepPurple300 = '#9575cd';
-export const deepPurple400 = '#7e57c2';
-export const deepPurple500 = '#673ab7';
-export const deepPurple600 = '#5e35b1';
-export const deepPurple700 = '#512da8';
-export const deepPurple800 = '#4527a0';
-export const deepPurple900 = '#311b92';
-export const deepPurpleA100 = '#b388ff';
-export const deepPurpleA200 = '#7c4dff';
-export const deepPurpleA400 = '#651fff';
-export const deepPurpleA700 = '#6200ea';
-
-export const indigo50 = '#e8eaf6';
-export const indigo100 = '#c5cae9';
-export const indigo200 = '#9fa8da';
-export const indigo300 = '#7986cb';
-export const indigo400 = '#5c6bc0';
-export const indigo500 = '#3f51b5';
-export const indigo600 = '#3949ab';
-export const indigo700 = '#303f9f';
-export const indigo800 = '#283593';
-export const indigo900 = '#1a237e';
-export const indigoA100 = '#8c9eff';
-export const indigoA200 = '#536dfe';
-export const indigoA400 = '#3d5afe';
-export const indigoA700 = '#304ffe';
-
-export const blue50 = '#e3f2fd';
-export const blue100 = '#bbdefb';
-export const blue200 = '#90caf9';
-export const blue300 = '#64b5f6';
-export const blue400 = '#42a5f5';
-export const blue500 = '#2196f3';
-export const blue600 = '#1e88e5';
-export const blue700 = '#1976d2';
-export const blue800 = '#1565c0';
-export const blue900 = '#0d47a1';
-export const blueA100 = '#82b1ff';
-export const blueA200 = '#448aff';
-export const blueA400 = '#2979ff';
-export const blueA700 = '#2962ff';
-
-export const lightBlue50 = '#e1f5fe';
-export const lightBlue100 = '#b3e5fc';
-export const lightBlue200 = '#81d4fa';
-export const lightBlue300 = '#4fc3f7';
-export const lightBlue400 = '#29b6f6';
-export const lightBlue500 = '#03a9f4';
-export const lightBlue600 = '#039be5';
-export const lightBlue700 = '#0288d1';
-export const lightBlue800 = '#0277bd';
-export const lightBlue900 = '#01579b';
-export const lightBlueA100 = '#80d8ff';
-export const lightBlueA200 = '#40c4ff';
-export const lightBlueA400 = '#00b0ff';
-export const lightBlueA700 = '#0091ea';
-
-export const cyan50 = '#e0f7fa';
-export const cyan100 = '#b2ebf2';
-export const cyan200 = '#80deea';
-export const cyan300 = '#4dd0e1';
-export const cyan400 = '#26c6da';
-export const cyan500 = '#00bcd4';
-export const cyan600 = '#00acc1';
-export const cyan700 = '#0097a7';
-export const cyan800 = '#00838f';
-export const cyan900 = '#006064';
-export const cyanA100 = '#84ffff';
-export const cyanA200 = '#18ffff';
-export const cyanA400 = '#00e5ff';
-export const cyanA700 = '#00b8d4';
-
-export const teal50 = '#e0f2f1';
-export const teal100 = '#b2dfdb';
-export const teal200 = '#80cbc4';
-export const teal300 = '#4db6ac';
-export const teal400 = '#26a69a';
-export const teal500 = '#009688';
-export const teal600 = '#00897b';
-export const teal700 = '#00796b';
-export const teal800 = '#00695c';
-export const teal900 = '#004d40';
-export const tealA100 = '#a7ffeb';
-export const tealA200 = '#64ffda';
-export const tealA400 = '#1de9b6';
-export const tealA700 = '#00bfa5';
-
-export const green50 = '#e8f5e9';
-export const green100 = '#c8e6c9';
-export const green200 = '#a5d6a7';
-export const green300 = '#81c784';
-export const green400 = '#66bb6a';
-export const green500 = '#4caf50';
-export const green600 = '#43a047';
-export const green700 = '#388e3c';
-export const green800 = '#2e7d32';
-export const green900 = '#1b5e20';
-export const greenA100 = '#b9f6ca';
-export const greenA200 = '#69f0ae';
-export const greenA400 = '#00e676';
-export const greenA700 = '#00c853';
-
-export const lightGreen50 = '#f1f8e9';
-export const lightGreen100 = '#dcedc8';
-export const lightGreen200 = '#c5e1a5';
-export const lightGreen300 = '#aed581';
-export const lightGreen400 = '#9ccc65';
-export const lightGreen500 = '#8bc34a';
-export const lightGreen600 = '#7cb342';
-export const lightGreen700 = '#689f38';
-export const lightGreen800 = '#558b2f';
-export const lightGreen900 = '#33691e';
-export const lightGreenA100 = '#ccff90';
-export const lightGreenA200 = '#b2ff59';
-export const lightGreenA400 = '#76ff03';
-export const lightGreenA700 = '#64dd17';
-
-export const lime50 = '#f9fbe7';
-export const lime100 = '#f0f4c3';
-export const lime200 = '#e6ee9c';
-export const lime300 = '#dce775';
-export const lime400 = '#d4e157';
-export const lime500 = '#cddc39';
-export const lime600 = '#c0ca33';
-export const lime700 = '#afb42b';
-export const lime800 = '#9e9d24';
-export const lime900 = '#827717';
-export const limeA100 = '#f4ff81';
-export const limeA200 = '#eeff41';
-export const limeA400 = '#c6ff00';
-export const limeA700 = '#aeea00';
-
-export const yellow50 = '#fffde7';
-export const yellow100 = '#fff9c4';
-export const yellow200 = '#fff59d';
-export const yellow300 = '#fff176';
-export const yellow400 = '#ffee58';
-export const yellow500 = '#ffeb3b';
-export const yellow600 = '#fdd835';
-export const yellow700 = '#fbc02d';
-export const yellow800 = '#f9a825';
-export const yellow900 = '#f57f17';
-export const yellowA100 = '#ffff8d';
-export const yellowA200 = '#ffff00';
-export const yellowA400 = '#ffea00';
-export const yellowA700 = '#ffd600';
-
-export const amber50 = '#fff8e1';
-export const amber100 = '#ffecb3';
-export const amber200 = '#ffe082';
-export const amber300 = '#ffd54f';
-export const amber400 = '#ffca28';
-export const amber500 = '#ffc107';
-export const amber600 = '#ffb300';
-export const amber700 = '#ffa000';
-export const amber800 = '#ff8f00';
-export const amber900 = '#ff6f00';
-export const amberA100 = '#ffe57f';
-export const amberA200 = '#ffd740';
-export const amberA400 = '#ffc400';
-export const amberA700 = '#ffab00';
-
-export const orange50 = '#fff3e0';
-export const orange100 = '#ffe0b2';
-export const orange200 = '#ffcc80';
-export const orange300 = '#ffb74d';
-export const orange400 = '#ffa726';
-export const orange500 = '#ff9800';
-export const orange600 = '#fb8c00';
-export const orange700 = '#f57c00';
-export const orange800 = '#ef6c00';
-export const orange900 = '#e65100';
-export const orangeA100 = '#ffd180';
-export const orangeA200 = '#ffab40';
-export const orangeA400 = '#ff9100';
-export const orangeA700 = '#ff6d00';
-
-export const deepOrange50 = '#fbe9e7';
-export const deepOrange100 = '#ffccbc';
-export const deepOrange200 = '#ffab91';
-export const deepOrange300 = '#ff8a65';
-export const deepOrange400 = '#ff7043';
-export const deepOrange500 = '#ff5722';
-export const deepOrange600 = '#f4511e';
-export const deepOrange700 = '#e64a19';
-export const deepOrange800 = '#d84315';
-export const deepOrange900 = '#bf360c';
-export const deepOrangeA100 = '#ff9e80';
-export const deepOrangeA200 = '#ff6e40';
-export const deepOrangeA400 = '#ff3d00';
-export const deepOrangeA700 = '#dd2c00';
-
-export const brown50 = '#efebe9';
-export const brown100 = '#d7ccc8';
-export const brown200 = '#bcaaa4';
-export const brown300 = '#a1887f';
-export const brown400 = '#8d6e63';
-export const brown500 = '#795548';
-export const brown600 = '#6d4c41';
-export const brown700 = '#5d4037';
-export const brown800 = '#4e342e';
-export const brown900 = '#3e2723';
-
-export const blueGrey50 = '#eceff1';
-export const blueGrey100 = '#cfd8dc';
-export const blueGrey200 = '#b0bec5';
-export const blueGrey300 = '#90a4ae';
-export const blueGrey400 = '#78909c';
-export const blueGrey500 = '#607d8b';
-export const blueGrey600 = '#546e7a';
-export const blueGrey700 = '#455a64';
-export const blueGrey800 = '#37474f';
-export const blueGrey900 = '#263238';
-
-export const grey50 = '#fafafa';
-export const grey100 = '#f5f5f5';
-export const grey200 = '#eeeeee';
-export const grey300 = '#e0e0e0';
-export const grey400 = '#bdbdbd';
-export const grey500 = '#9e9e9e';
-export const grey600 = '#757575';
-export const grey700 = '#616161';
-export const grey800 = '#424242';
-export const grey900 = '#212121';
-
-export const black = '#000000';
-export const white = '#ffffff';
-
-export const transparent = 'rgba(0, 0, 0, 0)';
-export const fullBlack = 'rgba(0, 0, 0, 1)';
-export const darkBlack = 'rgba(0, 0, 0, 0.87)';
-export const lightBlack = 'rgba(0, 0, 0, 0.54)';
-export const minBlack = 'rgba(0, 0, 0, 0.26)';
-export const faintBlack = 'rgba(0, 0, 0, 0.12)';
-export const fullWhite = 'rgba(255, 255, 255, 1)';
-export const darkWhite = 'rgba(255, 255, 255, 0.87)';
-export const lightWhite = 'rgba(255, 255, 255, 0.54)';
\ No newline at end of file
diff --git a/www/app/lib/common.js b/www/app/lib/common.js
deleted file mode 100644
index 14a30c32..00000000
--- a/www/app/lib/common.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * pollute the global namespace with some useful utils
- */
-
-import React from 'react'
-
-window.elem = React.createElement
\ No newline at end of file
diff --git a/www/app/lib/validate.js b/www/app/lib/validate.js
deleted file mode 100644
index 4e672e8f..00000000
--- a/www/app/lib/validate.js
+++ /dev/null
@@ -1,82 +0,0 @@
-// https://github.com/dropbox/zxcvbn
-
-var DOMAIN_RE = /^((?:(?:(?:\w[\.\-\+]?)*)\w)+)((?:(?:(?:\w[\.\-\+]?){0,62})\w)+)\.(\w{2,6})$/
-var USER_RE   = /^[a-z0-9_-]{1,200}$/
-var USER_INT_RE = /^[\.@a-z0-9_-]*$/
-
-//
-// Validations returns an error message or false if no errors
-//
-
-class Validations {
-
-  domain(input) {
-    if (!input.match(DOMAIN_RE)) {
-      return "Not a domain name"
-    } else {
-      return null
-    }
-  }
-
-  usernameInteractive(input) {
-    if (!input.match(USER_INT_RE)) {
-      return "Username contains an invalid character"
-    }
-    return false
-  }
-
-  username(input) {
-    if (!input.match(USER_INT_RE)) {
-      return "Username contains an invalid character"
-    }
-    if (!input.match('@')) {
-      return "Username must be in the form username@domain"
-    }
-    let parts      = input.split('@')
-    let userpart   = parts[0]
-    let domainpart = parts[1]
-    if (!userpart.match(USER_RE)) {
-      return "Username contains an invalid character"
-    } else if (!domainpart.match(DOMAIN_RE)) {
-      return "Username must include a valid domain name."
-    }
-    return false
-  }
-
-  passwordStrength(passwd) {
-    if (typeof(zxcvbn) == 'function') {
-      // zxcvbn performs very slow on long strings, so we cap
-      // the calculation at 30 characters
-      return zxcvbn(passwd.substring(0,30))
-    } else {
-      return null
-    }
-  }
-
-  //
-  // loads the zxcvbn library. because this library is big, we don't load it
-  // every time, just when needed.
-  //
-  // this is the webpack way to do this:
-  //
-  //    require.ensure([], function () {
-  //      var zxcvbn = require('zxcvbn');
-  //    });
-  //
-  // that works, but requires that we also process the original coffeescript
-  // source if we want to avoid warning messages.
-  //
-  loadPasswdLib(onload) {
-    var id = "zxcvbn-script"
-    if (!document.getElementById(id)) {
-      var script = document.createElement('script')
-      script.id = id
-      script.onload = onload
-      script.src = './js/zxcvbn.js'
-      document.getElementsByTagName('script')[0].appendChild(script)
-    }
-  }
-}
-
-var Validate = new Validations()
-export default Validate
diff --git a/www/app/main.js b/www/app/main.js
deleted file mode 100644
index b1628953..00000000
--- a/www/app/main.js
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// main entry point for app execution
-//
-// This is determined by the 'entry' option in webpack.config.js
-//
-
-import React from 'react'
-import ReactDOM from 'react-dom'
-
-import PanelSwitcher from 'components/panel_switcher'
-import App from 'app'
-
-class Main extends React.Component {
-  render() {
-    return React.createElement(PanelSwitcher)
-  }
-
-  componentDidMount() {
-    App.start()
-  }
-}
-
-ReactDOM.render(
-  React.createElement(Main),
-  document.getElementById('app')
-)
diff --git a/www/app/models/account.js b/www/app/models/account.js
deleted file mode 100644
index 52fea93d..00000000
--- a/www/app/models/account.js
+++ /dev/null
@@ -1,143 +0,0 @@
-//
-// An account is an abstraction of a user and a provider.
-// The user part is optional, so an Account might just represent a provider.
-//
-
-import bitmask from 'lib/bitmask'
-
-export default class Account {
-
-  constructor(address, props={}) {
-    this.address = address
-    this._authenticated = props.authenticated
-  }
-
-  //
-  // currently, bitmask.js uses address for id, so we return address here too.
-  // also, we don't know uuid until after authentication.
-  //
-  // TODO: change to uuid when possible.
-  //
-  get id() {
-    return this._address
-  }
-
-  get domain() {
-    return this._address.split('@')[1]
-  }
-
-  get address() {
-    return this._address
-  }
-
-  set address(address) {
-    if (!address.match('@')) {
-      this._address = '@' + address
-    } else {
-      this._address = address
-    }
-  }
-
-  get userpart() {
-    return this._address.split('@')[0]
-  }
-
-  get authenticated() {
-    return this._authenticated
-  }
-
-  get hasEmail() {
-    return true
-  }
-
-  //
-  // returns a promise, fulfill is passed account object
-  //
-  login(password) {
-    return bitmask.bonafide.user.auth(this.address, password).then(
-      response => {
-        if (response.uuid) {
-          this._uuid = response.uuid
-          this._authenticated = true
-        }
-        return this
-      }
-    )
-  }
-
-  //
-  // returns a promise, fulfill is passed account object
-  //
-  logout() {
-    return bitmask.bonafide.user.logout(this.id).then(
-      response => {
-        this._authenticated = false
-        this._address = '@' + this.domain
-        return this
-      }
-    )
-  }
-
-  //
-  // returns the matching account in the list of accounts, or adds it
-  // if it is not already present.
-  //
-  static find(address) {
-    // search by full address
-    let account = Account.list.find(i => {
-      return i.address == address
-    })
-    // failing that, search by domain
-    if (!account) {
-      let domain = '@' + address.split('@')[1]
-      account = Account.list.find(i => {
-        return i.address == domain
-      })
-      if (account) {
-        account.address = address
-      }
-    }
-    // failing that, create new account
-    if (!account) {
-      account = new Account(address)
-      Account.list.push(account)
-    }
-    return account
-  }
-
-  //
-  // returns a promise, fullfill is passed account object
-  //
-  static active() {
-    return bitmask.bonafide.user.active().then(
-      response => {
-        if (response.user == '<none>') {
-          return null
-        } else {
-          return new Account(response.user, {authenticated: true})
-        }
-      }
-    )
-  }
-
-  static add(account) {
-    if (!Account.list.find(i => {return i.id == account.id})) {
-      Account.list.push(account)
-    }
-  }
-
-  static remove(account) {
-    Account.list = Account.list.filter(i => {
-      return i.id != account.id
-    })
-  }
-  //   return Account.list
-  //   return new Promise(function(resolve, reject) {
-  //     window.setTimeout(function() {
-  //       resolve(['@blah', '@lala'])
-  //     }, 1000)
-  //   })
-  // }
-}
-
-Account.list = []
diff --git a/www/app/models/dummy_account.js b/www/app/models/dummy_account.js
deleted file mode 100644
index 99fb6623..00000000
--- a/www/app/models/dummy_account.js
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// A proxy of an account, but with a different ID. For testing.
-//
-
-import bitmask from 'lib/bitmask'
-
-export default class DummyAccount {
-
-  constructor(account) {
-    this.account = account
-  }
-
-  get id() {
-    return 'dummy--' + this.account.address
-  }
-
-  get domain() {return this.account.domain}
-  get address() {return this.account.address}
-  get userpart() {return this.account.userpart}
-  get authenticated() {return this.account.authenticated}
-  get hasEmail() {return this.account.hasEmail}
-  login(password) {return this.account.login(password)}
-
-  logout() {
-    return bitmask.bonafide.user.logout(this.address).then(
-      response => {
-        this._authenticated = false
-        this._address = '@' + this.domain
-        return this
-      }
-    )
-  }
-}
diff --git a/www/package.json b/www/package.json
deleted file mode 100644
index d491edd8..00000000
--- a/www/package.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "name": "bitmask_js",
-  "version": "0.0.1",
-  "description": "bitmask user interface in javascript",
-  "license": "GPL-3.0",
-  "homepage": "https://bitmask.net",
-  "repository": "https://leap.se/git/bitmask_client.git",
-  "dependencies": {},
-  "devDependencies": {
-    "babel": "^6.5.2",
-    "babel-loader": "^6.2.4",
-    "babel-polyfill": "^6.13.0",
-    "babel-preset-es2015": "^6.9.0",
-    "babel-preset-react": "^6.11.1",
-    "babel-preset-stage-0": "^6.5.0",
-    "bootstrap": "^3.3.7",
-    "copy-webpack-plugin": "^3.0.1",
-    "css-loader": "^0.23.1",
-    "less": "^2.7.1",
-    "less-loader": "^2.2.3",
-    "react": "^15.2.1",
-    "react-bootstrap": "^0.30.2",
-    "react-dom": "^15.2.1",
-    "style-loader": "^0.13.1",
-    "webpack": "^1.13.1",
-    "zxcvbn": "^4.3.0"
-  },
-  "scripts": {
-    "open": "gnome-open http://localhost:7070",
-    "watch": "NODE_ENV=development webpack --watch",
-    "build": "NODE_ENV=development webpack",
-    "build:production": "NODE_ENV=production webpack"
-  }
-}
diff --git a/www/pydist/README.md b/www/pydist/README.md
deleted file mode 100644
index 6e2b11f6..00000000
--- a/www/pydist/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-This directory holds the python package of the javascript app, called 'bitmask_js'.
-
-Why it it done this way? By creating a python package, it is easier for the
-javascript app to be distributed with the bitmask client.
diff --git a/www/pydist/setup.py b/www/pydist/setup.py
deleted file mode 100644
index b5d3ffb1..00000000
--- a/www/pydist/setup.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# setup.py
-# Copyright (C) 2016 LEAP Encryption Acess Project
-#
-# This program 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 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Setup file for bitmask_js
-"""
-
-from setuptools import setup
-import datetime
-import time
-
-long_description = \
-'''bitmask_js
------------------ 
-This package contains the already compiled javascript resources for the bitmask UI.
-
-If you want to develop for this UI, please checkout the bitmask-dev [0] repo and follow the instructions in the ui/README.md file.
-
-[0] https://github.com/leapcode/bitmask-dev'''
-
-now       = datetime.datetime.now()
-timestamp = time.strftime('%Y%m%d%H%M', now.timetuple())
-
-setup(
-    name='bitmask_js',
-    version='0.1.%s' % timestamp,
-    description='Bitmask UI',
-    long_description=long_description,
-    author='LEAP Encrypted Access Project',
-    author_email='info@leap.se',
-    url='http://leap.se',
-    packages=['bitmask_js'],
-    package_data={
-        '': ['public/*',
-             'public/css/*',
-             'public/fonts/*',
-             'public/img/*',
-             'publlic/js/*',
-             ]
-    }
-)
diff --git a/www/webpack.config.js b/www/webpack.config.js
deleted file mode 100644
index c0f8d191..00000000
--- a/www/webpack.config.js
+++ /dev/null
@@ -1,84 +0,0 @@
-var path = require('path')
-var webpack = require('webpack')
-var CopyWebpackPlugin = require('copy-webpack-plugin');
-
-var config = {
-  context: path.join(__dirname, 'app'),
-  entry: ['babel-polyfill', './main.js'],
-  output: {
-    path: path.join(__dirname, 'pydist', 'bitmask_js', 'public'),
-    filename: 'app.bundle.js'
-  },
-  resolve: {
-    modulesDirectories: ['node_modules', './app'],
-    extensions: ['', '.js', '.jsx']
-  },
-  module: {
-    loaders: [
-      // babel transform
-      {
-        test: /\.js$/,
-        loader: 'babel-loader',
-        exclude: /node_modules/,
-        query: {
-          presets: ['react', 'es2015']
-        }
-      },
-      {
-        test: /\.css$/,
-        loader: "style!css"
-      },
-      {
-        test: /\.less$/,
-        loader: "style!css!less?noIeCompat"
-      }
-    ]
-  },
-  plugins: [
-    // don't bundle when there is an error:
-    new webpack.NoErrorsPlugin(),
-
-    // https://webpack.github.io/docs/code-splitting.html
-    // new webpack.optimize.CommonChunkPlugin('common.js')
-
-    //
-    // ASSETS
-    //
-    // If you make changes to the asset files, you will need to stop then rerun
-    // `npm run watch` for the changes to take effect.
-    //
-    // For more information: https://github.com/kevlened/copy-webpack-plugin
-    //
-    new CopyWebpackPlugin([
-      { from: 'css/*.css' },
-      { from: 'img/*'},
-      { from: 'index.html' },
-      { from: '../node_modules/bootstrap/dist/css/bootstrap.min.css', to: 'css' },
-      { from: '../node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff', to: 'fonts' },
-      { from: '../node_modules/zxcvbn/dist/zxcvbn.js', to: 'js' }
-    ])
-  ],
-  stats: {
-    colors: true
-  },
-  // source-map can be used in production or development
-  // but it creates a separate file.
-  devtool: 'source-map'
-}
-
-/*
-if (process.env.NODE_ENV == 'production') {
-  // see https://github.com/webpack/docs/wiki/optimization
-  config.plugins.push(
-    new webpack.optimize.UglifyJsPlugin({
-      compress: { warnings: false },
-      output: { comments: false }
-    }),
-    new webpack.optimize.DedupePlugin()
-  )
-} else {
-  config.devtool = 'inline-source-map';
-}
-*/
-
-module.exports = config
-- 
cgit v1.2.3