design(playground/client): add initial skin to the app

Wrap the playground UI in the Vanadium style guide. This is a rough first pass.

Close veyron/release-issues#802

Change-Id: Ied23c7fd76a2ac867a977fbbf5f64bfe3594fba0
diff --git a/client/Makefile b/client/Makefile
index d446ba2..1ec34b4 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -26,9 +26,8 @@
 public/bundle.js: browser/index.js $(js_files) node_modules
 	browserify --debug $< 1> $@
 
-# TODO(jasoncampbell): Hookup the css compiler from www
 public/bundle.css: stylesheets/index.css $(css_files) node_modules
-	cp $< $@
+	bin/compile-css $< 1> $@
 
 # Each profile (glob file with file patterns to include) from
 # `bundles/<profile>.bundle` is applied to each example folder of the
diff --git a/client/bin/compile-css b/client/bin/compile-css
new file mode 100755
index 0000000..249f812
--- /dev/null
+++ b/client/bin/compile-css
@@ -0,0 +1,87 @@
+#!/usr/bin/env node
+var minimist = require('minimist');
+var assert = require('assert');
+var myth = require('myth');
+var rework = require('rework');
+var inherit = require('rework-inherit');
+var path = require('path');
+var fs = require('fs');
+var argv = minimist(process.argv.slice(2));
+var infile = argv._[0];
+
+if (! infile) {
+  var err = new Error('Missing infile, try `./bin/compile-css <infile>`');
+  return exit(err);
+}
+
+read(infile, function onstring(err, string) {
+  if (err) {
+    return exit(err);
+  }
+
+  compile(string, function onCSS(err, css){
+    if (err) {
+      exit(err);
+    } else {
+      process.stdout.write(css);
+    }
+  });
+});
+
+
+// # compile-css
+//
+// This is a quick css pre-processor based on `myth`, once we start to
+// standardize the css this should get bundled up into a module.
+//
+// ## Usage
+//
+// There is no bin so right now use node to run this:
+//
+//     node rework-css.js <infile>
+//
+// This program will spit out the compiled CSS to stdout. Redirect with `>`:
+//
+//     node rework-css.js stylesheets/index.css > public/bundle.css
+//
+function compile(string, callback) {
+  assert.ok(string, 'Missing string argument');
+  assert.ok(callback, 'Missing callback argument');
+
+  var css;
+  var err;
+
+  // myth/rework has a sync api that throws :/
+  try {
+    css = preprocess(string);
+  } catch (e) {
+    err = e;
+  }
+
+  callback(err, css);
+}
+
+// wraps the call to rework with all the options and plugins
+function preprocess(string) {
+  var css = myth(string, {
+    compress: true,
+    source: infile
+  })
+
+  return rework(css)
+  .use(inherit())
+  .toString();
+}
+
+function read(file, callback) {
+  file = path.resolve(file);
+  fs.readFile(file, 'utf8', callback);
+}
+
+function exit(err) {
+  if (err) {
+    console.error(err.stack);
+  }
+
+  process.exit(1);
+}
diff --git a/client/browser/api/index.js b/client/browser/api/index.js
index 899d0fe..c2e5d6b 100644
--- a/client/browser/api/index.js
+++ b/client/browser/api/index.js
@@ -67,7 +67,7 @@
 
   // Append trailing slash if it's missing
   if (! clone.pathname.match(/\/$/)) {
-    clone.pathname = clone.pathname + '/'
+    clone.pathname = clone.pathname + '/';
   }
 
   // NOTE: paying attention to options.action is temporary until the API gets
@@ -106,10 +106,10 @@
   // TODO(jasoncampbell): remove this list once a list API endpoint is
   // available.
   var ids = [
-    '_39bce22c2acd70cd235ad2764738f3ffcaec7a5fea67ec4c14a674b991a373c',
-    '_8e15dfe13cf1288ace5f58b0c35a77015be2b23b61ca673a497c7d50afedf3b',
-    '_de64000da6c89b19f566135e7459557fdd3be1f6f4b6e74f76c60f2693fceb1',
-    '_e9a1eb094d30d3d7602f561610f155e29e3d0c9d875116657a65415937e1137'
+    '_051039d58946cfbe3a603fd7ed3149b0501b8f1e13ee0778e9c30e08d8a94d3',
+    '_9999dc7e35426090058fb0240a8a2c1b128ea958e092b0d53ac5f2a5ca4c54a',
+    '_eee960846ba210e4d9b6b01463625a2e5bf15b9dab61d76b4fc38ae04317bbd',
+    '_5f199e7f67cc6b60efddf2d37cdf2c0d1aeec488007981e972c1e0a43f91c3e'
   ];
 
   var workers = ids.map(createWorker);
diff --git a/client/browser/components/bundle/index.js b/client/browser/components/bundle/index.js
index b49ba5b..d8885f4 100644
--- a/client/browser/components/bundle/index.js
+++ b/client/browser/components/bundle/index.js
@@ -6,7 +6,7 @@
 var hg = require('mercury');
 var log = require('../log');
 var api = require('../../api');
-var resultsConsole = require('../results-console');
+var results = require('../results');
 var router = require('../../router');
 var toArray = require('../../util').toArray;
 
@@ -20,7 +20,7 @@
     tab: hg.value(json.files[0].name),
     running: hg.value(false),
     reset: hg.value(false),
-    resultsConsole: resultsConsole(),
+    results: results(),
     channels: {
       tab: tab,
       run: run,
@@ -51,9 +51,8 @@
 
     // If running clear previous logs and open the console.
     if (running) {
-      state.resultsConsole.logs.set(hg.array([]));
-      state.resultsConsole.open.set(true);
-      state.resultsConsole.follow.set(true);
+      state.results.logs.set(hg.array([]));
+      state.results.follow.set(true);
     }
   });
 
@@ -99,8 +98,8 @@
     // Since a new id is generated and the id is in the url the router needs
     // to be updated.
     //
-    // TODO(jasoncampbell): put the the value of the new bundle here, maybe don't trigger a
-    // reload.
+    // TODO(jasoncampbell): put the the value of the new bundle here, maybe
+    // don't trigger a reload.
     router.href.set(data.uuid);
   });
 }
@@ -126,7 +125,7 @@
     });
 
     stream.on('data', function ondata(data) {
-      state.resultsConsole.logs.push(log(data));
+      state.results.logs.push(log(data));
     });
 
     stream.on('end', function() {
diff --git a/client/browser/components/bundle/render.js b/client/browser/components/bundle/render.js
index 1bb65dd..615d49a 100644
--- a/client/browser/components/bundle/render.js
+++ b/client/browser/components/bundle/render.js
@@ -3,100 +3,74 @@
 // license that can be found in the LICENSE file.
 
 var debug = require('debug')('components:bundle:render');
-var anchor = require('../../router/anchor');
 var h = require('mercury').h;
 var hg = require('mercury');
-var results = require('../results-console');
+var results = require('../results').render;
 var toArray = require('../../util').toArray;
+var click = require('../../event-handlers/click');
+var path = require('path');
+var aceWidget = require('../../widgets/ace-widget');
+var aceChange = require('../../event-handlers/ace-change');
 
 module.exports = render;
 
 function render(state) {
   debug('update %o', state);
 
+  // NOTE: It is possible to try and render a bundle without it being
+  // populated from the API yet. In that case show a loader.
   if (! state) {
-    return h('.bundle', [
-      h('p', 'Loading...')
-    ]);
+    return h('.bundle', 'Loading...');
   }
 
   return h('.bundle', [
-    h('.pg', [
-      hg.partial(anchorbar, state, state.channels),
-      hg.partial(tabs, state, state.channels),
-      hg.partial(controls, state, state.channels),
-      hg.partial(editors, state, state.channels),
-      hg.partial(results.render,
-        state.resultsConsole,
-        state.resultsConsole.channels)
-    ])
+    hg.partial(code, state, state.channels),
+    hg.partial(results, state.results, state.results.channels),
   ]);
 }
 
-var options = { preventDefault: true };
-
-function anchorbar(state, channels) {
-  return h('.widget-bar', [
-    h('p', [
-      h('strong', 'anchor:'),
-      anchor({ href: '/' + state.uuid }, state.uuid)
-    ])
+function code(state, channels) {
+  return h('.code', [
+    hg.partial(tabs, state, channels),
+    hg.partial(editors, state, channels)
   ]);
 }
 
 function tabs(state, channels) {
   var files = toArray(state.files);
 
-  return h('span.tabs', files.map(tab, state));
+  return h('.tabs', files.map(tab, state));
 }
 
 function tab(file, index, array) {
   var state = this;
   var channels = state.channels;
+  var name = path.basename(file.name);
 
-  return h('span.tab', {
+  return h('a.tab', {
     className: state.tab === file.name ? 'active' : '',
-    'ev-click': hg.sendClick(channels.tab, { tab: file.name }, options)
-  }, file.name);
+    href: '#',
+    'ev-click': click(channels.tab, { tab: file.name })
+  }, name);
 }
 
-function controls(state, channels) {
-  return h('span.btns', [
-    hg.partial(runButton, state, channels),
-    h('button.btn', {
-      'ev-click': hg.sendClick(channels.save)
-    }, 'Save')
-  ]);
-}
 
-function runButton(state, channels) {
-  var text = 'Run Code';
-  var sink = channels.run;
-
-  if (state.running) {
-    text = 'Stop';
-    sink = channels.stop;
-  }
-
-  return h('button.btn', {
-    'ev-click': hg.sendClick(sink)
-  }, text);
-}
-
-// TODO(jasoncampbell): It makes sense to break the editor into it's own
-// component as we will be adding features...
-var aceWidget = require('../../widgets/ace-widget');
-var aceChange = require('../../event-handlers/ace-change');
 
 function editors(state, channels) {
   var files = toArray(state.files);
 
-  return h('.editors', files.map(function(file) {
-    return h('.editor', {
-      className: (state.tab === file.name ? 'active' : ''),
-      'ev-ace-change': aceChange(state.channels.fileChange),
-    }, [
-      aceWidget(file)
-    ]);
-  }));
+  return h('.editors', files.map(editor, state));
+}
+
+function editor(file, index, files) {
+  var state = this;
+  var channels = state.channels;
+  var isActive = state.tab === file.name;
+
+  return h('.editor', {
+    className: (isActive ? 'active' : ''),
+    'ev-ace-change': aceChange(channels.fileChange),
+  }, [
+    aceWidget(file)
+  ]);
 }
diff --git a/client/browser/components/header.js b/client/browser/components/header.js
index 2727cd6..c933792 100644
--- a/client/browser/components/header.js
+++ b/client/browser/components/header.js
@@ -1,15 +1,62 @@
 // Copyright 2015 The Vanadium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
-
+var debug = require('debug')('components:header:render');
+var hg = require('mercury');
 var h = require('mercury').h;
+var anchor = require('../router/anchor');
+var click = require('../event-handlers/click');
 
 module.exports = {
   render: render
 };
 
-function render(state, chanels) {
+function render(state, channels) {
+  debug('update %o', state);
+
   return h('header', [
-    h('h1', state.title)
+    h('nav.left', [
+      anchor({ className: 'logo', href: '/' }, 'Vanadium')
+    ]),
+    hg.partial(controls, state, channels)
   ]);
 }
+
+function controls(state, channels) {
+  var bundle = state.uuid ? state.bundles[state.uuid] : null;
+
+  if (! bundle) {
+    return h('nav.main');
+  } else {
+    return h('nav.main', [
+      hg.partial(save, bundle, bundle.channels),
+      hg.partial(runOrStop, bundle, bundle.channels)
+    ]);
+  }
+}
+
+function save(bundle, channels) {
+  return h('a.bundle-save', {
+    'href': '#',
+    'ev-click': click(channels.save)
+  }, 'Save');
+}
+
+function runOrStop(bundle, channels) {
+  var text;
+  var sink;
+
+  if (bundle.running) {
+    text = 'Stop';
+    sink = channels.stop;
+  } else {
+    text = 'Run';
+    sink = channels.run;
+  }
+
+  return h('a.bundle-run-or-stop', {
+    className: bundle.running ? 'running' : 'stopped',
+    href: '#',
+    'ev-click': click(sink)
+  }, text);
+}
diff --git a/client/browser/components/log/render.js b/client/browser/components/log/render.js
index ac0f253..a064fbf 100644
--- a/client/browser/components/log/render.js
+++ b/client/browser/components/log/render.js
@@ -1,34 +1,30 @@
 // Copyright 2015 The Vanadium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
-
 var h = require('mercury').h;
 var moment = require('moment');
 
 module.exports = render;
 
-function render(state) {
-  var date = moment(state.timestamp).format('H:mm:ss.SSS');
+// This is expected to be called an iterator fn passed to logs.map(render)
+function render(state, index, logs) {
+  var time = moment(state.timestamp).format('MMM D HH:mm:ss SSS');
+  var stream = state.stream || 'unknown';
 
-  var children = [
-    h('span.timestamp', date + ' '),
-    h('span.filename', state.file ? state.file + ': ' : '')
-  ];
-
-  // TODO(jasoncampbell): render in a pre tag instead
-
-  // A single trailing newline is always ignored.
-  // Ignoring the last character, check if there are any newlines in message.
-  if (state.message.slice(0, -1).indexOf('\n') !== -1) {
-    children.push('\u23ce'); // U+23CE RETURN SYMBOL
-    children.push('br');
-  }
-
-  var message = h('span.message', {
-    className: state.stream || 'unknown'
-  }, state.message);
-
-  children.push(message);
-
-  return h('.log', children);
+  return h('.log', {
+    className: stream
+  }, [
+    h('.meta', [
+      h('.source', [
+        h('span', state.file || 'system: '),
+        h('span.stream', (! state.file) ? stream : '')
+      ]),
+      h('.time', '[' + time + ']')
+    ]),
+    h('.message', [
+      h('pre', [
+        h('code', state.message)
+      ])
+    ])
+  ]);
 }
diff --git a/client/browser/components/results-console/render.js b/client/browser/components/results-console/render.js
deleted file mode 100644
index 964ddfb..0000000
--- a/client/browser/components/results-console/render.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-var debug = require('debug')('components:results-console:render');
-var h = require('mercury').h;
-var scroll = require('../../event-handlers/scroll');
-var followHook = require('./follow-hook');
-var log = require('../log');
-
-module.exports = render;
-
-function render(state, channels) {
-  debug('update console %o', state);
-
-  return h('.console', {
-    className: state.open ? 'open' : 'closed',
-    'ev-scroll': scroll(channels.follow, { scrolling: true }),
-    'follow-console': followHook(state.follow)
-  }, [
-    h('.text', state.logs.map(log.render))
-  ]);
-}
diff --git a/client/browser/components/results-console/follow-hook.js b/client/browser/components/results/follow-hook.js
similarity index 100%
rename from client/browser/components/results-console/follow-hook.js
rename to client/browser/components/results/follow-hook.js
diff --git a/client/browser/components/results-console/index.js b/client/browser/components/results/index.js
similarity index 70%
rename from client/browser/components/results-console/index.js
rename to client/browser/components/results/index.js
index 6368399..95c287c 100644
--- a/client/browser/components/results-console/index.js
+++ b/client/browser/components/results/index.js
@@ -1,22 +1,20 @@
 // Copyright 2015 The Vanadium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
-
 var hg = require('mercury');
-var debug = require('debug')('components:results-console:state');
 
-module.exports = resultsConsole;
+module.exports = results;
 module.exports.render = require('./render');
 
-function resultsConsole() {
-  debug('create');
-
+function results() {
   var state = hg.state({
     logs: hg.array([]),
     open: hg.value(false),
     follow: hg.value(true),
+    debug: hg.value(false),
     channels: {
-      follow: follow
+      follow: follow,
+      debug: debug
     }
   });
 
@@ -34,3 +32,11 @@
     state.follow.set(true);
   }
 }
+
+function debug(state, data) {
+  var current = state.debug();
+
+  if (data.debug !== current) {
+    state.debug.set(data.debug);
+  }
+}
diff --git a/client/browser/components/results/render.js b/client/browser/components/results/render.js
new file mode 100644
index 0000000..330917a
--- /dev/null
+++ b/client/browser/components/results/render.js
@@ -0,0 +1,41 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+var h = require('mercury').h;
+var hg = require('mercury');
+var scroll = require('../../event-handlers/scroll');
+var click = require('../../event-handlers/click');
+var followHook = require('./follow-hook');
+var log = require('../log');
+var format = require('format');
+
+module.exports = render;
+
+function render(state, channels) {
+  return h('.results', [
+    hg.partial(controls, state, channels),
+    h('.console', {
+      className: state.debug ? 'debug' : ''
+    }, [
+      h('.scroller', {
+        'ev-scroll': scroll(channels.follow, { scrolling: true }),
+        'follow-console': followHook(state.follow)
+      }, state.logs.map(log.render))
+    ])
+  ]);
+}
+
+function controls(state, channels) {
+  var onOrOff = (state.debug ? 'on' : 'off');
+  var title = format('Toggle debug console output %s.', onOrOff);
+  var text = format(' Debug: %s', onOrOff);
+
+  return h('.controls', [
+    'Results',
+    h('a.debug', {
+      href: '#',
+      'ev-click': click(channels.debug, { debug: ! state.debug }),
+      title: title
+    }, text)
+  ]);
+}
diff --git a/client/browser/event-handlers/click.js b/client/browser/event-handlers/click.js
new file mode 100644
index 0000000..b618a32
--- /dev/null
+++ b/client/browser/event-handlers/click.js
@@ -0,0 +1,11 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+var hg = require('mercury');
+var options = { preventDefault: true };
+
+module.exports = click;
+
+function click(sink, data) {
+  return hg.sendClick(sink, data, options);
+}
diff --git a/client/browser/render.js b/client/browser/render.js
index 580af26..303e751 100644
--- a/client/browser/render.js
+++ b/client/browser/render.js
@@ -17,7 +17,8 @@
 function render(state) {
   return h('.playground', [
     hg.partial(header.render, state, state.channels),
-    hg.partial(main, state)
+    hg.partial(main, state),
+    hg.partial(footer, state, state.channels)
   ]);
 }
 
@@ -50,3 +51,37 @@
 
   return h('main', [ partial ]);
 }
+
+function footer(state, channels) {
+  return h('footer', [
+    h('nav.main', [
+      h('a', {
+        href: 'https://v.io/introduction.html'
+      }, 'Intro'),
+      h('a', {
+        href: 'https://v.io/installation/'
+      }, 'Install'),
+      h('a', {
+        href: 'https://v.io/tutorials/'
+      }, 'Tutorials'),
+      h('a', {
+        href: 'https://v.io/docs.html'
+      }, 'API'),
+      h('a', {
+        href: 'https://v.io/community/'
+      }, 'Community'),
+      h('a', {
+        href: 'https://v.io/tos.html'
+      }, 'Terms of Service'),
+      h('a', {
+        href: 'https://github.com/vanadium/issues/issues/'
+      }, 'File a Bug')
+    ]),
+    h('nav.social', [
+      h('a.icon-github', { href: 'https://github.com/vanadium' }),
+      h('a.icon-twitter', {
+        href: 'https://twitter.com/vanadiumteam'
+      })
+    ])
+  ]);
+}
diff --git a/client/browser/widgets/ace-widget.js b/client/browser/widgets/ace-widget.js
index 9559fcd..21ff6be 100644
--- a/client/browser/widgets/ace-widget.js
+++ b/client/browser/widgets/ace-widget.js
@@ -32,7 +32,7 @@
   editor.extname = path.extname(file.name).replace('.', '');
   editor.text = file.body;
 
-  debug('initialization %s - %s', editor.filename, editor.visible);
+  debug('initialization %s', editor.filename);
 }
 
 // The first time an instance of widget is seen it will have widget.init()
diff --git a/client/package.json b/client/package.json
index 1188b23..e0689aa 100644
--- a/client/package.json
+++ b/client/package.json
@@ -9,7 +9,6 @@
   "main": "browser/index.js",
   "dependencies": {
     "brace": "^0.4.0",
-    "browserify": "^8.1.1",
     "debug": "^2.1.3",
     "domready": "^1.0.7",
     "format": "^0.2.1",
@@ -17,7 +16,6 @@
     "hyperquest": "^1.0.1",
     "mercury": "^14.0.0",
     "moment": "^2.10.2",
-    "pgbundle": "0.0.1",
     "prr": "^1.0.1",
     "routes": "^2.0.0",
     "run-parallel": "^1.1.0",
@@ -33,8 +31,14 @@
     "test": "make test"
   },
   "devDependencies": {
+    "browserify": "^8.1.1",
     "http-server": "^0.7.4",
     "jshint": "^2.6.0",
+    "minimist": "^1.1.1",
+    "myth": "^1.4.0",
+    "pgbundle": "0.0.1",
+    "rework-inherit": "^0.2.3",
+    "rework": "^1.0.1",
     "run-browser": "^2.0.2",
     "tape": "^3.5.0"
   },
diff --git a/client/public/favicon.ico b/client/public/favicon.ico
new file mode 100644
index 0000000..78fbfdd
--- /dev/null
+++ b/client/public/favicon.ico
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-114x114.png b/client/public/favicons/apple-touch-icon-114x114.png
new file mode 100644
index 0000000..229b077
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-114x114.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-120x120.png b/client/public/favicons/apple-touch-icon-120x120.png
new file mode 100644
index 0000000..aba6d3d
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-120x120.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-144x144.png b/client/public/favicons/apple-touch-icon-144x144.png
new file mode 100644
index 0000000..98cf8ac
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-144x144.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-152x152.png b/client/public/favicons/apple-touch-icon-152x152.png
new file mode 100644
index 0000000..9349c12
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-152x152.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-180x180.png b/client/public/favicons/apple-touch-icon-180x180.png
new file mode 100644
index 0000000..070b46b
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-180x180.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-57x57.png b/client/public/favicons/apple-touch-icon-57x57.png
new file mode 100644
index 0000000..7507114
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-57x57.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-60x60.png b/client/public/favicons/apple-touch-icon-60x60.png
new file mode 100644
index 0000000..9fbee18
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-60x60.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-72x72.png b/client/public/favicons/apple-touch-icon-72x72.png
new file mode 100644
index 0000000..99adfd4
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-72x72.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon-76x76.png b/client/public/favicons/apple-touch-icon-76x76.png
new file mode 100644
index 0000000..b3b5cbd
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon-76x76.png
Binary files differ
diff --git a/client/public/favicons/apple-touch-icon.png b/client/public/favicons/apple-touch-icon.png
new file mode 100644
index 0000000..7507114
--- /dev/null
+++ b/client/public/favicons/apple-touch-icon.png
Binary files differ
diff --git a/client/public/favicons/favicon-160x160.png b/client/public/favicons/favicon-160x160.png
new file mode 100644
index 0000000..18ec48e
--- /dev/null
+++ b/client/public/favicons/favicon-160x160.png
Binary files differ
diff --git a/client/public/favicons/favicon-16x16.png b/client/public/favicons/favicon-16x16.png
new file mode 100644
index 0000000..07985f4
--- /dev/null
+++ b/client/public/favicons/favicon-16x16.png
Binary files differ
diff --git a/client/public/favicons/favicon-192x192.png b/client/public/favicons/favicon-192x192.png
new file mode 100644
index 0000000..f45a635
--- /dev/null
+++ b/client/public/favicons/favicon-192x192.png
Binary files differ
diff --git a/client/public/favicons/favicon-32x32.png b/client/public/favicons/favicon-32x32.png
new file mode 100644
index 0000000..12fdc65
--- /dev/null
+++ b/client/public/favicons/favicon-32x32.png
Binary files differ
diff --git a/client/public/favicons/favicon-48x48.png b/client/public/favicons/favicon-48x48.png
new file mode 100644
index 0000000..885531a
--- /dev/null
+++ b/client/public/favicons/favicon-48x48.png
Binary files differ
diff --git a/client/public/favicons/favicon-96x96.png b/client/public/favicons/favicon-96x96.png
new file mode 100644
index 0000000..91d47ac
--- /dev/null
+++ b/client/public/favicons/favicon-96x96.png
Binary files differ
diff --git a/client/public/favicons/mstile-144x144.png b/client/public/favicons/mstile-144x144.png
new file mode 100644
index 0000000..113de02
--- /dev/null
+++ b/client/public/favicons/mstile-144x144.png
Binary files differ
diff --git a/client/public/icons/github-grey.svg b/client/public/icons/github-grey.svg
new file mode 100644
index 0000000..a7fa471
--- /dev/null
+++ b/client/public/icons/github-grey.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="31px" height="30px" viewBox="0 0 31 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.3 (11970) - http://www.bohemiancoding.com/sketch -->
+    <title>github-grey</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="github-grey" sketch:type="MSLayerGroup" transform="translate(-1.000000, -1.000000)">
+            <path d="M0.5,0 L32.5,0 L32.5,32 L0.5,32 L0.5,0 Z" id="Shape" sketch:type="MSShapeGroup"></path>
+            <path d="M16.4986187,1.5 C8.21659658,1.5 1.5,8.15610973 1.5,16.3674109 C1.5,22.9359042 5.79755364,28.5077607 11.7581417,30.4736541 C12.5086252,30.6105548 12.7821142,30.1514812 12.7821142,29.7572074 C12.7821142,29.4049163 12.7692225,28.4694285 12.7618558,27.2291086 C8.5895362,28.1271769 7.70921452,25.2358351 7.70921452,25.2358351 C7.02687314,23.5181883 6.04341754,23.0609401 6.04341754,23.0609401 C4.68149728,22.1391424 6.14655146,22.1573958 6.14655146,22.1573958 C7.65212253,22.262353 8.44404371,23.6897704 8.44404371,23.6897704 C9.78202216,25.9614086 11.9552012,25.3051981 12.8097394,24.9246143 C12.9460235,23.9644844 13.3336966,23.3091866 13.7618865,22.9377295 C10.4312134,22.5626217 6.92926425,21.2867076 6.92926425,15.5898152 C6.92926425,13.9670861 7.51399675,12.6391498 8.47351054,11.6005301 C8.31880966,11.2245097 7.8040609,9.71221381 8.62084472,7.66600539 C8.62084472,7.66600539 9.87963105,7.26625548 12.7452807,9.19016599 C13.94145,8.85977908 15.225099,8.6954983 16.5004604,8.6891096 C17.774901,8.6954983 19.0576291,8.85977908 20.2556401,9.19016599 C23.1194481,7.26625548 24.3763928,7.66600539 24.3763928,7.66600539 C25.1950183,9.71221381 24.6802695,11.2245097 24.5264895,11.6005301 C25.4878449,12.6391498 26.0679732,13.9670861 26.0679732,15.5898152 C26.0679732,21.3013104 22.5604991,22.5580584 19.2196967,22.9258648 C19.7574665,23.3849383 20.2372234,24.2921333 20.2372234,25.6793932 C20.2372234,27.6662781 20.2188066,29.269841 20.2188066,29.7572074 C20.2188066,30.1551319 20.4895331,30.6178561 21.2501458,30.4727414 C27.2061297,28.5022847 31.5,22.9340788 31.5,16.3674109 C31.5,8.15610973 24.7834034,1.5 16.4986187,1.5" id="github" fill="#90A4AE" sketch:type="MSShapeGroup"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/client/public/icons/github.svg b/client/public/icons/github.svg
new file mode 100644
index 0000000..a7fa471
--- /dev/null
+++ b/client/public/icons/github.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="31px" height="30px" viewBox="0 0 31 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.3 (11970) - http://www.bohemiancoding.com/sketch -->
+    <title>github-grey</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="github-grey" sketch:type="MSLayerGroup" transform="translate(-1.000000, -1.000000)">
+            <path d="M0.5,0 L32.5,0 L32.5,32 L0.5,32 L0.5,0 Z" id="Shape" sketch:type="MSShapeGroup"></path>
+            <path d="M16.4986187,1.5 C8.21659658,1.5 1.5,8.15610973 1.5,16.3674109 C1.5,22.9359042 5.79755364,28.5077607 11.7581417,30.4736541 C12.5086252,30.6105548 12.7821142,30.1514812 12.7821142,29.7572074 C12.7821142,29.4049163 12.7692225,28.4694285 12.7618558,27.2291086 C8.5895362,28.1271769 7.70921452,25.2358351 7.70921452,25.2358351 C7.02687314,23.5181883 6.04341754,23.0609401 6.04341754,23.0609401 C4.68149728,22.1391424 6.14655146,22.1573958 6.14655146,22.1573958 C7.65212253,22.262353 8.44404371,23.6897704 8.44404371,23.6897704 C9.78202216,25.9614086 11.9552012,25.3051981 12.8097394,24.9246143 C12.9460235,23.9644844 13.3336966,23.3091866 13.7618865,22.9377295 C10.4312134,22.5626217 6.92926425,21.2867076 6.92926425,15.5898152 C6.92926425,13.9670861 7.51399675,12.6391498 8.47351054,11.6005301 C8.31880966,11.2245097 7.8040609,9.71221381 8.62084472,7.66600539 C8.62084472,7.66600539 9.87963105,7.26625548 12.7452807,9.19016599 C13.94145,8.85977908 15.225099,8.6954983 16.5004604,8.6891096 C17.774901,8.6954983 19.0576291,8.85977908 20.2556401,9.19016599 C23.1194481,7.26625548 24.3763928,7.66600539 24.3763928,7.66600539 C25.1950183,9.71221381 24.6802695,11.2245097 24.5264895,11.6005301 C25.4878449,12.6391498 26.0679732,13.9670861 26.0679732,15.5898152 C26.0679732,21.3013104 22.5604991,22.5580584 19.2196967,22.9258648 C19.7574665,23.3849383 20.2372234,24.2921333 20.2372234,25.6793932 C20.2372234,27.6662781 20.2188066,29.269841 20.2188066,29.7572074 C20.2188066,30.1551319 20.4895331,30.6178561 21.2501458,30.4727414 C27.2061297,28.5022847 31.5,22.9340788 31.5,16.3674109 C31.5,8.15610973 24.7834034,1.5 16.4986187,1.5" id="github" fill="#90A4AE" sketch:type="MSShapeGroup"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/client/public/icons/logo-text-white.svg b/client/public/icons/logo-text-white.svg
new file mode 100644
index 0000000..9cd3bfe
--- /dev/null
+++ b/client/public/icons/logo-text-white.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"

+	 viewBox="0 0 400 100" enable-background="new 0 0 400 100" xml:space="preserve">

+<g>

+	<g>

+		<path fill="#FFFFFF" d="M139.6,63.5l1.1,3.7h0.2l1.1-3.7l13.3-38.8h6.9l-18.7,50.6H138l-18.7-50.6h6.9L139.6,63.5z"/>

+		<path fill="#FFFFFF" d="M182.6,75.2c-0.2-1.1-0.4-2-0.5-2.8c-0.1-0.8-0.2-1.6-0.2-2.4c-1.2,1.7-2.7,3.1-4.6,4.2

+			c-1.9,1.1-4,1.7-6.2,1.7c-3.7,0-6.4-0.9-8.3-2.8c-1.9-1.9-2.9-4.4-2.9-7.7c0-3.3,1.4-6,4.1-7.9c2.7-1.9,6.4-2.8,11-2.8h6.9v-3.5

+			c0-2.1-0.6-3.7-1.9-4.9c-1.3-1.2-3-1.8-5.3-1.8c-2.1,0-3.7,0.5-5,1.6c-1.3,1-1.9,2.3-1.9,3.8h-6.1l-0.1-0.2

+			c-0.1-2.5,1.1-4.9,3.6-7c2.5-2.1,5.8-3.2,9.8-3.2c4,0,7.2,1,9.6,3c2.4,2,3.6,4.9,3.6,8.7v16.9c0,1.3,0.1,2.5,0.2,3.6

+			c0.1,1.2,0.4,2.3,0.7,3.4H182.6z M172,70.5c2.3,0,4.4-0.6,6.3-1.8c1.9-1.2,3.1-2.5,3.6-4.1V59h-7.2c-2.6,0-4.6,0.6-6.2,1.9

+			c-1.5,1.3-2.3,2.8-2.3,4.6c0,1.6,0.5,2.8,1.5,3.7C168.7,70.1,170.1,70.5,172,70.5z"/>

+		<path fill="#FFFFFF" d="M201.3,40.2l0.5,5.2c1.2-1.9,2.6-3.3,4.4-4.3c1.8-1,3.8-1.5,6-1.5c3.8,0,6.7,1.1,8.8,3.3

+			c2.1,2.2,3.1,5.6,3.1,10.3v22.1h-6.4v-22c0-3.1-0.6-5.3-1.8-6.6c-1.2-1.3-3.1-1.9-5.6-1.9c-1.8,0-3.5,0.4-4.9,1.3

+			c-1.4,0.9-2.5,2.1-3.4,3.6v25.6h-6.4V40.2H201.3z"/>

+		<path fill="#FFFFFF" d="M252.5,75.2c-0.2-1.1-0.4-2-0.5-2.8c-0.1-0.8-0.2-1.6-0.2-2.4c-1.2,1.7-2.7,3.1-4.6,4.2

+			c-1.9,1.1-4,1.7-6.2,1.7c-3.7,0-6.4-0.9-8.3-2.8c-1.9-1.9-2.9-4.4-2.9-7.7c0-3.3,1.4-6,4.1-7.9c2.7-1.9,6.4-2.8,11-2.8h6.9v-3.5

+			c0-2.1-0.6-3.7-1.9-4.9c-1.3-1.2-3-1.8-5.3-1.8c-2.1,0-3.7,0.5-5,1.6c-1.3,1-1.9,2.3-1.9,3.8h-6.1l-0.1-0.2

+			c-0.1-2.5,1.1-4.9,3.6-7c2.5-2.1,5.8-3.2,9.8-3.2c4,0,7.2,1,9.6,3c2.4,2,3.6,4.9,3.6,8.7v16.9c0,1.3,0.1,2.5,0.2,3.6

+			c0.1,1.2,0.4,2.3,0.7,3.4H252.5z M241.9,70.5c2.3,0,4.4-0.6,6.3-1.8c1.9-1.2,3.1-2.5,3.6-4.1V59h-7.2c-2.6,0-4.6,0.6-6.2,1.9

+			c-1.5,1.3-2.3,2.8-2.3,4.6c0,1.6,0.5,2.8,1.5,3.7C238.6,70.1,240,70.5,241.9,70.5z"/>

+		<path fill="#FFFFFF" d="M263.9,58.4c0-5.6,1.2-10.2,3.6-13.6c2.4-3.5,5.8-5.2,10.1-5.2c2.1,0,3.9,0.4,5.5,1.1

+			c1.6,0.8,2.9,1.9,4.1,3.3V24.7h6.4v50.5h-5.2l-0.7-4.3c-1.1,1.6-2.5,2.9-4.2,3.7c-1.7,0.8-3.6,1.3-5.8,1.3c-4.3,0-7.6-1.5-10-4.6

+			c-2.4-3.1-3.6-7.2-3.6-12.2V58.4z M270.3,59c0,3.5,0.7,6.4,2.2,8.5c1.4,2.1,3.7,3.2,6.7,3.2c1.9,0,3.5-0.4,4.8-1.3

+			c1.3-0.9,2.4-2.1,3.2-3.6V49.4c-0.8-1.4-1.9-2.6-3.2-3.5c-1.3-0.9-2.9-1.3-4.7-1.3c-3.1,0-5.3,1.3-6.8,3.8

+			c-1.5,2.5-2.2,5.8-2.2,9.8V59z"/>

+		<path fill="#FFFFFF" d="M307.7,31.2h-6.4v-6.5h6.4V31.2z M307.7,75.2h-6.4V40.2h6.4V75.2z"/>

+		<path fill="#FFFFFF" d="M337.6,70.1c-1.1,1.9-2.5,3.3-4.2,4.3c-1.7,1-3.7,1.5-6,1.5c-3.8,0-6.8-1.2-8.9-3.7

+			c-2.1-2.4-3.2-6.2-3.2-11.4V40.2h6.4v20.7c0,3.7,0.6,6.3,1.7,7.7c1.1,1.4,2.8,2.1,5.2,2.1c2.3,0,4.1-0.5,5.6-1.4

+			c1.5-0.9,2.6-2.2,3.4-3.9V40.2h6.4v35.1h-5.7L337.6,70.1z"/>

+		<path fill="#FFFFFF" d="M357,40.2l0.5,4.6c1.1-1.7,2.6-3,4.4-3.9c1.8-0.9,3.8-1.4,6.1-1.4c2.3,0,4.3,0.5,6,1.6

+			c1.7,1.1,2.9,2.7,3.8,4.9c1.1-2,2.6-3.6,4.4-4.7c1.8-1.2,3.9-1.7,6.3-1.7c3.6,0,6.4,1.2,8.5,3.7c2.1,2.5,3.1,6.1,3.1,11.1v21h-6.4

+			v-21c0-3.5-0.6-5.9-1.8-7.3c-1.2-1.4-3-2.2-5.3-2.2c-2.2,0-3.9,0.8-5.3,2.3c-1.3,1.5-2.1,3.4-2.4,5.7V53v22.3h-6.4v-21

+			c0-3.3-0.6-5.7-1.8-7.2c-1.2-1.5-3-2.3-5.3-2.3c-1.9,0-3.5,0.4-4.8,1.2c-1.3,0.8-2.2,1.9-2.9,3.4v26h-6.4V40.2H357z"/>

+	</g>

+	<path fill="#FFFFFF" d="M95.8,13C92.7,7.6,87.2,4.4,81,4.4H15c-1.4,0-2.6,0.7-3.3,1.9C11,7.5,11,9,11.7,10.1l8,13.9L3.8,24

+		c-1.4,0-2.6,0.7-3.3,1.9c-0.7,1.2-0.7,2.6,0,3.8L33.4,87c3.1,5.4,8.6,8.6,14.9,8.6c6.2,0,11.8-3.2,14.9-8.6l32.7-56.8

+		C98.9,24.8,98.9,18.4,95.8,13z M89.2,26.3L56.5,83.2c-1.7,3-4.8,4.8-8.3,4.8c-3.4,0-6.5-1.8-8.3-4.8L10.4,31.7h13.7l17.8,30.8

+		c-0.6,1-0.9,2.2-0.9,3.5c0,4,3.3,7.3,7.3,7.3c4,0,7.3-3.3,7.3-7.3c0-1.3-0.3-2.5-0.9-3.5l15.7-27.3c3.9-0.1,7.1-3.3,7.1-7.3

+		c0-4-3.3-7.3-7.3-7.3c-2.6,0-4.9,1.4-6.2,3.5l-35.5,0l-6.9-12H81c3.4,0,6.5,1.8,8.2,4.8C90.9,19.8,90.9,23.3,89.2,26.3z M63.6,31.7

+		L48.3,58.4L32.9,31.7L63.6,31.7z"/>

+</g>

+</svg>

diff --git a/client/public/icons/twitter.svg b/client/public/icons/twitter.svg
new file mode 100644
index 0000000..33bffff
--- /dev/null
+++ b/client/public/icons/twitter.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="31px" height="30px" viewBox="0 0 31 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.3 (11970) - http://www.bohemiancoding.com/sketch -->
+    <title>twitter</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="twitter" sketch:type="MSLayerGroup" transform="translate(-1.000000, -1.000000)">
+            <path d="M0.5,0 L32.5,0 L32.5,32 L0.5,32 L0.5,0 Z" id="Shape" sketch:type="MSShapeGroup"></path>
+            <path d="M1.5,15.0435275 C1.99372565,7.2047279 8.5137978,1 16.484985,1 C24.7777778,1 31.5,7.7155 31.5,16 C31.5,24.2845 24.7777778,31 16.484985,31 C8.5137978,31 1.99372565,24.7952721 1.5,16.9564725 L1.5,15.0435275 L1.5,15.0435275 Z M19.346457,8.5539494 C17.6408047,9.17460105 16.5629679,10.7754004 16.6854049,12.5272922 L16.7264344,13.2033011 L16.0439129,13.1205909 C13.5606551,12.8034269 11.3906537,11.7275438 9.54823658,9.91964354 L8.64754273,9.02285726 L8.41569384,9.68518961 C7.92464312,11.1615981 8.23855088,12.7207167 9.26168177,13.7692468 C9.80743842,14.348869 9.68500137,14.4315792 8.74327808,14.0864109 C8.41569384,13.9763478 8.12913904,13.8936376 8.10178608,13.9346671 C8.00605073,14.031705 8.33363497,15.2866847 8.59283681,15.7835968 C8.94777401,16.4732821 9.67132489,17.149291 10.4626069,17.5498164 L11.1314519,17.8669805 L10.3401698,17.880657 C9.57558953,17.880657 9.54823658,17.8943334 9.63029545,18.1841445 C9.90317377,19.0809308 10.9810106,20.0330743 12.1819357,20.4472762 L13.0279236,20.7370873 L12.2913475,21.177991 C11.1998343,21.8129704 9.91685025,22.1718151 8.63386625,22.1991681 C8.01972721,22.2128446 7.515,22.2682017 7.515,22.3098824 C7.515,22.4472985 9.1796229,23.2203452 10.1486991,23.5238328 C13.0552766,24.4206191 16.508262,24.0344213 19.1009317,22.5026557 C20.9433488,21.4130962 22.7857659,19.2463511 23.6454303,17.149291 C24.1091281,16.0317272 24.5734771,13.9900243 24.5734771,13.0105278 C24.5734771,12.3755484 24.6138553,12.2928383 25.3784356,11.5341193 C25.8284569,11.0925644 26.2517765,10.6099801 26.3338354,10.4719128 C26.4699489,10.2094546 26.4562724,10.2094546 25.7607258,10.4445598 C24.6008301,10.8581105 24.4367123,10.8027533 25.0098219,10.1821017 C25.4331415,9.74054679 25.9378688,8.94014712 25.9378688,8.70569319 C25.9378688,8.66466375 25.7333728,8.73369741 25.5015239,8.85743699 C25.2553473,8.9955043 24.7095907,9.20260527 24.3005988,9.32634484 L23.5633715,9.56145003 L22.8945265,9.10556739 C22.5265641,8.85743699 22.0075091,8.58195362 21.7346308,8.49859222 C21.0390842,8.30581899 19.9742726,8.33317195 19.346457,8.5539494 L19.346457,8.5539494 L19.346457,8.5539494 Z" id="path-1" fill="#90A4AE" sketch:type="MSShapeGroup"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/client/public/index.html b/client/public/index.html
index 8fb013a..a0d203e 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -1,10 +1,58 @@
-<!DOCTYPE html>
+<!doctype html>
 <html>
 <head>
-  <title>Playground</title>
+  <meta
+     name="viewport"
+     content="width=device-width,
+              initial-scale=1,
+              maximum-scale=1,
+              user-scalable=no,
+              minimal-ui">
+
+  <meta
+     name="apple-mobile-web-app-capable"
+     content="yes">
+
+  <meta
+     name="apple-mobile-web-app-status-bar-style"
+     content="black">
+
+  <title>Vanadium Playground</title>
+
+  <link href='//fonts.googleapis.com/css?family=Source+Code+Pro:400,500%7CRoboto:500,400italic,300,500italic,300italic,400'
+    rel='stylesheet'
+    type='text/css'>
   <link rel="stylesheet" href="/bundle.css">
-  <script src="/bundle.js" async></script>
+
+  <link rel="apple-touch-icon" sizes="57x57" href="/favicons/apple-touch-icon-57x57.png">
+  <link rel="apple-touch-icon" sizes="114x114" href="/favicons/apple-touch-icon-114x114.png">
+  <link rel="apple-touch-icon" sizes="72x72" href="/favicons/apple-touch-icon-72x72.png">
+  <link rel="apple-touch-icon" sizes="144x144" href="/favicons/apple-touch-icon-144x144.png">
+  <link rel="apple-touch-icon" sizes="60x60" href="/favicons/apple-touch-icon-60x60.png">
+  <link rel="apple-touch-icon" sizes="120x120" href="/favicons/apple-touch-icon-120x120.png">
+  <link rel="apple-touch-icon" sizes="76x76" href="/favicons/apple-touch-icon-76x76.png">
+  <link rel="apple-touch-icon" sizes="152x152" href="/favicons/apple-touch-icon-152x152.png">
+  <link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon-180x180.png">
+  <link rel="icon" type="image/png" href="/favicons/favicon-192x192.png" sizes="192x192">
+  <link rel="icon" type="image/png" href="/favicons/favicon-160x160.png" sizes="160x160">
+  <link rel="icon" type="image/png" href="/favicons/favicon-96x96.png" sizes="96x96">
+  <link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
+  <link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
+  <meta name="msapplication-TileColor" content="#da532c">
+  <meta name="msapplication-TileImage" content="/favicons/mstile-144x144.png">
+
+  <!-- NOTE: Avoid the async attribute to prevent a brief flash of un-styled content -->
+  <script src="/bundle.js"></script>
+
+  <script>
+    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+    ga('create', 'UA-59720824-3', 'auto');
+    ga('send', 'pageview');
+  </script>
 </head>
-<body>
-</body>
+<body></body>
 </html>
diff --git a/client/stylesheets/components/buttons.css b/client/stylesheets/components/buttons.css
new file mode 100644
index 0000000..a3d8724
--- /dev/null
+++ b/client/stylesheets/components/buttons.css
@@ -0,0 +1,67 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+.button {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 14px;
+  font-weight: var(--font-weight-medium);
+  text-transform: uppercase;
+  padding: 12px;
+  border-radius: 4px;
+  text-decoration: none;
+}
+
+.button-passive {
+  inherit: .button;
+  color: var(--cyan-800);
+  border: 1px solid var(--cyan-800);
+  background-color: transparent;
+  transition: background-color 0.28s cubic-bezier(0.4, 0.0, 0.2, 1);
+/* See: https://spec.googleplex.com/quantum/resources/animation.html */
+}
+
+.button-passive:hover, .button-passive:active {
+  color: var(--white);
+  background-color: var(--cyan-800);
+  text-decoration: none;
+}
+
+.button-secondary {
+  inherit: .button;
+  color: var(--cyan-800);
+  background-color: var(--white);
+  border: 1px solid var(--white);
+  box-shadow: var(--drop-shadow);
+}
+
+.button-secondary:hover {
+  text-decoration: none;
+  background-color: var(--white);
+}
+
+.button-secondary:active {
+  background-color: var(--white);
+  box-shadow: var(--drop-shadow-intense);
+}
+
+.button-primary {
+  inherit: .button;
+  color: var(--white);
+  background-color: var(--cyan-800);
+  border: 1px solid var(--cyan-800);
+  box-shadow: var(--drop-shadow);
+}
+
+.button-primary:hover {
+  text-decoration: none;
+  color: var(--white);
+  background-color: var(--cyan-800);
+}
+
+.button-primary:active {
+  color: var(--cyan-800);
+  color: var(--white);
+  box-shadow: var(--drop-shadow-intense);
+}
diff --git a/client/stylesheets/components/footer.css b/client/stylesheets/components/footer.css
new file mode 100644
index 0000000..fe7b290
--- /dev/null
+++ b/client/stylesheets/components/footer.css
@@ -0,0 +1,48 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+footer {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  background-color: var(--blue-grey-100);
+  padding: 0 var(--gutter);
+  padding-top: var(--gutter);
+}
+
+footer .main {
+  flex: 1;
+}
+
+footer .main a {
+  display: inline-block;
+  margin-right: var(--gutter);
+  margin-bottom: var(--gutter);
+  font-size: 14px;
+  line-height: 14px;
+  font-weight: var(--font-weight-medium);
+  color: var(--blue-grey-500);
+  text-transform: uppercase;
+}
+
+footer .social {
+  display: flex;
+}
+
+footer .social a {
+  margin-left: var(--gutter);
+  margin-bottom: var(--gutter);
+}
+
+@media (--narrow-window) {
+  footer .main,
+  footer .social {
+    flex: 0 0 100%;
+  }
+
+  footer .social a {
+    margin-left: 0;
+    margin-right: var(--gutter);
+  }
+}
diff --git a/client/stylesheets/components/header.css b/client/stylesheets/components/header.css
new file mode 100644
index 0000000..438eec3
--- /dev/null
+++ b/client/stylesheets/components/header.css
@@ -0,0 +1,75 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+header {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: var(--header-height);
+  background-color: var(--cyan-800);
+  color: white;
+  z-index: 900;
+  box-shadow: var(--drop-shadow);
+  transition:
+    background-color ease-in-out 0.3s,
+    box-shadow ease-in-out 0.3s;
+}
+
+header a,
+header a:hover,
+header a:active {
+  color: inherit;
+  text-decoration: none;
+}
+
+header nav {
+  display: flex;
+  align-items: center;
+}
+
+header .left {
+  width: var(--sidebar-width);
+  padding-left: var(--gutter);
+}
+
+
+/* TODO(jasoncampbell): move to icons */
+header a.logo {
+  text-indent: -1000em;
+  overflow: hidden;
+
+  display: block;
+  width: 176px;
+  height: 44px;
+
+  background-image: url(/icons/logo-text-white.svg);
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: auto 40px;
+}
+
+header .main {
+  flex: 1;
+  justify-content: flex-end;
+  margin-right: var(--gutter);
+}
+
+header .main a {
+  font-size: 14px;
+  line-height: 14px;
+  font-weight: var(--font-weight-medium);
+  text-transform: uppercase;
+  margin-left: var(--gutter);
+}
+
+header .bundle-run-or-stop {
+  inherit: .button-passive;
+  color: var(--white);
+  border: 1px solid var(--white);
+  padding-left: var(--gutter-half);
+  padding-right: var(--gutter-half);
+  text-align: center;
+  /* STOP || RUN */
+  min-width: calc(2.5 * var(--gutter));
+}
diff --git a/client/stylesheets/icons.css b/client/stylesheets/icons.css
new file mode 100644
index 0000000..5cc16cd
--- /dev/null
+++ b/client/stylesheets/icons.css
@@ -0,0 +1,110 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+@font-face {
+  font-family: 'icons';
+  src:url('/fonts/icons.eot?-al0ki6');
+  src:url('/fonts/icons.eot?#iefix-al0ki6') format('embedded-opentype'),
+  url('/fonts/icons.woff?-al0ki6') format('woff'),
+  url('/fonts/icons.ttf?-al0ki6') format('truetype'),
+  url('/fonts/icons.svg?-al0ki6#icons') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+
+[class^="icon-"], [class*=" icon-"], .link-with-arrow::after {
+  font-family: 'icons';
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+[class^="icon-"]:before, [class*=" icon-"]:before {
+  /* Matches the line-height of .title */
+  display: inline-block;
+  font-size: 24px;
+  line-height: 32px;
+  width: 32px;
+  text-align: center;
+}
+
+.icon-arrow-forward:before {
+  content: "\e603";
+}
+
+.icon-arrow-back:before {
+  content: "\e604";
+}
+
+.icon-drop-up:before {
+  content: "\e605";
+}
+
+.icon-menu:before {
+  content: "\e602";
+}
+
+.icon-logo:before {
+  content: "\e600";
+}
+
+.icon-search:before {
+  content: "\e601";
+}
+
+.link-with-arrow {
+  text-transform: uppercase;
+  font-weight: var(--font-weight-medium);
+}
+
+.link-with-arrow:hover {
+  text-decoration: none;
+}
+
+.link-with-arrow::after {
+  content: "\e603";
+  padding: 0 0 0 5px;
+  color: var(--deeporange-A200);
+}
+
+/* New icons setup */
+/* TODO(jasoncampbell): inline svgs automatically */
+.icon-base {
+  content: ' ';
+  display: inline-block;
+  /* matches typography line-height */
+  width: 28px;
+  height: 28px;
+  vertical-align: middle;
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 24px auto;
+}
+
+.icon-twitter, .icon-github {
+  inherit: .icon-base;
+  width: 32px;
+  height: 32px;
+  background-size: 30px auto;
+}
+
+.icon-clipboard {
+  inherit: .icon-base;
+  background-image: url(/icons/clipboard.svg);
+  background-size: 18px auto;
+}
+
+.icon-twitter {
+  background-image: url(/icons/twitter.svg);
+}
+
+.icon-github {
+  background-image: url(/icons/github-grey.svg);
+}
diff --git a/client/stylesheets/index.css b/client/stylesheets/index.css
index 55b47ce..93be513 100644
--- a/client/stylesheets/index.css
+++ b/client/stylesheets/index.css
@@ -2,176 +2,164 @@
 /* Use of this source code is governed by a BSD-style */
 /* license that can be found in the LICENSE file. */
 
-/* CSS rules for elements inside Playground instances. */
+@import "./variables.css";
+@import "./reset.css";
+@import "./typography.css";
+@import "./icons.css";
+@import "./components/buttons.css";
+@import "./components/header.css";
+@import "./components/footer.css";
 
-.pg {
-  width: 100%;
+body, .playground {
+  display: flex;
+  min-height: 100vh;
+  flex-direction: column;
 }
 
-.pg .widget-bar {
+main {
+  display: flex;
+  flex: 1;
+}
+
+.bundle {
+  flex: 1;
+  display: flex;
+  flex-wrap: wrap;
+  align-content: center;
+  justify-content: center;
+}
+
+.bundle .code,
+.bundle .results {
+  flex: 1;
+}
+
+.bundle .code {
+  display: flex;
+  flex-direction: column;
+  background-color: var(--grey-50);
+}
+
+.bundle .tabs {
+  display: flex;
+  background-color: var(--cyan-700);
+}
+
+.bundle .tabs .tab {
+  padding: calc(var(--gutter-half) - 2px) var(--gutter);
+  border-bottom: 4px solid transparent;
+  color: var(--white-54);
+}
+
+.bundle .tabs .tab.active {
+  border-bottom-color: var(--deeporange-A200);
+  color: var(--white);
+}
+
+.editors {
+  flex: 1;
+  display: flex;
   position: relative;
-  margin-top: 2px;
-  overflow: auto;
 }
 
-.pg .btn,
-.pg .tab {
-  cursor: pointer;
-  display: inline-block;
-  font-size: 14px;
-  font-weight: normal;
-  padding: 6px 12px;
-  user-select: none;
-}
-
-.pg .tab {
-  background-color: #bbb;
-  margin-right: 2px;
-}
-
-.pg .tab.active {
-  background-color: #ddd;
-}
-
-.pg .btns {
-  float: right;
-}
-
-.pg .btn {
-  background-color: #0277bd;
-  color: #fff;
-  border: 0;
-  margin-left: 2px;
-  min-width: 70px;
-}
-
-/* TODO(sadovsky): Add .no-touch once we integrate Modernizr. */
-.pg .btn:hover {
-  background-color: #01579b;
-}
-
-.pg .btn:focus,
-.pg .btn:active {
-  outline: 0;
-}
-
-.pg .btn:active {
-  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
-
-.pg .btn:disabled {
-  background-color: #bbb;
-}
-
-/* TODO(sadovsky): Make this reactive. */
-.pg .editors {
-  position: relative;
-  border-top: 4px solid #ddd;
-  height: 200px;
-}
-
-.pg .editor {
-  position: absolute;  /* stack editors one on top of the other */
+.editor {
+  flex: 1;
+  position: absolute;
   width: 100%;
   height: 100%;
   visibility: hidden;
 }
 
-.pg .editor.active {
+.editor.active {
   visibility: visible;
 }
 
-.pg .ace_editor {
+.ace_editor {
   width: 100%;
   height: 100%;
-  font-family: "Source Code Pro", monospace;
-  font-size: 13px;
 }
 
-.pg .console {
-  background-color: #ddd;
-  width: 100%;
-  height: 4px;  /* matches .editors border-top; expands on run */
-  transition: height 0.2s;
-  overflow: hidden;
+.results {
+  display: flex;
+  flex-direction: column;
+  background-color: var(--grey-50);
 }
 
-.pg .console .text {
-  color: #222;
-  font-family: "Source Code Pro", monospace;
-  font-size: 13px;
-  overflow-y: auto;
-  padding: 8px;
+.results .controls {
+  background-color: var(--cyan-600);
+  padding: var(--gutter-half) var(--gutter);
+  color: var(--white);
 }
 
-.pg .console .text .timestamp {
-  color: #9e9e9e;
+.results .controls a {
+  color: var(--blue-grey-700);
 }
 
-.pg .console .text .filename {
-  font-weight: bold;
+.results .console {
+  flex: 1;
+  position: relative;
 }
 
-/* Known bug: Chrome doesn't show tabs; refer to
-   https://code.google.com/p/chromium/issues/detail?id=398274 */
-.pg .console .text .message {
-  white-space: pre-wrap;
-  tab-size: 4;
-}
-
-.pg .console .text .stderr {
-  color: #d01716;
-}
-
-.pg .console .text .svc-stderr {
-  color: #d01716;
-}
-
-.pg .console .text .debug {
-  color: #455ede;
-}
-
-.pg .console .text .syserr {
-  color: #d01716;
-  font-weight: bold;
-}
-
-.pg .console.open {
-  height: 200px;
-  overflow: auto;
-}
-
-.pg .bundleid {
-  color: #0033ff;
-  width: 64ch; /* input size doesn't work properly in Chrome */
-  font-family: monospace;
-  font-size: 12px;
-  user-select: all;
-}
-
-.pg .spinner-overlay {
+.scroller {
   position: absolute;
-  height: 100%;
-  width: 100%;
-  z-index: 1999999999; /* one less than spinner-internal */
-  background-color: #000;
-  opacity: 0.5;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  padding: var(--gutter);
 }
 
-.pg .notif {
-  font-size: 14px;
+.log {
+  margin-bottom: var(--gutter);
 }
 
-.pg .notif.success {
-  color: #33cc33;
+.log .meta {
+  display: flex;
 }
 
-.pg .notif.error {
-  color: #d01716;
+.log .meta .source {
+  flex: 1;
+  font-weight: bold;
 }
 
-.clearfix {
-  display: table;
-  clear: both;
-  content: "";
+.log .meta .source .stream {
+  font-weight: normal;
+}
+
+.log .message {
+  font-family: "Source Code Pro", monospace;
+  padding-left: var(--gutter-half);
+  overflow: hidden;
+  overflow-y: scroll;
+}
+
+.log .message pre {
+  padding: 0;
+  margin: 0;
+  white-space: pre-wrap;       /* CSS 3 */
+  white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+  white-space: -pre-wrap;      /* Opera 4-6 */
+  white-space: -o-pre-wrap;    /* Opera 7 */
+  word-wrap: break-word;
+}
+
+.log.debug pre {
+  color: #00B9F7;
+}
+
+.log.stdout {
+
+}
+
+.log.stderr pre {
+  color: #F03A76;
+}
+
+.log.debug {
+  display: none;
+}
+
+.console.debug .log.debug {
+  display: block;
 }
diff --git a/client/stylesheets/reset.css b/client/stylesheets/reset.css
new file mode 100644
index 0000000..44e5734
--- /dev/null
+++ b/client/stylesheets/reset.css
@@ -0,0 +1,87 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+*,
+*:before,
+*:after {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+/**
+ * Based on Eric Meyer's reset:
+ * http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/)
+ */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+  border: 0;
+  outline: 0;
+
+  font-weight: inherit;
+  font-style: inherit;
+  font-family: inherit;
+  font-size: 100%;
+  vertical-align: baseline;
+}
+
+body {
+  line-height: 1;
+  color: black;
+  background: white;
+}
+
+ol, ul {
+  list-style: none;
+}
+
+table {
+  border-collapse: separate;
+  border-spacing: 0;
+  vertical-align: middle;
+}
+
+caption, th, td {
+  text-align: left;
+  font-weight: normal;
+  vertical-align: middle;
+}
+
+a img {
+  border: none
+}
+
+p img {
+  max-width: 100%;
+}
+
+article, aside, canvas, details, figcaption,
+figure, footer, header, hgroup, menu, nav,
+section, summary, main {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  outline: 0;
+  display: block
+}
+
+audio, canvas, video {
+  display: inline;
+  display: inline-block;
+  zoom: 1;
+}
+
+audio:not([controls]), [hidden] {
+  display: none;
+}
+
+textarea, input {
+  outline: none;
+}
diff --git a/client/stylesheets/typography.css b/client/stylesheets/typography.css
new file mode 100644
index 0000000..b8d0ae8
--- /dev/null
+++ b/client/stylesheets/typography.css
@@ -0,0 +1,259 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+/**
+ * Vertical spacing is added to element selectors directly so that type rules
+ * can be inherited without effecting the layout of the elements doing the
+ * inheritance.
+ */
+.type-base {
+  font-family: var(--primary-font);
+  font-weight: var(--font-weight-regular);
+  font-style: normal;
+  color: var(--blue-grey-700);
+  -webkit-font-smoothing: antialiased;
+}
+
+.type-body {
+  font-size: 14px;
+  line-height: 24px;
+}
+
+body {
+  inherits: .type-base;
+  inherits: .type-body;
+}
+
+a {
+  color: var(--link-primary);
+  font-weight: var(--font-weight-medium);
+  text-decoration: none;
+}
+
+a:active, a.active, a:hover, a.hover {
+  color: var(--link-primary-active);
+}
+
+/* Headings */
+
+.type-display3 {
+  font-size: 56px;
+  font-weight: var(--font-weight-light);
+  line-height: 66px;
+  color: var(--blue-grey-900);
+}
+
+.type-display1 {
+  font-size: 34px;
+  font-weight: var(--font-weight-regular);
+  line-height: 40px;
+  color: var(--cyan-700);
+}
+
+.type-headline {
+  font-size: 24px;
+  font-weight: var(--font-weight-regular);
+  line-height: 32px;
+  color: var(--blue-grey-500);
+}
+
+.type-title {
+  font-size: 20px;
+  font-weight: var(--font-weight-medium);
+  line-height: 30px;
+  color: var(--blue-grey-800);
+}
+
+.type-caption {
+  font-size: 12px;
+  font-weight: var(--font-weight-medium);
+  line-height: 16px;
+  color: var(--blue-grey-500);
+}
+
+.type-smallhead {
+  font-size: 16px;
+  font-weight: var(--font-weight-medium);
+  line-height: 14px;
+  color: var(--blue-grey-800);
+}
+
+.type-subhead {
+  font-size: 12px;
+  font-weight: var(--font-weight-medium);
+  line-height: 14px;
+  color: var(--blue-grey-800);
+  text-transform: uppercase;
+}
+
+.type-bold {
+  font-weight: var(--font-weight-medium);
+  color: var(--blue-grey-900);
+}
+
+h1 {
+  inherits: .type-headline;
+  position: relative;
+  margin: var(--gutter-wide) 0 var(--gutter-half);
+}
+
+h2 {
+  inherits: .type-title;
+  position: relative;
+  margin: var(--gutter) 0 var(--gutter-half);
+}
+
+h3, h4, .title {
+  inherits: .type-smallhead;
+  position: relative;
+  margin: var(--gutter) 0 var(--gutter-half);
+}
+
+h5 {
+  inherit: .type-subhead;
+  margin: var(--gutter) 0 var(--gutter-half);
+}
+
+h6 {
+  inherit: .type-caption;
+}
+
+strong {
+  inherit: .type-bold;
+}
+
+.intro-head {
+  inherits: .type-display3;
+  margin: var(--gutter) 0;
+}
+
+.page-head {
+  inherits: .type-display1;
+  position: relative;
+  margin: var(--gutter-wide) 0 var(--gutter);
+}
+
+.contrast-head {
+  inherits: .type-display1;
+  padding: var(--gutter-double);
+  padding-top: calc(var(--header-height) + var(--gutter-half));
+  margin-bottom: 0;
+  color: #fff;
+  background-color: var(--blue-grey-500);
+}
+
+/* Add appropriate vertical rhythm and set stacking order */
+p, blockquote, pre, address,
+dl, dt, dd,
+ol, ul,
+figcaption, article, aside {
+  inherit: .type-body;
+  margin: var(--gutter-half) 0 var(--gutter-half);
+  position: relative;
+  z-index: 1;
+}
+
+/* Lists */
+ul, ol {
+  list-style-position: outside;
+}
+
+ul {
+  list-style-type: disc;
+}
+
+ol {
+  list-style-type: decimal;
+}
+
+/* Nested lists */
+
+ol ol, ul ol {
+  list-style-type: lower-latin;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+  margin: 0;
+  margin-left: var(--gutter-half); /* nested indent */
+}
+
+li {
+  margin-left: var(--gutter); /* allows room for outside bullets */
+  margin-bottom: var(--gutter-half);
+}
+
+ol {
+  list-style: latin;
+}
+
+/* Inline elements */
+sub, sup {
+  line-height: 0;
+  vertical-align: super;
+  font-size: smaller;
+}
+
+sub {
+  vertical-align: sub;
+}
+
+figure {
+  margin: 0 -var(--gutter);
+}
+
+figure img {
+  max-width: 100%;
+  display: block;
+}
+
+figcaption {
+  margin: var(--gutter-half) var(--gutter);
+}
+
+figcaption, small {
+  inherit: .type-caption;
+}
+
+em {
+  font-style: italic;
+}
+
+sub {
+  vertical-align: sub;
+}
+
+.text-yellow {
+  color: var(--text-yellow);
+}
+
+.text-orange {
+  color: var(--text-orange);
+}
+
+.text-red {
+  color: var(--text-red);
+}
+
+.text-magenta {
+  color: var(--text-magenta);
+}
+
+.text-violet {
+  color: var(--text-violet);
+}
+
+.text-blue {
+  color: var(--text-blue);
+}
+
+.text-cyan {
+  color: var(--text-cyan);
+}
+
+.text-green {
+  color: var(--text-green);
+}
diff --git a/client/stylesheets/variables.css b/client/stylesheets/variables.css
new file mode 100644
index 0000000..19f9a33
--- /dev/null
+++ b/client/stylesheets/variables.css
@@ -0,0 +1,98 @@
+/* Copyright 2015 The Vanadium Authors. All rights reserved. */
+/* Use of this source code is governed by a BSD-style */
+/* license that can be found in the LICENSE file. */
+
+/* Variables */
+:root {
+
+  /* Colors */
+  --white:                  #FFFFFF;
+  --white-54:               rgba(255,255,255,.54);
+  --white-87:               rgba(255,255,255,.87);
+  --black:                  #000000;
+  --black-54:               rgba(0,0,0,.54);
+  --black-87:               rgba(0,0,0,.87);
+  --grey-50:                #FAFAFA;
+  --grey-100:               #F5F5F5;
+  --grey-700:               #616161;
+  --blue-grey-50:           #ECEFF1;
+  --blue-grey-100:          #CFD8DC;
+  --blue-grey-200:          #B0BEC5;
+  --blue-grey-300:          #90A4AE;
+  --blue-grey-500:          #607D8B;
+  --blue-grey-700:          #455A64;
+  --blue-grey-800:          #37474F;
+  --blue-grey-900:          #263238;
+  --cyan-50:                #E0F7FA;
+  --cyan-600:               #00ACC1;
+  --cyan-700:               #0097A7;
+  --cyan-800:               #00838F;
+  --cyan-900:               #006064;
+  --deeporange-A200:        #FF6E40;
+
+  --sidebar-color:          var(--grey-50);
+  --codebox-color:          #FFFBF0;
+  --codeboxoutput-color:    #F9F8FB;
+
+  --link-primary:           var(--cyan-700);
+  --link-primary-active:    var(--cyan-800);
+  --link-secondary:         var(--black-87);
+  --link-secondary-active:  var(--black);
+
+  /* Syntax highlighting */
+
+  --text-yellow:            #F57F17;
+  --text-orange:            #D84315;
+  --text-red:               #D32F2F;
+  --text-magenta:           #D81B60;
+  --text-violet:            #7E57C2;
+  --text-blue:              #2196F3;
+  --text-cyan:              #26A69A;
+  --text-green:             #9E9D24;
+  --text-base03:            #263238;
+  --text-base02:            #37474F;
+  --text-base01:            #546E7A;
+  --text-base00:            #607D8B;
+  --text-base0:             #78909C;
+  --text-base1:             #90A4AE;
+  --text-base2:             #ECEFF1;
+  --text-base3:             #FFF8E1;
+
+  /* Fonts */
+  --primary-font: Roboto;
+  --font-weight-light: 300;
+  --font-weight-regular: 400;
+  --font-weight-medium: 500;
+
+  /* Rhythm & motion (gutters & margins) */
+  --gutter-quarter: 7px;
+  --gutter-half: 14px;
+  --gutter: 28px;
+  --gutter-wide: 42px;
+  --gutter-double: 56px;
+
+  /* Shadows */
+  --drop-shadow:          rgba(0, 0, 0, 0.30) 0px 2px 5px 0px;
+  --drop-shadow-intense:  rgba(0, 0, 0, 0.60) 0px 2px 5px 0px;
+
+  /* Heights */
+  --header-height: calc(3 * var(--gutter));
+
+  /* Widths */
+  --sidebar-width-half: 8em;
+  --sidebar-width: 16em;
+  --content-max-width: 40em;
+  --content-min-width: 24em;
+  --wide-width: calc(var(--content-max-width) + var(--sidebar-width-half));
+  --max-width: calc(var(--content-max-width) + var(--sidebar-width));
+
+  /* Radii */
+  --box-radius: 6px;
+
+  /* Animation curves */
+  --anim-fastslow: cubic-bezier(0.4, 0.0, 0.2, 1);
+}
+
+/* Break Points */
+@custom-media --narrow-window screen and (max-width: 740px);
+@custom-media --double-sidebar screen and (max-width: 72em);