diff --git a/.gitignore b/.gitignore
index 7cbd6fc236..f5a3b1bdcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ lib
 .DS_Store
 key.pem
 cert.pem
-build
+vector/components.css
diff --git a/README.md b/README.md
index c834e01379..529e7cdf0f 100644
--- a/README.md
+++ b/README.md
@@ -36,9 +36,10 @@ about them:
 2. `cd matrix-react-sdk`
 3. `git checkout develop`
 4. `npm install`
-5. `npm start` (to start the dev rebuilder)
-6. `cd ../vector-web`
-7. Link the react sdk package into the example:
+5. `npm run build`
+6. `npm start` (to start the dev rebuilder)
+7. `cd ../vector-web`
+8. Link the react sdk package into the example:
    `npm link path/to/your/react/sdk`
 
 Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk`
@@ -53,6 +54,34 @@ about "Cannot resolve module 'source-map-loader'" due to shortcomings in webpack
 Deployment
 ==========
 
-Just run `npm build` and then mount the `vector` directory on your webserver to
+Just run `npm run build` and then mount the `vector` directory on your webserver to
 actually serve up the app, which is entirely static content.
 
+Enabling encryption
+===================
+
+End-to-end encryption in Vector and Matrix is not yet considered ready for
+day-to-day use; it is experimental and should be considered only as a
+proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview
+of the current progress.
+
+To build a version of vector with support for end-to-end encryption, install
+the olm module with `npm i https://matrix.org/packages/npm/olm/olm-0.1.0.tgz`
+before running `npm start`. The olm library will be detected and used if
+available.
+
+To enable encryption for a room, type
+
+```
+/encrypt on
+```
+
+in the message bar in that room. Vector will then generate a set of keys, and
+encrypt all outgoing messages in that room. (Note that other people in that
+room will send messages in the clear unless they also `/encrypt on`.)
+
+Note that historical encrypted messages cannot currently be decoded - history
+is therefore lost when the page is reloaded.
+
+There is currently no visual indication of whether encryption is enabled for a
+room, or whether a particular message was encrypted.
diff --git a/package.json b/package.json
index fb7558ad5a..88df783386 100644
--- a/package.json
+++ b/package.json
@@ -9,46 +9,51 @@
   },
   "license": "Apache-2.0",
   "style": "bundle.css",
+  "matrix-react-parent": "matrix-react-sdk",
   "scripts": {
-    "reskindex": "reskindex vector -h src/skins/vector/header",
+    "reskindex": "reskindex -h src/header",
     "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
-    "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css -c uglifycss --no-watch",
+    "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch",
     "build:compile": "babel --source-maps -d lib src",
     "build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
     "build": "npm run build:css && npm run build:compile && npm run build:bundle",
     "start:js": "webpack -w src/vector/index.js vector/bundle.js",
-    "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/bundle.css",
+    "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css",
     "//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270",
     "start": "parallelshell \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
-    "clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map",
+    "clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css*",
     "prepublish": "npm run build:css && npm run build:compile"
   },
   "dependencies": {
     "classnames": "^2.1.2",
+    "extract-text-webpack-plugin": "^0.9.1",
     "filesize": "^3.1.2",
     "flux": "~2.0.3",
+    "gemini-scrollbar": "^1.3.0",
+    "gfm.css": "^1.1.1",
+    "highlight.js": "^9.0.0",
     "linkifyjs": "^2.0.0-beta.4",
-    "matrix-js-sdk": "^0.3.0",
-    "matrix-react-sdk": "^0.0.2",
+    "matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop",
+    "matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop",
     "modernizr": "^3.1.0",
     "q": "^1.4.1",
     "react": "^0.14.2",
     "react-dnd": "^2.0.2",
     "react-dnd-html5-backend": "^2.0.0",
     "react-dom": "^0.14.2",
-    "react-gemini-scrollbar": "^2.0.1",
-    "sanitize-html": "^1.0.0"
+    "react-gemini-scrollbar": "^2.0.1"
   },
   "devDependencies": {
     "babel": "^5.8.23",
     "babel-core": "^5.8.25",
     "babel-loader": "^5.3.2",
     "catw": "^1.0.1",
+    "css-raw-loader": "^0.1.1",
     "http-server": "^0.8.4",
     "json-loader": "^0.5.3",
     "parallelshell": "^1.2.0",
     "rimraf": "^2.4.3",
     "source-map-loader": "^0.1.5",
-    "uglifycss": "0.0.15"
+    "webpack": "^1.12.6"
   }
 }
diff --git a/src/Avatar.js b/src/Avatar.js
deleted file mode 100644
index afc5e9dd6d..0000000000
--- a/src/Avatar.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-
-module.exports = {
-    avatarUrlForMember: function(member, width, height, resizeMethod) {
-        var url = member.getAvatarUrl(
-            MatrixClientPeg.get().getHomeserverUrl(),
-            width,
-            height,
-            resizeMethod
-        );
-        if (!url) {
-            // member can be null here currently since on invites, the JS SDK
-            // does not have enough info to build a RoomMember object for
-            // the inviter.
-            url = this.defaultAvatarUrlForString(member ? member.userId : '');
-        }
-        return url;
-    },
-
-    defaultAvatarUrlForString: function(s) {
-        var total = 0;
-        for (var i = 0; i < s.length; ++i) {
-            total += s.charCodeAt(i);
-        }
-        switch (total % 3) {
-            case 0:
-                return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9QjNbxSKP4eagAFnTseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAABSwCRWJw31gAAAAASUVORK5CYII=";
-            case 1:
-                return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9chOaxgCP4eagAFk9seHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAtKMCks/JG8MAAAAASUVORK5CYII=";
-            case 2:
-                return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrszQENADAIACB9YzNayQCP4eagADldseHFErFYLBaLxWKxWCwWi8Vi8cX4CzAAyiACeHwPiu4AAAAASUVORK5CYII=";
-        }
-    }
-}
-
diff --git a/src/ContextualMenu.js b/src/ContextualMenu.js
deleted file mode 100644
index 3327aa9486..0000000000
--- a/src/ContextualMenu.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-// Shamelessly ripped off Modal.js.  There's probably a better way
-// of doing reusable widgets like dialog boxes & menus where we go and
-// pass in a custom control as the actual body.
-
-module.exports = {
-    ContextualMenuContainerId: "mx_ContextualMenu_Container",
-
-    getOrCreateContainer: function() {
-        var container = document.getElementById(this.ContextualMenuContainerId);
-
-        if (!container) {
-            container = document.createElement("div");
-            container.id = this.ContextualMenuContainerId;
-            document.body.appendChild(container);
-        }
-
-        return container;
-    },
-
-    createMenu: function (Element, props) {
-        var self = this;
-
-        var closeMenu = function() {
-            React.unmountComponentAtNode(self.getOrCreateContainer());
-
-            if (props && props.onFinished) props.onFinished.apply(null, arguments);
-        };
-
-        var position = {
-            top: props.top - 20,
-        };
-
-        var chevron = null;
-        if (props.left) {
-            chevron = <img className="mx_ContextualMenu_chevron_left" src="img/chevron-left.png" width="9" height="16" />
-            position.left = props.left + 8;
-        } else {
-            chevron = <img className="mx_ContextualMenu_chevron_right" src="img/chevron-right.png" width="9" height="16" />
-            position.right = props.right + 8;
-        }
-
-        var className = 'mx_ContextualMenu_wrapper';
-
-        // FIXME: If a menu uses getDefaultProps it clobbers the onFinished
-        // property set here so you can't close the menu from a button click!
-        var menu = (
-            <div className={className}>
-                <div className="mx_ContextualMenu" style={position}>
-                    {chevron}
-                    <Element {...props} onFinished={closeMenu}/>
-                </div>
-                <div className="mx_ContextualMenu_background" onClick={closeMenu}></div>
-            </div>
-        );
-
-        ReactDOM.render(menu, this.getOrCreateContainer());
-
-        return {close: closeMenu};
-    },
-};
diff --git a/src/DateUtils.js b/src/DateUtils.js
deleted file mode 100644
index fe363586ab..0000000000
--- a/src/DateUtils.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-
-module.exports = {
-    formatDate: function(date) {
-        // date.toLocaleTimeString is completely system dependent.
-        // just go 24h for now
-        function pad(n) {
-            return (n < 10 ? '0' : '') + n;
-        }
-
-        var now = new Date();
-        if (date.toDateString() === now.toDateString()) {
-            return pad(date.getHours()) + ':' + pad(date.getMinutes());
-        }
-        else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
-            return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
-        }
-        else if (now.getFullYear() === date.getFullYear()) {
-            return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
-        }
-        else {
-            return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes());
-        }
-    }
-}
-
diff --git a/src/Resend.js b/src/Resend.js
deleted file mode 100644
index 5d7d7815ce..0000000000
--- a/src/Resend.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var dis = require('matrix-react-sdk/lib/dispatcher');
-
-module.exports = {
-    resend: function(event) {
-        MatrixClientPeg.get().resendEvent(
-            event, MatrixClientPeg.get().getRoom(event.getRoomId())
-        ).done(function() {
-            dis.dispatch({
-                action: 'message_sent',
-                event: event
-            });
-        }, function() {
-            dis.dispatch({
-                action: 'message_send_failed',
-                event: event
-            });
-        });
-        dis.dispatch({
-            action: 'message_resend_started',
-            event: event
-        });
-    },
-};
\ No newline at end of file
diff --git a/src/modules/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
similarity index 93%
rename from src/modules/VectorConferenceHandler.js
rename to src/VectorConferenceHandler.js
index 637e34f943..7628e0f5e1 100644
--- a/src/modules/VectorConferenceHandler.js
+++ b/src/VectorConferenceHandler.js
@@ -85,15 +85,15 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
 };
 
 /**
- * Check if this room member is in fact a conference bot.
- * @param {RoomMember} The room member to check
+ * Check if this user ID is in fact a conference bot.
+ * @param {string} userId The user ID to check.
  * @return {boolean} True if it is a conference bot.
  */
-module.exports.isConferenceUser = function(roomMember) {
-    if (roomMember.userId.indexOf("@" + USER_PREFIX) !== 0) {
+module.exports.isConferenceUser = function(userId) {
+    if (userId.indexOf("@" + USER_PREFIX) !== 0) {
         return false;
     }
-    var base64part = roomMember.userId.split(":")[0].substring(1 + USER_PREFIX.length);
+    var base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length);
     if (base64part) {
         var decoded = new Buffer(base64part, "base64").toString();
         // ! $STUFF : $STUFF
diff --git a/src/component-index.js b/src/component-index.js
new file mode 100644
index 0000000000..e6ff997e5a
--- /dev/null
+++ b/src/component-index.js
@@ -0,0 +1,47 @@
+/*
+Copyright 2015 OpenMarket Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+ * THIS FILE IS AUTO-GENERATED
+ * You can edit it you like, but your changes will be overwritten,
+ * so you'd just be trying to swim upstream like a salmon.
+ * You are not a salmon.
+ */
+
+module.exports.components = require('matrix-react-sdk/lib/component-index').components;
+
+module.exports.components['structures.BottomLeftMenu'] = require('./components/structures/BottomLeftMenu');
+module.exports.components['structures.CompatibilityPage'] = require('./components/structures/CompatibilityPage');
+module.exports.components['structures.LeftPanel'] = require('./components/structures/LeftPanel');
+module.exports.components['structures.RightPanel'] = require('./components/structures/RightPanel');
+module.exports.components['structures.RoomDirectory'] = require('./components/structures/RoomDirectory');
+module.exports.components['structures.RoomSubList'] = require('./components/structures/RoomSubList');
+module.exports.components['structures.ViewSource'] = require('./components/structures/ViewSource');
+module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
+module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
+module.exports.components['views.globals.MatrixToolbar'] = require('./components/views/globals/MatrixToolbar');
+module.exports.components['views.login.CustomServerDialog'] = require('./components/views/login/VectorCustomServerDialog');
+module.exports.components['views.login.LoginFooter'] = require('./components/views/login/VectorLoginFooter');
+module.exports.components['views.login.LoginHeader'] = require('./components/views/login/VectorLoginHeader');
+module.exports.components['views.messages.DateSeparator'] = require('./components/views/messages/DateSeparator');
+module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp');
+module.exports.components['views.messages.SenderProfile'] = require('./components/views/messages/SenderProfile');
+module.exports.components['views.rooms.BottomLeftMenuTile'] = require('./components/views/rooms/BottomLeftMenuTile');
+module.exports.components['views.rooms.MessageContextMenu'] = require('./components/views/rooms/MessageContextMenu');
+module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView');
+module.exports.components['views.rooms.RoomDropTarget'] = require('./components/views/rooms/RoomDropTarget');
+module.exports.components['views.rooms.RoomTooltip'] = require('./components/views/rooms/RoomTooltip');
+module.exports.components['views.rooms.SearchBar'] = require('./components/views/rooms/SearchBar');
diff --git a/src/skins/vector/views/molecules/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js
similarity index 92%
rename from src/skins/vector/views/molecules/BottomLeftMenu.js
rename to src/components/structures/BottomLeftMenu.js
index 2020d29d84..f942efd56b 100644
--- a/src/skins/vector/views/molecules/BottomLeftMenu.js
+++ b/src/components/structures/BottomLeftMenu.js
@@ -40,13 +40,13 @@ module.exports = React.createClass({
             return <div className="mx_RoomTile_name">{name}</div>
         }
         else if (this.state.hover) {
-            var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
+            var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
             return <RoomTooltip name={name}/>;
         }
     },
 
     render: function() {
-        var BottomLeftMenuTile = sdk.getComponent('molecules.BottomLeftMenuTile');
+        var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile');
         return (
             <div className="mx_BottomLeftMenu">
                 <div className="mx_BottomLeftMenu_options">
diff --git a/src/skins/vector/views/pages/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js
similarity index 100%
rename from src/skins/vector/views/pages/CompatibilityPage.js
rename to src/components/structures/CompatibilityPage.js
diff --git a/src/skins/vector/views/organisms/LeftPanel.js b/src/components/structures/LeftPanel.js
similarity index 81%
rename from src/skins/vector/views/organisms/LeftPanel.js
rename to src/components/structures/LeftPanel.js
index 96d48e0e15..aaab8084d4 100644
--- a/src/skins/vector/views/organisms/LeftPanel.js
+++ b/src/components/structures/LeftPanel.js
@@ -22,6 +22,7 @@ var HTML5Backend = require('react-dnd-html5-backend');
 var sdk = require('matrix-react-sdk')
 var dis = require('matrix-react-sdk/lib/dispatcher');
 
+var VectorConferenceHandler = require('../../VectorConferenceHandler');
 var CallHandler = require("matrix-react-sdk/lib/CallHandler");
 
 var LeftPanel = React.createClass({
@@ -84,9 +85,9 @@ var LeftPanel = React.createClass({
     },
 
     render: function() {
-        var RoomList = sdk.getComponent('organisms.RoomList');
-        var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu');
-        var IncomingCallBox = sdk.getComponent('molecules.voip.IncomingCallBox');
+        var RoomList = sdk.getComponent('rooms.RoomList');
+        var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
+        var IncomingCallBox = sdk.getComponent('voip.IncomingCallBox');
 
         var collapseButton;
         var classes = "mx_LeftPanel";
@@ -100,8 +101,12 @@ var LeftPanel = React.createClass({
 
         var callPreview;
         if (this.state.showCallElement) {
-            var CallView = sdk.getComponent('molecules.voip.CallView');
-            callPreview = <CallView className="mx_LeftPanel_callView" onClick={this.onCallViewClick} />
+            var CallView = sdk.getComponent('voip.CallView');
+            callPreview = (
+                <CallView
+                    className="mx_LeftPanel_callView" onClick={this.onCallViewClick}
+                    ConferenceHandler={VectorConferenceHandler} />
+            );
         }
 
         return (
@@ -109,7 +114,10 @@ var LeftPanel = React.createClass({
                 { collapseButton }
                 <IncomingCallBox />
                 { callPreview }
-                <RoomList selectedRoom={this.props.selectedRoom} collapsed={this.props.collapsed}/>
+                <RoomList
+                    selectedRoom={this.props.selectedRoom}
+                    collapsed={this.props.collapsed}
+                    ConferenceHandler={VectorConferenceHandler} />
                 <BottomLeftMenu collapsed={this.props.collapsed}/>
             </aside>
         );
diff --git a/src/skins/vector/views/organisms/RightPanel.js b/src/components/structures/RightPanel.js
similarity index 90%
rename from src/skins/vector/views/organisms/RightPanel.js
rename to src/components/structures/RightPanel.js
index feebcfeba5..f4f0a8f332 100644
--- a/src/skins/vector/views/organisms/RightPanel.js
+++ b/src/components/structures/RightPanel.js
@@ -68,6 +68,11 @@ module.exports = React.createClass({
         if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
             this.forceUpdate();
         }
+        else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId &&
+                member.userId === this.state.member.userId) {
+            // refresh the member info (e.g. new power level)
+            this.forceUpdate();
+        }
     },
 
     onAction: function(payload) {
@@ -94,7 +99,7 @@ module.exports = React.createClass({
     },
 
     render: function() {
-        var MemberList = sdk.getComponent('organisms.MemberList');
+        var MemberList = sdk.getComponent('rooms.MemberList');
         var buttonGroup;
         var panel;
 
@@ -122,12 +127,12 @@ module.exports = React.createClass({
             buttonGroup =
                     <div className="mx_RightPanel_headerButtonGroup">
                         <div className="mx_RightPanel_headerButton" onClick={ this.onMemberListButtonClick }>
-                            <img src="img/members.png" width="17" height="22" title="Members" alt="Members"/>
+                            <img src="img/members.svg" width="17" height="22" title="Members" alt="Members"/>
                             { membersBadge }
                             { membersHighlight }
                         </div>
                         <div className="mx_RightPanel_headerButton mx_RightPanel_filebutton">
-                            <img src="img/files.png" width="17" height="22" title="Files" alt="Files"/>
+                            <img src="img/files.svg" width="17" height="22" title="Files" alt="Files"/>
                             { filesHighlight }
                         </div>
                     </div>;
@@ -137,7 +142,7 @@ module.exports = React.createClass({
                     panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
                 }
                 else if(this.state.phase == this.Phase.MemberInfo) {
-                    var MemberInfo = sdk.getComponent('molecules.MemberInfo');
+                    var MemberInfo = sdk.getComponent('rooms.MemberInfo');
                     panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId} />
                 }
             }
diff --git a/src/skins/vector/views/organisms/RoomDirectory.js b/src/components/structures/RoomDirectory.js
similarity index 94%
rename from src/skins/vector/views/organisms/RoomDirectory.js
rename to src/components/structures/RoomDirectory.js
index 28135bfeb4..c1a29779ca 100644
--- a/src/skins/vector/views/organisms/RoomDirectory.js
+++ b/src/components/structures/RoomDirectory.js
@@ -40,7 +40,7 @@ module.exports = React.createClass({
             if (err) {
                 self.setState({ loading: false });                
                 console.error("Failed to get publicRooms: %s", JSON.stringify(err));
-                var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                 Modal.createDialog(ErrorDialog, {
                     title: "Failed to get public room list",
                     description: err.message
@@ -67,7 +67,7 @@ module.exports = React.createClass({
             });
         }, function(err) {
             console.error("Failed to join room: %s", JSON.stringify(err));
-            var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
             Modal.createDialog(ErrorDialog, {
                 title: "Failed to join room",
                 description: err.message
@@ -119,7 +119,7 @@ module.exports = React.createClass({
 
     render: function() {
         if (this.state.loading) {
-            var Loader = sdk.getComponent("atoms.Spinner");            
+            var Loader = sdk.getComponent("elements.Spinner");            
             return (
                 <div className="mx_RoomDirectory">
                     <Loader />
@@ -127,7 +127,7 @@ module.exports = React.createClass({
             );
         }
 
-        var RoomHeader = sdk.getComponent('molecules.RoomHeader');
+        var RoomHeader = sdk.getComponent('rooms.RoomHeader');
         return (
             <div className="mx_RoomDirectory">
                 <RoomHeader simpleHeader="Public Rooms" />
diff --git a/src/skins/vector/views/organisms/RoomSubList.js b/src/components/structures/RoomSubList.js
similarity index 92%
rename from src/skins/vector/views/organisms/RoomSubList.js
rename to src/components/structures/RoomSubList.js
index 35210b499a..f7efb4254f 100644
--- a/src/skins/vector/views/organisms/RoomSubList.js
+++ b/src/components/structures/RoomSubList.js
@@ -20,6 +20,7 @@ var React = require('react');
 var DropTarget = require('react-dnd').DropTarget;
 var sdk = require('matrix-react-sdk')
 var dis = require('matrix-react-sdk/lib/dispatcher');
+var UnreadStatus = require('matrix-react-sdk/lib/UnreadStatus');
 
 // turn this on for drop & drag console debugging galore
 var debug = false;
@@ -88,10 +89,20 @@ var RoomSubList = React.createClass({
     },
 
     tsOfNewestEvent: function(room) {
-        if (room.timeline.length) {
-            return room.timeline[room.timeline.length - 1].getTs();
+        for (var i = room.timeline.length - 1; i >= 0; --i) {
+            var ev = room.timeline[i];
+            // logic copied from RoomList.js for when we do/don't highlight
+            if (UnreadStatus.eventTriggersUnreadCount(ev)) {
+                return ev.getTs();
+            }
         }
-        else {
+
+        // we might only have events that don't trigger the unread indicator,
+        // in which case use the oldest event even if normally it wouldn't count.
+        // This is better than just assuming the last event was forever ago.
+        if (room.timeline.length) {
+            return room.timeline[0].getTs();
+        } else {
             return Number.MAX_SAFE_INTEGER;
         }
     },
@@ -215,7 +226,7 @@ var RoomSubList = React.createClass({
 
     makeRoomTiles: function() {
         var self = this;
-        var RoomTile = sdk.getComponent("molecules.RoomTile");
+        var RoomTile = sdk.getComponent("rooms.RoomTile");
         return this.state.sortedList.map(function(room) {
             var selected = room.roomId == self.props.selectedRoom;
             // XXX: is it evil to pass in self as a prop to RoomTile?
@@ -235,7 +246,7 @@ var RoomSubList = React.createClass({
 
     render: function() {
         var connectDropTarget = this.props.connectDropTarget;
-        var RoomDropTarget = sdk.getComponent('molecules.RoomDropTarget');
+        var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
 
         var label = this.props.collapsed ? null : this.props.label;
 
@@ -265,7 +276,7 @@ var RoomSubList = React.createClass({
             return connectDropTarget(
                 <div>
                     <h2 onClick={ this.onClick } className="mx_RoomSubList_label">{ this.props.collapsed ? '' : this.props.label }
-                        <img className="mx_RoomSubList_chevron" src={ this.state.hidden ? "img/list-open.png" : "img/list-close.png" } width="10" height="10"/>
+                        <img className="mx_RoomSubList_chevron" src={ this.state.hidden ? "img/list-open.svg" : "img/list-close.svg" } width="10" height="10"/>
                     </h2>
                     { subList }
                 </div>
diff --git a/src/skins/vector/views/organisms/ViewSource.js b/src/components/structures/ViewSource.js
similarity index 100%
rename from src/skins/vector/views/organisms/ViewSource.js
rename to src/components/structures/ViewSource.js
diff --git a/src/skins/vector/views/atoms/ImageView.js b/src/components/views/elements/ImageView.js
similarity index 91%
rename from src/skins/vector/views/atoms/ImageView.js
rename to src/components/views/elements/ImageView.js
index a842f7c855..741524be86 100644
--- a/src/skins/vector/views/atoms/ImageView.js
+++ b/src/components/views/elements/ImageView.js
@@ -20,7 +20,7 @@ var React = require('react');
 
 var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
 
-var DateUtils = require('../../../../DateUtils');
+var DateUtils = require('matrix-react-sdk/lib/DateUtils');
 var filesize = require('filesize');
 
 module.exports = React.createClass({
@@ -30,7 +30,8 @@ module.exports = React.createClass({
         onFinished: React.PropTypes.func.isRequired
     },
 
-    // XXX: keyboard shortcuts for managing dialogs should be done by the modal dialog base class somehow, surely...
+    // XXX: keyboard shortcuts for managing dialogs should be done by the modal
+    // dialog base class somehow, surely...
     componentDidMount: function() {
         document.addEventListener("keydown", this.onKeyDown);
     },
@@ -54,7 +55,7 @@ module.exports = React.createClass({
         ).done(function() {
             if (self.props.onFinished) self.props.onFinished();
         }, function(e) {
-            var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
             // display error message stating you couldn't delete this.
             var code = e.errcode || e.statusCode;
             Modal.createDialog(ErrorDialog, {
@@ -104,6 +105,11 @@ module.exports = React.createClass({
             res = ", " + style.width + "x" + style.height + "px";
         }
 
+        var size;
+        if (this.props.mxEvent.getContent().info && this.props.mxEvent.getContent().info.size) {
+            size = filesize(this.props.mxEvent.getContent().info.size);
+        }
+
         return (
             <div className="mx_ImageView">
                 <div className="mx_ImageView_lhs">
@@ -123,7 +129,7 @@ module.exports = React.createClass({
                             <a className="mx_ImageView_link" href={ this.props.src } target="_blank">
                                 <div className="mx_ImageView_download">
                                         Download this file<br/>
-                                        <span className="mx_ImageView_size">({ filesize(this.props.mxEvent.getContent().info.size) }{ res })</span>
+                                         <span className="mx_ImageView_size">{ size } { res }</span>
                                 </div>
                             </a>
                             <div className="mx_ImageView_button">
diff --git a/src/skins/vector/views/atoms/Spinner.js b/src/components/views/elements/Spinner.js
similarity index 100%
rename from src/skins/vector/views/atoms/Spinner.js
rename to src/components/views/elements/Spinner.js
diff --git a/src/skins/vector/views/molecules/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js
similarity index 83%
rename from src/skins/vector/views/molecules/MatrixToolbar.js
rename to src/components/views/globals/MatrixToolbar.js
index 5b613f563d..7b953c4a84 100644
--- a/src/skins/vector/views/molecules/MatrixToolbar.js
+++ b/src/components/views/globals/MatrixToolbar.js
@@ -17,30 +17,28 @@ limitations under the License.
 'use strict';
 
 var React = require('react');
-
+var Notifier = require("matrix-react-sdk/lib/Notifier");
 var sdk = require('matrix-react-sdk')
 
 module.exports = React.createClass({
     displayName: 'MatrixToolbar',
 
     hideToolbar: function() {
-        var Notifier = sdk.getComponent('organisms.Notifier');
         Notifier.setToolbarHidden(true);
     },
 
     onClick: function() {
-        var Notifier = sdk.getComponent('organisms.Notifier');
         Notifier.setEnabled(true);
     },
 
     render: function() {
         return (
             <div className="mx_MatrixToolbar">
-                <img className="mx_MatrixToolbar_warning" src="img/warning.png" width="28" height="28" alt="/!\"/>
+                <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
                 <div>
                     You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a>
                 </div>
-                <div className="mx_MatrixToolbar_close"><img src="img/cancel-black2.png" width="23" height="23" onClick={ this.hideToolbar } /></div>
+                <div className="mx_MatrixToolbar_close"><img src="img/cancel.svg" width="18" height="18" onClick={ this.hideToolbar } /></div>
             </div>
         );
     }
diff --git a/src/skins/vector/views/organisms/ErrorDialog.js b/src/components/views/login/VectorCustomServerDialog.js
similarity index 53%
rename from src/skins/vector/views/organisms/ErrorDialog.js
rename to src/components/views/login/VectorCustomServerDialog.js
index 992ea05054..22c188cc7a 100644
--- a/src/skins/vector/views/organisms/ErrorDialog.js
+++ b/src/components/views/login/VectorCustomServerDialog.js
@@ -14,38 +14,37 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-'use strict';
-
-/*
- * Usage:
- * Modal.createDialog(ErrorDialog, {
- *   title: "some text", (default: "Error")
- *   description: "some more text",
- *   button: "Button Text",
- *   onClose: someFunction,
- *   focus: true|false (default: true)
- * });
- */
-
-var React = require('react');
-var ErrorDialogController = require('matrix-react-sdk/lib/controllers/organisms/ErrorDialog')
+var React = require("react");
 
 module.exports = React.createClass({
-    displayName: 'ErrorDialog',
-    mixins: [ErrorDialogController],
+    displayName: 'VectorCustomServerDialog',
+    statics: {
+        replaces: 'CustomServerDialog',
+    },
 
     render: function() {
         return (
             <div className="mx_ErrorDialog">
                 <div className="mx_ErrorDialogTitle">
-                    {this.props.title}
+                    Custom Server Options
                 </div>
                 <div className="mx_Dialog_content">
-                    {this.props.description}
+                    <span>
+                        You can use the custom server options to log into other Matrix
+                        servers by specifying a different Home server URL.
+                        <br/>
+                        This allows you to use Vector with an existing Matrix account on
+                        a different Home server.
+                        <br/>
+                        <br/>
+                        You can also set a custom Identity server but this will affect
+                        people&#39;s ability to find you if you use a server in a group other
+                        than the main Matrix.org group.
+                    </span>
                 </div>
                 <div className="mx_Dialog_buttons">
-                    <button onClick={this.props.onFinished} autoFocus={this.props.focus}>
-                        {this.props.button}
+                    <button onClick={this.props.onFinished} autoFocus={true}>
+                        Dismiss
                     </button>
                 </div>
             </div>
diff --git a/src/skins/vector/views/molecules/EventAsTextTile.js b/src/components/views/login/VectorLoginFooter.js
similarity index 60%
rename from src/skins/vector/views/molecules/EventAsTextTile.js
rename to src/components/views/login/VectorLoginFooter.js
index ec644a4ec4..2b2f1ae827 100644
--- a/src/skins/vector/views/molecules/EventAsTextTile.js
+++ b/src/components/views/login/VectorLoginFooter.js
@@ -18,26 +18,20 @@ limitations under the License.
 
 var React = require('react');
 
-var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
-
 module.exports = React.createClass({
-    displayName: 'EventAsTextTile',
-
+    displayName: 'VectorLoginFooter',
     statics: {
-        needsSenderProfile: function() {
-            return false;
-        }
+        replaces: 'LoginFooter',
     },
 
     render: function() {
-        var text = TextForEvent.textForEvent(this.props.mxEvent);
-        if (text == null || text.length == 0) return null;
-
         return (
-            <div className="mx_EventAsTextTile">
-                {TextForEvent.textForEvent(this.props.mxEvent)}
+            <div className="mx_Login_links">
+                <a href="https://medium.com/@Vector">blog</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
+                <a href="https://twitter.com/@VectorCo">twitter</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
+                <a href="https://github.com/vector-im/vector-web">github</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
+                <a href="https://matrix.org">powered by Matrix</a>
             </div>
         );
-    },
+    }
 });
-
diff --git a/src/skins/vector/views/atoms/voip/VideoFeed.js b/src/components/views/login/VectorLoginHeader.js
similarity index 75%
rename from src/skins/vector/views/atoms/voip/VideoFeed.js
rename to src/components/views/login/VectorLoginHeader.js
index 9cf28d1ba4..61b04267fc 100644
--- a/src/skins/vector/views/atoms/voip/VideoFeed.js
+++ b/src/components/views/login/VectorLoginHeader.js
@@ -19,13 +19,16 @@ limitations under the License.
 var React = require('react');
 
 module.exports = React.createClass({
-    displayName: 'VideoFeed',
+    displayName: 'VectorLoginHeader',
+    statics: {
+        replaces: 'LoginHeader',
+    },
 
     render: function() {
         return (
-            <video>
-            </video>
+            <div className="mx_Login_logo">
+                <img src="img/logo.png" width="249" height="78" alt="vector"/>
+            </div>
         );
-    },
+    }
 });
-
diff --git a/src/skins/vector/views/molecules/DateSeparator.js b/src/components/views/messages/DateSeparator.js
similarity index 100%
rename from src/skins/vector/views/molecules/DateSeparator.js
rename to src/components/views/messages/DateSeparator.js
diff --git a/src/skins/vector/views/atoms/MessageTimestamp.js b/src/components/views/messages/MessageTimestamp.js
similarity index 93%
rename from src/skins/vector/views/atoms/MessageTimestamp.js
rename to src/components/views/messages/MessageTimestamp.js
index 5795e55657..b4b7546e50 100644
--- a/src/skins/vector/views/atoms/MessageTimestamp.js
+++ b/src/components/views/messages/MessageTimestamp.js
@@ -17,7 +17,7 @@ limitations under the License.
 'use strict';
 
 var React = require('react');
-var DateUtils = require('../../../../DateUtils');
+var DateUtils = require('matrix-react-sdk/lib/DateUtils');
 
 module.exports = React.createClass({
     displayName: 'MessageTimestamp',
diff --git a/src/skins/vector/views/molecules/MEmoteTile.js b/src/components/views/messages/SenderProfile.js
similarity index 72%
rename from src/skins/vector/views/molecules/MEmoteTile.js
rename to src/components/views/messages/SenderProfile.js
index de2d93650b..ef0173d975 100644
--- a/src/skins/vector/views/molecules/MEmoteTile.js
+++ b/src/components/views/messages/SenderProfile.js
@@ -18,19 +18,20 @@ limitations under the License.
 
 var React = require('react');
 
-var MEmoteTileController = require('matrix-react-sdk/lib/controllers/molecules/MEmoteTile')
-
 module.exports = React.createClass({
-    displayName: 'MEmoteTile',
-    mixins: [MEmoteTileController],
+    displayName: 'SenderProfile',
 
     render: function() {
         var mxEvent = this.props.mxEvent;
-        var content = mxEvent.getContent();
         var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
+
+        var msgtype = mxEvent.getContent().msgtype;
+        if (msgtype && msgtype == 'm.emote') {
+            name = ''; // emote message must include the name so don't duplicate it
+        }
         return (
-            <span ref="content" className="mx_MEmoteTile mx_MessageTile_content">
-                * {name} {content.body}
+            <span className="mx_SenderProfile">
+                {name} { this.props.aux }
             </span>
         );
     },
diff --git a/src/skins/vector/views/molecules/BottomLeftMenuTile.js b/src/components/views/rooms/BottomLeftMenuTile.js
similarity index 95%
rename from src/skins/vector/views/molecules/BottomLeftMenuTile.js
rename to src/components/views/rooms/BottomLeftMenuTile.js
index 8c28058d10..2535490fea 100644
--- a/src/skins/vector/views/molecules/BottomLeftMenuTile.js
+++ b/src/components/views/rooms/BottomLeftMenuTile.js
@@ -41,7 +41,7 @@ module.exports = React.createClass({
             label = <div className="mx_RoomTile_name">{ this.props.label }</div>;
         }
         else if (this.state.hover) {
-            var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
+            var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
             label = <RoomTooltip bottom={ true } label={ this.props.label }/>;
         }
 
diff --git a/src/skins/vector/views/molecules/MessageContextMenu.js b/src/components/views/rooms/MessageContextMenu.js
similarity index 76%
rename from src/skins/vector/views/molecules/MessageContextMenu.js
rename to src/components/views/rooms/MessageContextMenu.js
index b36d4ccbbf..4950cd8825 100644
--- a/src/skins/vector/views/molecules/MessageContextMenu.js
+++ b/src/components/views/rooms/MessageContextMenu.js
@@ -22,7 +22,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
 var dis = require('matrix-react-sdk/lib/dispatcher');
 var sdk = require('matrix-react-sdk')
 var Modal = require('matrix-react-sdk/lib/Modal');
-var Resend = require("../../../../Resend");
+var Resend = require("matrix-react-sdk/lib/Resend");
 
 module.exports = React.createClass({
     displayName: 'MessageContextMenu',
@@ -33,7 +33,7 @@ module.exports = React.createClass({
     },
 
     onViewSourceClick: function() {
-        var ViewSource = sdk.getComponent('organisms.ViewSource');
+        var ViewSource = sdk.getComponent('structures.ViewSource');
         Modal.createDialog(ViewSource, {
             mxEvent: this.props.mxEvent
         });
@@ -46,7 +46,7 @@ module.exports = React.createClass({
         ).done(function() {
             // message should disappear by itself
         }, function(e) {
-            var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
             // display error message stating you couldn't delete this.
             var code = e.errcode || e.statusCode;
             Modal.createDialog(ErrorDialog, {
@@ -57,25 +57,42 @@ module.exports = React.createClass({
         if (this.props.onFinished) this.props.onFinished();
     },
 
+    onCancelSendClick: function() {
+        Resend.removeFromQueue(this.props.mxEvent);
+        if (this.props.onFinished) this.props.onFinished();
+    },
+
     render: function() {
+        var eventStatus = this.props.mxEvent.status;
         var resendButton;
         var viewSourceButton;
         var redactButton;
+        var cancelButton;
 
-        if (this.props.mxEvent.status == 'not_sent') {
+        if (eventStatus === 'not_sent') {
             resendButton = (
                 <div className="mx_ContextualMenu_field" onClick={this.onResendClick}>
                     Resend
                 </div>
             );
         }
-        else {
+
+        if (!eventStatus) { // sent
             redactButton = (
                 <div className="mx_ContextualMenu_field" onClick={this.onRedactClick}>
                     Redact
                 </div>
             );
         }
+
+        if (eventStatus === "queued" || eventStatus === "not_sent") {
+            cancelButton = (
+                <div className="mx_ContextualMenu_field" onClick={this.onCancelSendClick}>
+                    Cancel Sending
+                </div>
+            );
+        }
+
         viewSourceButton = (
             <div className="mx_ContextualMenu_field" onClick={this.onViewSourceClick}>
                 View Source
@@ -86,6 +103,7 @@ module.exports = React.createClass({
             <div>
                 {resendButton}
                 {redactButton}
+                {cancelButton}
                 {viewSourceButton}
             </div>
         );
diff --git a/src/skins/vector/views/molecules/RoomTile.js b/src/components/views/rooms/RoomDNDView.js
similarity index 64%
rename from src/skins/vector/views/molecules/RoomTile.js
rename to src/components/views/rooms/RoomDNDView.js
index 31dead455c..9b01629df2 100644
--- a/src/skins/vector/views/molecules/RoomTile.js
+++ b/src/components/views/rooms/RoomDNDView.js
@@ -19,13 +19,11 @@ limitations under the License.
 var React = require('react');
 var DragSource = require('react-dnd').DragSource;
 var DropTarget = require('react-dnd').DropTarget;
-var classNames = require('classnames');
-
-var RoomTileController = require('matrix-react-sdk/lib/controllers/molecules/RoomTile')
 
+var dis = require("matrix-react-sdk/lib/dispatcher");
 var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-
-var sdk = require('matrix-react-sdk')
+var sdk = require('matrix-react-sdk');
+var RoomTile = require('matrix-react-sdk/lib/components/views/rooms/RoomTile');
 
 /**
  * Specifies the drag source contract.
@@ -69,7 +67,7 @@ var roomTileSource = {
 
         if (monitor.didDrop() && item.targetList.props.editable) {
             // if we moved lists, remove the old tag
-            if (item.targetList !== item.originalList) {
+            if (item.targetList !== item.originalList && item.originalList.props.tagName) {
                 // commented out attempts to set a spinner on our target component as component is actually
                 // the original source component being dragged, not our target.  To fix we just need to
                 // move all of this to endDrop in the target instead.  FIXME later.
@@ -78,7 +76,7 @@ var roomTileSource = {
                 MatrixClientPeg.get().deleteRoomTag(item.room.roomId, item.originalList.props.tagName).finally(function() {
                     //component.state.set({ spinner: component.state.spinner-- });
                 }).fail(function(err) {
-                    var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+                    var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                     Modal.createDialog(ErrorDialog, {
                         title: "Failed to remove tag " + item.originalList.props.tagName + " from room",
                         description: err.toString()
@@ -97,7 +95,7 @@ var roomTileSource = {
                 MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() {
                     //component.state.set({ spinner: component.state.spinner-- });
                 }).fail(function(err) {
-                    var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
+                    var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                     Modal.createDialog(ErrorDialog, {
                         title: "Failed to add tag " + item.targetList.props.tagName + " to room",
                         description: err.toString()
@@ -183,117 +181,6 @@ var roomTileTarget = {
     },
 };
 
-var RoomTile = React.createClass({
-    displayName: 'RoomTile',
-    mixins: [RoomTileController],
-
-    propTypes: {
-        connectDragSource: React.PropTypes.func.isRequired,
-        connectDropTarget: React.PropTypes.func.isRequired,
-        isDragging: React.PropTypes.bool.isRequired,
-        room: React.PropTypes.object.isRequired,
-        collapsed: React.PropTypes.bool.isRequired,
-        selected: React.PropTypes.bool.isRequired,
-        unread: React.PropTypes.bool.isRequired,
-        highlight: React.PropTypes.bool.isRequired,
-        isInvite: React.PropTypes.bool.isRequired,
-        roomSubList: React.PropTypes.object.isRequired,
-    },
-
-    getInitialState: function() {
-        return( { hover : false });
-    },
-
-    onMouseEnter: function() {
-        this.setState( { hover : true });
-    },
-
-    onMouseLeave: function() {
-        this.setState( { hover : false });
-    },
-
-    render: function() {
-        // if (this.props.clientOffset) {
-        //     //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y);
-        // }
-
-/*
-        if (this.props.room._dragging) {
-            var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget");
-            return <RoomDropTarget placeholder={true}/>;
-        }
-*/        
-
-        var myUserId = MatrixClientPeg.get().credentials.userId;
-        var me = this.props.room.currentState.members[myUserId];
-        var classes = classNames({
-            'mx_RoomTile': true,
-            'mx_RoomTile_selected': this.props.selected,
-            'mx_RoomTile_unread': this.props.unread,
-            'mx_RoomTile_highlight': this.props.highlight,
-            'mx_RoomTile_invited': (me && me.membership == 'invite'),
-        });
-
-        var name;
-        if (this.props.isInvite) {
-            name = this.props.room.getMember(myUserId).events.member.getSender();
-        }
-        else {
-            // XXX: We should never display raw room IDs, but sometimes the room name js sdk gives is undefined
-            name = this.props.room.name || this.props.room.roomId;
-        }
-
-        name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
-        var badge;
-        if (this.props.highlight) {
-            badge = <div className="mx_RoomTile_badge"/>;
-        }
-        /*
-        if (this.props.highlight) {
-            badge = <div className="mx_RoomTile_badge">!</div>;
-        }
-        else if (this.props.unread) {
-            badge = <div className="mx_RoomTile_badge">1</div>;
-        }
-        var nameCell;
-        if (badge) {
-            nameCell = <div className="mx_RoomTile_nameBadge"><div className="mx_RoomTile_name">{name}</div><div className="mx_RoomTile_badgeCell">{badge}</div></div>;
-        }
-        else {
-            nameCell = <div className="mx_RoomTile_name">{name}</div>;
-        }
-        */
-
-        var label;
-        if (!this.props.collapsed) {
-            var className = 'mx_RoomTile_name' + (this.props.isInvite ? ' mx_RoomTile_invite' : '');
-            label = <div className={ className }>{name}</div>;
-        }
-        else if (this.state.hover) {
-            var RoomTooltip = sdk.getComponent("molecules.RoomTooltip");
-            label = <RoomTooltip room={this.props.room}/>;
-        }
-
-        var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
-
-        // These props are injected by React DnD,
-        // as defined by your `collect` function above:
-        var isDragging = this.props.isDragging;
-        var connectDragSource = this.props.connectDragSource;
-        var connectDropTarget = this.props.connectDropTarget;
-
-        return connectDragSource(connectDropTarget(
-            <div className={classes} onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
-                <div className="mx_RoomTile_avatar">
-                    <RoomAvatar room={this.props.room} width="24" height="24" />
-                    { badge }
-                </div>
-                { label }
-            </div>
-        ));
-    }
-});
-
 // Export the wrapped version, inlining the 'collect' functions
 // to more closely resemble the ES7
 module.exports = 
@@ -313,4 +200,6 @@ DragSource('RoomTile', roomTileSource, function(connect, monitor) {
         // You can ask the monitor about the current drag state:
         isDragging: monitor.isDragging()
     };
-})(RoomTile));
\ No newline at end of file
+})(RoomTile));
+
+module.exports.replaces = 'RoomTile';
diff --git a/src/skins/vector/views/molecules/RoomDropTarget.js b/src/components/views/rooms/RoomDropTarget.js
similarity index 100%
rename from src/skins/vector/views/molecules/RoomDropTarget.js
rename to src/components/views/rooms/RoomDropTarget.js
diff --git a/src/skins/vector/views/molecules/RoomTooltip.js b/src/components/views/rooms/RoomTooltip.js
similarity index 100%
rename from src/skins/vector/views/molecules/RoomTooltip.js
rename to src/components/views/rooms/RoomTooltip.js
diff --git a/src/skins/vector/views/molecules/SearchBar.js b/src/components/views/rooms/SearchBar.js
similarity index 57%
rename from src/skins/vector/views/molecules/SearchBar.js
rename to src/components/views/rooms/SearchBar.js
index 585b9a6d4a..e66f123609 100644
--- a/src/skins/vector/views/molecules/SearchBar.js
+++ b/src/components/views/rooms/SearchBar.js
@@ -19,6 +19,7 @@ limitations under the License.
 var React = require('react');
 var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
 var sdk = require('matrix-react-sdk');
+var classNames = require('classnames');
 
 module.exports = React.createClass({
     displayName: 'SearchBar',
@@ -39,17 +40,29 @@ module.exports = React.createClass({
 
     onSearchChange: function(e) {
         if (e.keyCode === 13) { // on enter...
-            this.props.onSearch(this.refs.search_term.value, this.state.scope);
+            this.onSearch();
         }
+        if (e.keyCode === 27) { // escape...
+            this.props.onCancelClick();
+        }
+    },
+
+    onSearch: function() {
+        this.props.onSearch(this.refs.search_term.value, this.state.scope);
     },
     
     render: function() {
+        var searchButtonClasses = classNames({ mx_SearchBar_searchButton : true, mx_SearchBar_searching: this.props.searchInProgress });
+        var thisRoomClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'Room' });
+        var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' });
+
         return (
             <div className="mx_SearchBar">
                 <input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/>
-                <div className={"mx_SearchBar_button" + (this.state.scope !== 'Room' ? " mx_SearchBar_unselected" : "")} onClick={this.onThisRoomClick}>This Room</div>
-                <div className={"mx_SearchBar_button" + (this.state.scope !== 'All' ? " mx_SearchBar_unselected" : "")} onClick={this.onAllRoomsClick}>All Rooms</div>
-                <img className="mx_SearchBar_cancel" src="img/cancel-black.png" width="18" height="18" onClick={this.props.onCancelClick} />
+                <div className={ searchButtonClasses } onClick={this.onSearch}><img src="img/search-button.svg" width="37" height="37" alt="Search"/></div>
+                <div className={ thisRoomClasses } onClick={this.onThisRoomClick}>This Room</div>
+                <div className={ allRoomsClasses } onClick={this.onAllRoomsClick}>All Rooms</div>
+                <img className="mx_SearchBar_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.props.onCancelClick} />
             </div>
         );
     }
diff --git a/src/controllers/molecules/voip/CallView.js b/src/controllers/molecules/voip/CallView.js
deleted file mode 100644
index ab71214882..0000000000
--- a/src/controllers/molecules/voip/CallView.js
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-var dis = require("matrix-react-sdk/lib/dispatcher");
-var CallHandler = require("matrix-react-sdk/lib/CallHandler");
-var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
-
-var VectorConferenceHandler = require('../../../modules/VectorConferenceHandler');
-
-/*
- * State vars:
- * this.state.call = MatrixCall|null
- *
- * Props:
- * this.props.room = Room (JS SDK)
- *
- * Internal state:
- * this._trackedRoom = (either from props.room or programatically set)
- */
-
-module.exports = {
-
-    componentDidMount: function() {
-        this.dispatcherRef = dis.register(this.onAction);
-        this._trackedRoom = null;
-        if (this.props.room) {
-            this._trackedRoom = this.props.room;
-            this.showCall(this._trackedRoom.roomId);
-        }
-        else {
-            var call = CallHandler.getAnyActiveCall();
-            if (call) {
-                console.log(
-                    "Global CallView is now tracking active call in room %s",
-                    call.roomId
-                );
-                this._trackedRoom = MatrixClientPeg.get().getRoom(call.roomId);
-                this.showCall(call.roomId);
-            }
-        }
-    },
-
-    componentWillUnmount: function() {
-        dis.unregister(this.dispatcherRef);
-    },
-
-    onAction: function(payload) {
-        // don't filter out payloads for room IDs other than props.room because
-        // we may be interested in the conf 1:1 room
-        if (payload.action !== 'call_state' || !payload.room_id) {
-            return;
-        }
-        this.showCall(payload.room_id);
-    },
-
-    showCall: function(roomId) {
-        var call = (
-            CallHandler.getCallForRoom(roomId) ||
-            VectorConferenceHandler.getConferenceCallForRoom(roomId)
-        );
-        if (call) {
-            call.setLocalVideoElement(this.getVideoView().getLocalVideoElement());
-            call.setRemoteVideoElement(this.getVideoView().getRemoteVideoElement());
-            // give a separate element for audio stream playback - both for voice calls
-            // and for the voice stream of screen captures
-            call.setRemoteAudioElement(this.getVideoView().getRemoteAudioElement());
-        }
-        if (call && call.type === "video" && call.state !== 'ended') {
-            // if this call is a conf call, don't display local video as the
-            // conference will have us in it
-            this.getVideoView().getLocalVideoElement().style.display = (
-                call.confUserId ? "none" : "initial"
-            );
-            this.getVideoView().getRemoteVideoElement().style.display = "initial";
-        }
-        else {
-            this.getVideoView().getLocalVideoElement().style.display = "none";
-            this.getVideoView().getRemoteVideoElement().style.display = "none";
-            dis.dispatch({action: 'video_fullscreen', fullscreen: false});
-        }
-    }
-};
-
diff --git a/src/controllers/organisms/RoomList.js b/src/controllers/organisms/RoomList.js
deleted file mode 100644
index 37d4a4e4a2..0000000000
--- a/src/controllers/organisms/RoomList.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require("react");
-var ReactDOM = require("react-dom");
-
-var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
-var RoomListSorter = require("matrix-react-sdk/lib/RoomListSorter");
-var dis = require("matrix-react-sdk/lib/dispatcher");
-
-var sdk = require('matrix-react-sdk');
-var VectorConferenceHandler = require("../../modules/VectorConferenceHandler");
-
-var HIDE_CONFERENCE_CHANS = true;
-
-module.exports = {
-    getInitialState: function() {
-        return {
-            activityMap: null,
-            lists: {},
-        }
-    },
-
-    componentWillMount: function() {
-        var cli = MatrixClientPeg.get();
-        cli.on("Room", this.onRoom);
-        cli.on("Room.timeline", this.onRoomTimeline);
-        cli.on("Room.name", this.onRoomName);
-        cli.on("Room.tags", this.onRoomTags);
-        cli.on("RoomState.events", this.onRoomStateEvents);
-        cli.on("RoomMember.name", this.onRoomMemberName);
-
-        var s = this.getRoomLists();
-        s.activityMap = {};
-        this.setState(s);
-    },
-
-    componentDidMount: function() {
-        this.dispatcherRef = dis.register(this.onAction);
-    },
-
-    onAction: function(payload) {
-        switch (payload.action) {
-            case 'view_tooltip':
-                this.tooltip = payload.tooltip;
-                this._repositionTooltip();
-                if (this.tooltip) this.tooltip.style.display = 'block';
-                break
-        }
-    },
-
-    componentWillUnmount: function() {
-        dis.unregister(this.dispatcherRef);
-        if (MatrixClientPeg.get()) {
-            MatrixClientPeg.get().removeListener("Room", this.onRoom);
-            MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
-            MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
-            MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
-        }
-    },
-
-    componentWillReceiveProps: function(newProps) {
-        this.state.activityMap[newProps.selectedRoom] = undefined;
-        this.setState({
-            activityMap: this.state.activityMap
-        });
-    },
-
-    onRoom: function(room) {
-        this.refreshRoomList();
-    },
-
-    onRoomTimeline: function(ev, room, toStartOfTimeline) {
-        if (toStartOfTimeline) return;
-
-        var newState = this.getRoomLists();
-        if (
-            room.roomId != this.props.selectedRoom &&
-            ev.getSender() != MatrixClientPeg.get().credentials.userId)
-        {
-            var hl = 1;
-
-            var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
-            if (actions && actions.tweaks && actions.tweaks.highlight) {
-                hl = 2;
-            }
-            // obviously this won't deep copy but this shouldn't be necessary
-            var amap = this.state.activityMap;
-            amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl);
-
-            newState.activityMap = amap;
-        }
-        this.setState(newState);
-    },
-
-    onRoomName: function(room) {
-        this.refreshRoomList();
-    },
-
-    onRoomTags: function(event, room) {
-        this.refreshRoomList();        
-    },
-
-    onRoomStateEvents: function(ev, state) {
-        setTimeout(this.refreshRoomList, 0);
-    },
-
-    onRoomMemberName: function(ev, member) {
-        setTimeout(this.refreshRoomList, 0);
-    },
-
-    refreshRoomList: function() {
-        // TODO: rather than bluntly regenerating and re-sorting everything
-        // every time we see any kind of room change from the JS SDK
-        // we could do incremental updates on our copy of the state
-        // based on the room which has actually changed.  This would stop
-        // us re-rendering all the sublists every time anything changes anywhere
-        // in the state of the client.
-        this.setState(this.getRoomLists());
-    },
-
-    getRoomLists: function() {
-        var s = { lists: {} };
-
-        s.lists["m.invite"] = [];
-        s.lists["m.favourite"] = [];
-        s.lists["m.recent"] = [];
-        s.lists["m.lowpriority"] = [];
-        s.lists["m.archived"] = [];
-
-        MatrixClientPeg.get().getRooms().forEach(function(room) {
-            var me = room.getMember(MatrixClientPeg.get().credentials.userId);
-
-            if (me && me.membership == "invite") {
-                s.lists["m.invite"].push(room);
-            }
-            else {
-                var shouldShowRoom =  (
-                    me && (me.membership == "join")
-                );
-
-                // hiding conf rooms only ever toggles shouldShowRoom to false
-                if (shouldShowRoom && HIDE_CONFERENCE_CHANS) {
-                    // we want to hide the 1:1 conf<->user room and not the group chat
-                    var joinedMembers = room.getJoinedMembers();
-                    if (joinedMembers.length === 2) {
-                        var otherMember = joinedMembers.filter(function(m) {
-                            return m.userId !== me.userId
-                        })[0];
-                        if (VectorConferenceHandler.isConferenceUser(otherMember)) {
-                            // console.log("Hiding conference 1:1 room %s", room.roomId);
-                            shouldShowRoom = false;
-                        }
-                    }
-                }
-
-                if (shouldShowRoom) {
-                    var tagNames = Object.keys(room.tags);
-                    if (tagNames.length) {
-                        for (var i = 0; i < tagNames.length; i++) {
-                            var tagName = tagNames[i];
-                            s.lists[tagName] = s.lists[tagName] || [];
-                            s.lists[tagNames[i]].push(room);
-                        }
-                    }
-                    else {
-                        s.lists["m.recent"].push(room); 
-                    }
-                }
-            }
-        });
-
-        //console.log("calculated new roomLists; m.recent = " + s.lists["m.recent"]);
-
-        // we actually apply the sorting to this when receiving the prop in RoomSubLists.
-
-        return s;
-    },
-
-    _repositionTooltip: function(e) {
-        if (this.tooltip && this.tooltip.parentElement) {
-            var scroll = ReactDOM.findDOMNode(this);
-            this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.children[2].scrollTop) + "px"; 
-        }
-    },
-};
diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
deleted file mode 100644
index e603198a72..0000000000
--- a/src/controllers/organisms/RoomView.js
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-var Matrix = require("matrix-js-sdk");
-var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
-var React = require("react");
-var ReactDOM = require("react-dom");
-var q = require("q");
-var ContentMessages = require("matrix-react-sdk/lib//ContentMessages");
-var WhoIsTyping = require("matrix-react-sdk/lib/WhoIsTyping");
-var Modal = require("matrix-react-sdk/lib/Modal");
-var sdk = require('matrix-react-sdk/lib/index');
-var CallHandler = require('matrix-react-sdk/lib/CallHandler');
-var VectorConferenceHandler = require('../../modules/VectorConferenceHandler');
-var Resend = require("../../Resend");
-
-var dis = require("matrix-react-sdk/lib/dispatcher");
-
-var PAGINATE_SIZE = 20;
-var INITIAL_SIZE = 20;
-
-module.exports = {
-    getInitialState: function() {
-        var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null;
-        return {
-            room: room,
-            messageCap: INITIAL_SIZE,
-            editingRoomSettings: false,
-            uploadingRoomSettings: false,
-            numUnreadMessages: 0,
-            draggingFile: false,
-            searching: false,
-            searchResults: null,
-            syncState: MatrixClientPeg.get().getSyncState(),
-            hasUnsentMessages: this._hasUnsentMessages(room)
-        }
-    },
-
-    componentWillMount: function() {
-        this.dispatcherRef = dis.register(this.onAction);
-        MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
-        MatrixClientPeg.get().on("Room.name", this.onRoomName);
-        MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
-        MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
-        MatrixClientPeg.get().on("sync", this.onSyncStateChange);
-        this.atBottom = true;
-    },
-
-    componentWillUnmount: function() {
-        if (this.refs.messagePanel) {
-            var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
-            messagePanel.removeEventListener('drop', this.onDrop);
-            messagePanel.removeEventListener('dragover', this.onDragOver);
-            messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd);
-            messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd);
-        }
-        dis.unregister(this.dispatcherRef);
-        if (MatrixClientPeg.get()) {
-            MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
-            MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
-            MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
-            MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
-            MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
-        }
-    },
-
-    onAction: function(payload) {
-        switch (payload.action) {
-            case 'message_send_failed':
-            case 'message_sent':
-                this.setState({
-                    hasUnsentMessages: this._hasUnsentMessages(this.state.room)
-                });
-            case 'message_resend_started':
-                this.setState({
-                    room: MatrixClientPeg.get().getRoom(this.props.roomId)
-                });
-                this.forceUpdate();
-                break;
-            case 'notifier_enabled':
-                this.forceUpdate();
-                break;
-            case 'call_state':
-                if (CallHandler.getCallForRoom(this.props.roomId)) {
-                    // Call state has changed so we may be loading video elements
-                    // which will obscure the message log.
-                    // scroll to bottom
-                    var scrollNode = this._getScrollNode();
-                    if (scrollNode) {
-                        scrollNode.scrollTop = scrollNode.scrollHeight;
-                    }
-                }
-
-                // possibly remove the conf call notification if we're now in
-                // the conf
-                this._updateConfCallNotification();
-                break;
-        }
-    },
-
-    _getScrollNode: function() {
-        var panel = ReactDOM.findDOMNode(this.refs.messagePanel);
-        if (!panel) return null;
-
-        if (panel.classList.contains('gm-prevented')) {
-            return panel;
-        } else {
-            return panel.children[2]; // XXX: Fragile!
-        }
-    },
-
-    onSyncStateChange: function(state) {
-        this.setState({
-            syncState: state
-        });
-    },
-
-    // MatrixRoom still showing the messages from the old room?
-    // Set the key to the room_id. Sadly you can no longer get at
-    // the key from inside the component, or we'd check this in code.
-    /*componentWillReceiveProps: function(props) {
-    },*/
-
-    onRoomTimeline: function(ev, room, toStartOfTimeline) {
-        if (!this.isMounted()) return;
-
-        // ignore anything that comes in whilst paginating: we get one
-        // event for each new matrix event so this would cause a huge
-        // number of UI updates. Just update the UI when the paginate
-        // call returns.
-        if (this.state.paginating) return;
-
-        // no point handling anything while we're waiting for the join to finish:
-        // we'll only be showing a spinner.
-        if (this.state.joining) return;
-        if (room.roomId != this.props.roomId) return;
-
-        var scrollNode = this._getScrollNode();
-        if (scrollNode) {
-            this.atBottom = (
-                scrollNode.scrollHeight - scrollNode.scrollTop <=
-                (scrollNode.clientHeight + 150) // 150?
-            );
-        }
-
-        var currentUnread = this.state.numUnreadMessages;
-        if (!toStartOfTimeline &&
-                (ev.getSender() !== MatrixClientPeg.get().credentials.userId)) {
-            // update unread count when scrolled up
-            if (this.atBottom) {
-                currentUnread = 0;
-            }
-            else {
-                currentUnread += 1;
-            }
-        }
-
-
-        this.setState({
-            room: MatrixClientPeg.get().getRoom(this.props.roomId),
-            numUnreadMessages: currentUnread
-        });
-
-        if (toStartOfTimeline && !this.state.paginating) {
-            this.fillSpace();
-        }
-    },
-
-    onRoomName: function(room) {
-        if (room.roomId == this.props.roomId) {
-            this.setState({
-                room: room
-            });
-        }
-    },
-
-    onRoomMemberTyping: function(ev, member) {
-        this.forceUpdate();
-    },
-
-    onRoomStateMember: function(ev, state, member) {
-        if (member.roomId !== this.props.roomId ||
-                member.userId !== VectorConferenceHandler.getConferenceUserIdForRoom(member.roomId)) {
-            return;
-        }
-        this._updateConfCallNotification();
-    },
-
-    _hasUnsentMessages: function(room) {
-        return this._getUnsentMessages(room).length > 0;
-    },
-
-    _getUnsentMessages: function(room) {
-        if (!room) { return []; }
-        // TODO: It would be nice if the JS SDK provided nicer constant-time
-        // constructs rather than O(N) (N=num msgs) on this.
-        return room.timeline.filter(function(ev) {
-            return ev.status === Matrix.EventStatus.NOT_SENT;
-        });
-    },
-
-    _updateConfCallNotification: function() {
-        var room = MatrixClientPeg.get().getRoom(this.props.roomId);
-        if (!room) return;
-        var confMember = room.getMember(
-            VectorConferenceHandler.getConferenceUserIdForRoom(this.props.roomId)
-        );
-
-        if (!confMember) {
-            return;
-        }
-        var confCall = VectorConferenceHandler.getConferenceCallForRoom(confMember.roomId);
-
-        // A conf call notification should be displayed if there is an ongoing
-        // conf call but this cilent isn't a part of it.
-        this.setState({
-            displayConfCallNotification: (
-                (!confCall || confCall.call_state === "ended") &&
-                confMember.membership === "join"
-            )
-        });
-    },
-
-    componentDidMount: function() {
-        if (this.refs.messagePanel) {
-            var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
-
-            messagePanel.addEventListener('drop', this.onDrop);
-            messagePanel.addEventListener('dragover', this.onDragOver);
-            messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd);
-            messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd);
-
-            var messageWrapperScroll = this._getScrollNode();
-
-            messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight;
-
-            this.fillSpace();
-        }
-
-        this._updateConfCallNotification();
-    },
-
-    componentDidUpdate: function() {
-        if (!this.refs.messagePanel) return;
-
-        var messageWrapperScroll = this._getScrollNode();
-
-        if (this.state.paginating && !this.waiting_for_paginate) {
-            var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight;
-            messageWrapperScroll.scrollTop += heightGained;
-            this.oldScrollHeight = undefined;
-            if (!this.fillSpace()) {
-                this.setState({paginating: false});
-            }
-        } else if (this.atBottom) {
-            messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight;
-            if (this.state.numUnreadMessages !== 0) {
-                this.setState({numUnreadMessages: 0});
-            }
-        }
-    },
-
-    fillSpace: function() {
-        if (!this.refs.messagePanel) return;
-        var messageWrapperScroll = this._getScrollNode();
-        if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) {
-            this.setState({paginating: true});
-
-            this.oldScrollHeight = messageWrapperScroll.scrollHeight;
-
-            if (this.state.messageCap < this.state.room.timeline.length) {
-                this.waiting_for_paginate = false;
-                var cap = Math.min(this.state.messageCap + PAGINATE_SIZE, this.state.room.timeline.length);
-                this.setState({messageCap: cap, paginating: true});
-            } else {
-                this.waiting_for_paginate = true;
-                var cap = this.state.messageCap + PAGINATE_SIZE;
-                this.setState({messageCap: cap, paginating: true});
-                var self = this;
-                MatrixClientPeg.get().scrollback(this.state.room, PAGINATE_SIZE).finally(function() {
-                    self.waiting_for_paginate = false;
-                    if (self.isMounted()) {
-                        self.setState({
-                            room: MatrixClientPeg.get().getRoom(self.props.roomId)
-                        });
-                    }
-                    // wait and set paginating to false when the component updates
-                });
-            }
-
-            return true;
-        }
-        return false;
-    },
-
-    onResendAllClick: function() {
-        var eventsToResend = this._getUnsentMessages(this.state.room);
-        eventsToResend.forEach(function(event) {
-            Resend.resend(event);
-        });
-    },
-
-    onJoinButtonClicked: function(ev) {
-        var self = this;
-        MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() {
-            self.setState({
-                joining: false,
-                room: MatrixClientPeg.get().getRoom(self.props.roomId)
-            });
-        }, function(error) {
-            self.setState({
-                joining: false,
-                joinError: error
-            });
-        });
-        this.setState({
-            joining: true
-        });
-    },
-
-    onMessageListScroll: function(ev) {
-        if (this.refs.messagePanel) {
-            var messageWrapperScroll = this._getScrollNode();
-            var wasAtBottom = this.atBottom;
-            this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1;
-            if (this.atBottom && !wasAtBottom) {
-                this.forceUpdate(); // remove unread msg count
-            }
-        }
-        if (!this.state.paginating) this.fillSpace();
-    },
-
-    onDragOver: function(ev) {
-        ev.stopPropagation();
-        ev.preventDefault();
-
-        ev.dataTransfer.dropEffect = 'none';
-
-        var items = ev.dataTransfer.items;
-        if (items.length == 1) {
-            if (items[0].kind == 'file') {
-                this.setState({ draggingFile : true });
-                ev.dataTransfer.dropEffect = 'copy';
-            }
-        }
-    },
-
-    onDrop: function(ev) {
-        ev.stopPropagation();
-        ev.preventDefault();
-        this.setState({ draggingFile : false });
-        var files = ev.dataTransfer.files;
-        if (files.length == 1) {
-            this.uploadFile(files[0]);
-        }
-    },
-
-    onDragLeaveOrEnd: function(ev) {
-        ev.stopPropagation();
-        ev.preventDefault();
-        this.setState({ draggingFile : false });
-    },
-
-    uploadFile: function(file) {
-        this.setState({
-            upload: {
-                fileName: file.name,
-                uploadedBytes: 0,
-                totalBytes: file.size
-            }
-        });
-        var self = this;
-        ContentMessages.sendContentToRoom(
-            file, this.props.roomId, MatrixClientPeg.get()
-        ).progress(function(ev) {
-            //console.log("Upload: "+ev.loaded+" / "+ev.total);
-            self.setState({
-                upload: {
-                    fileName: file.name,
-                    uploadedBytes: ev.loaded,
-                    totalBytes: ev.total
-                }
-            });
-        }).finally(function() {
-            self.setState({
-                upload: undefined
-            });
-        }).done(undefined, function(error) {
-            var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
-            Modal.createDialog(ErrorDialog, {
-                title: "Failed to upload file",
-                description: error.toString()
-            });
-        });
-    },
-
-    getWhoIsTypingString: function() {
-        return WhoIsTyping.whoIsTypingString(this.state.room);
-    },
-
-    onSearch: function(term, scope) {
-        var filter;
-        if (scope === "Room") { // FIXME: should be enum
-            filter = {
-                // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
-                rooms: [
-                    this.props.roomId
-                ]
-            };
-        }
-
-        var self = this;
-        MatrixClientPeg.get().search({
-            body: {
-                search_categories: {
-                    room_events: {
-                        search_term: term,
-                        filter: filter,
-                        order_by: "recent",
-                        event_context: {
-                            before_limit: 1,
-                            after_limit: 1,
-                        }
-                    }
-                }
-            }            
-        }).then(function(data) {
-            self.setState({
-                searchTerm: term,
-                searchResults: data,
-            });
-        }, function(error) {
-            var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
-            Modal.createDialog(ErrorDialog, {
-                title: "Search failed",
-                description: error.toString()
-            });
-        });
-    },
-
-    getEventTiles: function() {
-        var DateSeparator = sdk.getComponent('molecules.DateSeparator');
-
-        var ret = [];
-        var count = 0;
-
-        var EventTile = sdk.getComponent('molecules.EventTile');
-
-        if (this.state.searchResults) {
-            // XXX: this dance is foul, due to the results API not returning sorted results
-            var results = this.state.searchResults.search_categories.room_events.results;
-            var eventIds = Object.keys(results);
-            // XXX: todo: merge overlapping results somehow?
-            // XXX: why doesn't searching on name work?
-            var resultList = eventIds.map(function(key) { return results[key]; }); // .sort(function(a, b) { b.rank - a.rank });
-            for (var i = 0; i < resultList.length; i++) {
-                var ts1 = resultList[i].result.origin_server_ts;
-                ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); //  Rank: {resultList[i].rank}
-                var mxEv = new Matrix.MatrixEvent(resultList[i].result);
-                if (resultList[i].context.events_before[0]) {
-                    var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]);
-                    if (EventTile.haveTileForEvent(mxEv2)) {
-                        ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
-                    }
-                }
-                if (EventTile.haveTileForEvent(mxEv)) {
-                    ret.push(<li key={mxEv.getId() + "+0"}><EventTile mxEvent={mxEv} searchTerm={this.state.searchTerm}/></li>);
-                }
-                if (resultList[i].context.events_after[0]) {
-                    var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]);
-                    if (EventTile.haveTileForEvent(mxEv2)) {
-                        ret.push(<li key={mxEv.getId() + "+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>);
-                    }
-                }
-            }
-            return ret;
-        }
-
-        for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) {
-            var mxEv = this.state.room.timeline[i];
-
-            if (!EventTile.haveTileForEvent(mxEv)) {
-                continue;
-            }
-
-            var continuation = false;
-            var last = false;
-            var dateSeparator = null;
-            if (i == this.state.room.timeline.length - 1) {
-                last = true;
-            }
-            if (i > 0 && count < this.state.messageCap - 1) {
-                if (this.state.room.timeline[i].sender &&
-                    this.state.room.timeline[i - 1].sender &&
-                    (this.state.room.timeline[i].sender.userId ===
-                        this.state.room.timeline[i - 1].sender.userId) &&
-                    (this.state.room.timeline[i].getType() ==
-                        this.state.room.timeline[i - 1].getType())
-                    )
-                {
-                    continuation = true;
-                }
-
-                var ts0 = this.state.room.timeline[i - 1].getTs();
-                var ts1 = this.state.room.timeline[i].getTs();
-                if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) {
-                    dateSeparator = <DateSeparator key={ts1} ts={ts1}/>;
-                    continuation = false;
-                }
-            }
-
-            if (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline
-                var ts1 = this.state.room.timeline[i].getTs();
-                dateSeparator = <li key={ts1}><DateSeparator ts={ts1}/></li>;
-                continuation = false;
-            }
-
-            ret.unshift(
-                <li key={mxEv.getId()}><EventTile mxEvent={mxEv} continuation={continuation} last={last}/></li>
-            );
-            if (dateSeparator) {
-                ret.unshift(dateSeparator);
-            }
-            ++count;
-        }
-        return ret;
-    },
-
-    uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) {
-        var old_name = this.state.room.name;
-
-        var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', '');
-        if (old_topic) {
-            old_topic = old_topic.getContent().topic;
-        } else {
-            old_topic = "";
-        }
-
-        var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', '');
-        if (old_join_rule) {
-            old_join_rule = old_join_rule.getContent().join_rule;
-        } else {
-            old_join_rule = "invite";
-        }
-
-        var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', '');
-        if (old_history_visibility) {
-            old_history_visibility = old_history_visibility.getContent().history_visibility;
-        } else {
-            old_history_visibility = "shared";
-        }
-
-        var deferreds = [];
-
-        if (old_name != new_name && new_name != undefined && new_name) {
-            deferreds.push(
-                MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name)
-            );
-        }
-
-        if (old_topic != new_topic && new_topic != undefined) {
-            deferreds.push(
-                MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic)
-            );
-        }
-
-        if (old_join_rule != new_join_rule && new_join_rule != undefined) {
-            deferreds.push(
-                MatrixClientPeg.get().sendStateEvent(
-                    this.state.room.roomId, "m.room.join_rules", {
-                        join_rule: new_join_rule,
-                    }, ""
-                )
-            );
-        }
-
-        if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) {
-            deferreds.push(
-                MatrixClientPeg.get().sendStateEvent(
-                    this.state.room.roomId, "m.room.history_visibility", {
-                        history_visibility: new_history_visibility,
-                    }, ""
-                )
-            );
-        }
-
-        if (new_power_levels) {
-            deferreds.push(
-                MatrixClientPeg.get().sendStateEvent(
-                    this.state.room.roomId, "m.room.power_levels", new_power_levels, ""
-                )
-            );
-        }
-
-        if (deferreds.length) {
-            var self = this;
-            q.all(deferreds).fail(function(err) {
-                var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
-                Modal.createDialog(ErrorDialog, {
-                    title: "Failed to set state",
-                    description: err.toString()
-                });
-            }).finally(function() {
-                self.setState({
-                    uploadingRoomSettings: false,
-                });
-            });
-        } else {
-            this.setState({
-                editingRoomSettings: false,
-                uploadingRoomSettings: false,
-            });
-        }
-    }
-};
diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js
deleted file mode 100644
index 5f88d59059..0000000000
--- a/src/controllers/templates/Register.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var extend = require('matrix-react-sdk/lib/extend');
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var BaseRegisterController = require('matrix-react-sdk/lib/controllers/templates/Register.js');
-
-var RegisterController = {};
-extend(RegisterController, BaseRegisterController);
-
-RegisterController.onRegistered = function(user_id, access_token) {
-    MatrixClientPeg.replaceUsingAccessToken(
-        this.state.hs_url, this.state.is_url, user_id, access_token
-    );
-
-    this.setState({
-        step: 'profile',
-        busy: true
-    });
-
-    var self = this;
-    var cli = MatrixClientPeg.get();
-    cli.getProfileInfo(cli.credentials.userId).done(function(result) {
-        self.setState({
-            avatarUrl: result.avatar_url,
-            busy: false
-        });
-    },
-    function(err) {
-        console.err(err);
-        self.setState({
-            busy: false
-        });
-    });
-};
-
-RegisterController.onAccountReady = function() {
-    if (this.props.onLoggedIn) {
-        this.props.onLoggedIn();
-    }
-};
-
-module.exports = RegisterController;
diff --git a/src/skins/vector/header b/src/header
similarity index 100%
rename from src/skins/vector/header
rename to src/header
diff --git a/src/skins/vector/css/MOVE_ME/UploadBar.css b/src/skins/vector/css/MOVE_ME/UploadBar.css
new file mode 100644
index 0000000000..bff271a354
--- /dev/null
+++ b/src/skins/vector/css/MOVE_ME/UploadBar.css
@@ -0,0 +1,44 @@
+.mx_UploadBar {
+    position: relative;
+}
+
+.mx_UploadBar_uploadProgressOuter {
+    height: 4px;
+    margin-left: 63px;
+    margin-top: -1px;
+}
+
+.mx_UploadBar_uploadProgressInner {
+    background-color: #76cfa6;
+    height: 4px;
+}
+
+.mx_UploadBar_uploadFilename {
+    margin-top: 5px;
+    margin-left: 65px;
+    opacity: 0.5;
+    color: #4a4a4a;
+}
+
+.mx_UploadBar_uploadIcon {
+    float: left;
+    margin-top: 1px;
+    margin-left: 14px;
+}
+
+.mx_UploadBar_uploadCancel {
+    float: right;
+    margin-top: 5px;
+    margin-right: 10px;
+    position: relative;
+    opacity: 0.6;
+    cursor: pointer;    
+    z-index: 1;
+}
+
+.mx_UploadBar_uploadBytes {
+    float: right;    
+    margin-top: 5px;
+    margin-right: 30px;
+    color: #76cfa6;
+}
diff --git a/src/skins/vector/css/atoms/ImageView.css b/src/skins/vector/css/atoms/ImageView.css
index 22ef343bee..9dd34f804e 100644
--- a/src/skins/vector/css/atoms/ImageView.css
+++ b/src/skins/vector/css/atoms/ImageView.css
@@ -87,13 +87,13 @@ limitations under the License.
 }
 
 .mx_ImageView_name {
-    font-size: 20px;
+    font-size: 18px;
     margin-bottom: 6px;
     pointer-events: all;
 }
 
 .mx_ImageView_metadata {
-    font-size: 16px;
+    font-size: 15px;
     opacity: 0.5;
     pointer-events: all;
 }
@@ -105,13 +105,13 @@ limitations under the License.
     margin-bottom: 6px;
     border-radius: 5px;
     background-color: #454545;
-    font-size: 16px;
+    font-size: 14px;
     padding: 9px;
     border: 1px solid #fff;
 }
 
 .mx_ImageView_size {
-    font-size: 12px;
+    font-size: 11px;
 }
 
 .mx_ImageView_link {
@@ -121,7 +121,7 @@ limitations under the License.
 
 .mx_ImageView_button {
     pointer-events: all;
-    font-size: 16px;
+    font-size: 15px;
     opacity: 0.5;
     margin-top: 18px;
     cursor: pointer;
diff --git a/src/skins/vector/css/atoms/MemberAvatar.css b/src/skins/vector/css/atoms/MemberAvatar.css
index 34ef139364..3da56172d1 100644
--- a/src/skins/vector/css/atoms/MemberAvatar.css
+++ b/src/skins/vector/css/atoms/MemberAvatar.css
@@ -20,11 +20,14 @@ limitations under the License.
 
 .mx_MemberAvatar_initial {
     position: absolute;
+    z-index: 1;
     color: #fff;
     text-align: center;
     speak: none;
+    pointer-events: none;
 }
 
 .mx_MemberAvatar_image {
-    border-radius: 20px;    
+    border-radius: 20px;
+    vertical-align: top;
 }
diff --git a/src/skins/vector/css/atoms/RoomAvatar.css b/src/skins/vector/css/atoms/RoomAvatar.css
index 01425190ee..f005b25176 100644
--- a/src/skins/vector/css/atoms/RoomAvatar.css
+++ b/src/skins/vector/css/atoms/RoomAvatar.css
@@ -15,6 +15,7 @@ limitations under the License.
 */
 
 .mx_RoomAvatar {
+    vertical-align: middle;
 }
 
 .mx_RoomAvatar_initial {
@@ -23,4 +24,5 @@ limitations under the License.
     text-align: center;
     font-weight: normal ! important;
     speak: none;
+    pointer-events: none;
 }
\ No newline at end of file
diff --git a/src/skins/vector/css/common.css b/src/skins/vector/css/common.css
index 121fbca7a4..2b6e02ed37 100644
--- a/src/skins/vector/css/common.css
+++ b/src/skins/vector/css/common.css
@@ -22,8 +22,13 @@ html {
 }
 
 body {
-    font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
-    font-size: 16px;
+    /* Open Sans lacks combining diacritics, so these will fall through
+       to the next font.  Helevetica's diacritics however do not combine
+       nicely with Open Sans (on OSX, at least) and result in a huge
+       horizontal mess.  Arial empirically gets it right, hence prioritising
+       Arial here. */
+    font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
+    font-size: 15px;
     color: #454545;
     border: 0px;
     margin: 0px;
@@ -36,7 +41,7 @@ div.error {
 h2 {
     color: #454545;
     font-weight: 400;
-    font-size: 20px;
+    font-size: 18px;
     margin-top: 16px;
     margin-bottom: 16px;
 }
@@ -47,6 +52,12 @@ a:visited {
     color: #76cfa6;
 }
 
+input[type=text]:focus, textarea:focus, .mx_RoomSettings textarea:focus {
+    border: 1px solid #76CFA6;
+    outline: none;
+    box-shadow: none;
+}
+
 /* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
    Stop the scrollbar view from pushing out the container's overall sizing, which causes
    flexbox to adapt to the new size and cause the view to keep growing.
@@ -99,19 +110,9 @@ a:visited {
     margin: 0 auto;
 }
 
-.mx_Dialog_background {
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    background-color: #000;
-    opacity: 0.2;
-    z-index: 4000;
-}
-
 .mx_Dialog_wrapper {
     position: fixed;
+    z-index: 4000;
     top: 0;
     left: 0;
     width: 100%;
@@ -134,12 +135,22 @@ a:visited {
     text-align: center;
     z-index: 4010;
     font-weight: 300;
-    font-size: 16px;
+    font-size: 15px;
     position: relative;
     border-radius: 8px;
     max-width: 80%;
 }
 
+.mx_Dialog_background {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: #000;
+    opacity: 0.2;
+}
+
 .mx_Dialog_lightbox .mx_Dialog_background {
     opacity: 0.85;
 }
@@ -167,7 +178,7 @@ a:visited {
     height: 36px;
     border-radius: 36px;
     font-weight: 400;
-    font-size: 16px;
+    font-size: 15px;
     color: #fff;
     background-color: #76cfa6;
     margin-left: 8px;
@@ -182,6 +193,6 @@ a:visited {
     padding: 12px;
     border-bottom: 1px solid #a4a4a4;
     font-weight: bold;
-    font-size: 20px;
+    font-size: 18px;
     line-height: 1.4;
 }
diff --git a/src/skins/vector/css/gemini-scrollbar.css b/src/skins/vector/css/gemini-scrollbar.css
deleted file mode 120000
index 4e3c83ba7d..0000000000
--- a/src/skins/vector/css/gemini-scrollbar.css
+++ /dev/null
@@ -1 +0,0 @@
-../../../../node_modules/react-gemini-scrollbar/node_modules/gemini-scrollbar/gemini-scrollbar.css
\ No newline at end of file
diff --git a/src/skins/vector/css/molecules/EventAsTextTile.css b/src/skins/vector/css/molecules/EventAsTextTile.css
index d18fdc809c..da95352296 100644
--- a/src/skins/vector/css/molecules/EventAsTextTile.css
+++ b/src/skins/vector/css/molecules/EventAsTextTile.css
@@ -16,4 +16,5 @@ limitations under the License.
 
 .mx_EventAsTextTile {
     opacity: 0.5;
+    overflow-y: hidden;
 }
diff --git a/src/skins/vector/css/molecules/EventTile.css b/src/skins/vector/css/molecules/EventTile.css
index d99bd4e1b5..e3956bdb2b 100644
--- a/src/skins/vector/css/molecules/EventTile.css
+++ b/src/skins/vector/css/molecules/EventTile.css
@@ -25,8 +25,10 @@ limitations under the License.
     padding-left: 18px;
     padding-right: 12px;
     margin-left: -73px;
-    margin-top: -4px;
+    margin-top: -2px;
     float: left;
+    position: relative;
+    top: 0px;
 }
 
 .mx_EventTile_avatar img {
@@ -41,15 +43,15 @@ limitations under the License.
 .mx_EventTile .mx_SenderProfile {
     color: #454545;
     opacity: 0.5;
-    font-size: 14px;
+    font-size: 13px;
     margin-bottom: 4px;
     display: block;
+    overflow-y: hidden;
 }
 
 .mx_EventTile .mx_MessageTimestamp {
     color: #acacac;
-    font-size: 12px;
-    float: right;
+    font-size: 11px;
 }
 
 .mx_EventTile_line {
@@ -64,8 +66,39 @@ limitations under the License.
 .mx_MessageTile_content {
     display: block;
     margin-right: 100px;
+    overflow-y: hidden;
 }
 
+/* Various markdown overrides */
+
+.mx_MessageTile_content .markdown-body {
+    font-family: inherit ! important;
+    white-space: normal ! important;
+    line-height: inherit ! important;
+    color: inherit;
+    font-size: 15px;
+}
+
+.mx_MessageTile_content .markdown-body h1,
+.mx_MessageTile_content .markdown-body h2,
+.mx_MessageTile_content .markdown-body h3,
+.mx_MessageTile_content .markdown-body h4,
+.mx_MessageTile_content .markdown-body h5,
+.mx_MessageTile_content .markdown-body h6
+{
+    font-family: inherit ! important;
+}
+
+.mx_MessageTile_content .markdown-body a {
+    color: #76cfa6;
+}
+
+.mx_MessageTile_content .markdown-body .hljs {
+    display: inherit ! important;
+}
+
+/* end of overrides */
+
 .mx_MessageTile_searchHighlight {
     background-color: #76cfa6;
     color: #fff;
@@ -78,10 +111,12 @@ limitations under the License.
 }
 
 .mx_EventTile_notSent {
-    color: #ddd;
+    color: #f44;
 }
 
-.mx_EventTile_highlight {
+.mx_EventTile_highlight,
+.mx_EventTile_highlight .markdown-body
+ {
     color: #FF0064;
 }
 
@@ -91,10 +126,18 @@ limitations under the License.
 
 .mx_EventTile_msgOption {
     float: right;
+    text-align: right;
+    z-index: 1;
+    position: relative;
+    width: 90px;
+    margin-right: 10px;
+    margin-top: -6px;
 }
 
 .mx_MessageTimestamp {
+    display: block;
     visibility: hidden;
+    text-align: right;
 }
 
 .mx_EventTile_last .mx_MessageTimestamp {
@@ -107,9 +150,8 @@ limitations under the License.
 
 .mx_EventTile_editButton {
     position: absolute;
-    right: 1px;
-    top: 15px;
-    visibility: hidden;
+    display: inline-block;
+    visibility: hidden; 
 }
 
 .mx_EventTile:hover .mx_EventTile_editButton {
@@ -123,3 +165,21 @@ limitations under the License.
 .mx_EventTile.menu .mx_MessageTimestamp {
     visibility: visible;
 }
+
+.mx_EventTile_readAvatars {
+    position: relative;
+    display: inline-block;
+    width: 14px;
+    height: 14px;
+}
+
+.mx_EventTile_readAvatars .mx_MemberAvatar {
+    position: absolute;
+    display: inline-block;
+}
+
+.mx_EventTile_readAvatarRemainder {
+    color: #acacac;
+    font-size: 11px;
+    position: absolute;
+}
diff --git a/src/skins/vector/css/molecules/MNoticeTile.css b/src/skins/vector/css/molecules/MNoticeTile.css
index 0a0db62ea6..9fe5376a9e 100644
--- a/src/skins/vector/css/molecules/MNoticeTile.css
+++ b/src/skins/vector/css/molecules/MNoticeTile.css
@@ -15,5 +15,6 @@ limitations under the License.
 */
 
 .mx_MNoticeTile {
+    white-space: pre-wrap;
     opacity: 0.6;
 }
diff --git a/src/skins/vector/css/molecules/MemberInfo.css b/src/skins/vector/css/molecules/MemberInfo.css
index 6471a86cb6..1c4ab3856c 100644
--- a/src/skins/vector/css/molecules/MemberInfo.css
+++ b/src/skins/vector/css/molecules/MemberInfo.css
@@ -37,8 +37,10 @@ limitations under the License.
 }
 
 .mx_MemberInfo_profileField {
-    opacity: 0.6;
-    font-size: 14px;
+    font-color: #999999;    
+    font-size: 13px;
+    position: relative;
+    background-color: #fff;
 }
 
 .mx_MemberInfo_buttons {
@@ -49,7 +51,7 @@ limitations under the License.
     cursor: pointer;
     width: 100px;
     text-align: center;
-    font-size: 12px;    
+    font-size: 11px;    
     background-color: #888;
     color: #fff;
     font-weight: bold;
diff --git a/src/skins/vector/css/molecules/MemberTile.css b/src/skins/vector/css/molecules/MemberTile.css
index cfeaeaee0c..874710d9de 100644
--- a/src/skins/vector/css/molecules/MemberTile.css
+++ b/src/skins/vector/css/molecules/MemberTile.css
@@ -25,8 +25,8 @@ limitations under the License.
     display: table-cell;
     padding-left: 3px;
     padding-right: 12px;
-    padding-top: 2px;
-    padding-bottom: 0px;
+    padding-top: 4px;
+    padding-bottom: 4px;
     vertical-align: middle;
     width: 36px;
     height: 36px;
@@ -55,13 +55,13 @@ limitations under the License.
 }
 
 .mx_MemberTile_userId {
-    font-size: 14px;
+    font-size: 13px;
     overflow: hidden;
     text-overflow: ellipsis;
 }
 
 .mx_MemberTile_presence {
-    font-size: 12px;
+    font-size: 11px;
     opacity: 0.5;
 }
 
@@ -98,10 +98,6 @@ limitations under the License.
     opacity: 0.25;
 }
 
-.mx_MemberTile_zalgo {
-    font-family: Helvetica, Arial, Sans-Serif;
-}
-
 .mx_MemberTile:hover .mx_MessageTimestamp {
     display: block;
 }
diff --git a/src/skins/vector/css/molecules/MessageComposer.css b/src/skins/vector/css/molecules/MessageComposer.css
index fbbeef6455..4d5668518d 100644
--- a/src/skins/vector/css/molecules/MessageComposer.css
+++ b/src/skins/vector/css/molecules/MessageComposer.css
@@ -59,7 +59,7 @@ limitations under the License.
     box-shadow: none;
 
     /* needed for FF */
-    font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
+    font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
 }
 
 /* hack for FF as vertical alignment of custom placeholder text is broken */
@@ -72,7 +72,9 @@ limitations under the License.
 }
 
 .mx_MessageComposer_upload,
-.mx_MessageComposer_call {
+.mx_MessageComposer_hangup,
+.mx_MessageComposer_voicecall,
+.mx_MessageComposer_videocall {
     display: table-cell;
     vertical-align: middle;
     padding-left: 10px;
@@ -80,7 +82,12 @@ limitations under the License.
     cursor: pointer;
 }
 
-.mx_MessageComposer_call {
+.mx_MessageComposer_videocall {
+    padding-right: 10px;
+    padding-top: 4px;
+}
+
+.mx_MessageComposer_voicecall {
     padding-right: 10px;
     padding-top: 4px;
 }
diff --git a/src/skins/vector/css/molecules/RoomDropTarget.css b/src/skins/vector/css/molecules/RoomDropTarget.css
index 4eea49e155..2e655c7376 100644
--- a/src/skins/vector/css/molecules/RoomDropTarget.css
+++ b/src/skins/vector/css/molecules/RoomDropTarget.css
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 .mx_RoomDropTarget {
-    font-size: 14px;
+    font-size: 13px;
     margin-left: 10px;
     margin-right: 15px;
     padding-top: 5px;
diff --git a/src/skins/vector/css/molecules/RoomHeader.css b/src/skins/vector/css/molecules/RoomHeader.css
index e033d735cd..b1469e3557 100644
--- a/src/skins/vector/css/molecules/RoomHeader.css
+++ b/src/skins/vector/css/molecules/RoomHeader.css
@@ -23,6 +23,9 @@ limitations under the License.
     height: 83px;
     border-bottom: 1px solid #eeeeee;
 
+    -webkit-align-items: center;
+    align-items: center;
+    
     display: -webkit-box;
     display: -moz-box;
     display: -ms-flexbox;
@@ -31,8 +34,6 @@ limitations under the License.
 }
 
 .mx_RoomHeader_leftRow {
-    height: 48px;
-    margin-top: 18px;
     margin-left: -2px;
 
     -webkit-box-ordinal-group: 1;
@@ -47,7 +48,6 @@ limitations under the License.
 
 .mx_RoomHeader_textButton {
     height: 48px;
-    margin-top: 18px;
     background-color: #76cfa6;
     border-radius: 48px;
     margin-right: 8px;
@@ -72,7 +72,7 @@ limitations under the License.
     }
 
 .mx_RoomHeader_rightRow {
-    margin-top: 32px;
+    margin-top: 4px;
     background-color: #fff;
 
     -webkit-box-ordinal-group: 3;
@@ -84,14 +84,14 @@ limitations under the License.
 
 .mx_RoomHeader_info {
     display: table-cell;
-    height: 48px;
+    /* height: 48px; */
     vertical-align: middle;
 }
 
 .mx_RoomHeader_simpleHeader {
     line-height: 83px;
     color: #454545;
-    font-size: 24px;
+    font-size: 22px;
     font-weight: bold;
     overflow: hidden;
     margin-left: 63px;
@@ -110,11 +110,13 @@ limitations under the License.
     cursor: pointer;
     vertical-align: middle;
     height: 28px;
+    overflow: hidden;
     color: #454545;
     font-weight: bold;
-    font-size: 24px;
+    font-size: 22px;
     padding-left: 19px;
     padding-right: 16px;
+    /* why isn't text-overflow working? */
     text-overflow: ellipsis;
 }
 
@@ -122,6 +124,13 @@ limitations under the License.
     display: inline-block;
 }
 
+.mx_RoomHeader_searchStatus {
+    display: inline-block;
+    font-weight: normal;
+    overflow-y: hidden;
+    opacity: 0.6;
+}
+
 .mx_RoomHeader_settingsButton {
     display: inline-block;
     visibility: hidden;
@@ -130,14 +139,24 @@ limitations under the License.
     left: 4px;
 }
 
-.mx_RoomHeader_name:hover {
+.mx_RoomHeader_leftRow:hover .mx_RoomHeader_name {
     color: #76cfa6;
 }
 
-.mx_RoomHeader_name:hover .mx_RoomHeader_settingsButton {
+.mx_RoomHeader_leftRow:hover .mx_RoomHeader_settingsButton {
     visibility: visible;
 }
 
+.mx_RoomHeader_leaveButton {
+    visibility: hidden;
+    margin-top: -1px;
+}
+
+.mx_RoomHeader_wrapper:hover .mx_RoomHeader_leaveButton {
+    visibility: visible;
+}
+
+
 .mx_RoomHeader_nameEditing {
     padding-left: 8px;
     padding-right: 16px;
@@ -149,7 +168,7 @@ limitations under the License.
     width: 260px;
     border: 1px solid #c7c7c7;
     font-weight: 300;
-    font-size: 14px;
+    font-size: 13px;
     padding: 9px;
 }
 
@@ -160,7 +179,7 @@ limitations under the License.
 .mx_RoomHeader_topic {
     vertical-align: bottom;
     float: left;
-    max-height: 38px;
+    max-height: 42px;
     color: #454545;
     font-weight: 300;  
     padding-left: 19px;
diff --git a/src/skins/vector/css/molecules/RoomSettings.css b/src/skins/vector/css/molecules/RoomSettings.css
index a669c5b2d1..dabdd55f09 100644
--- a/src/skins/vector/css/molecules/RoomSettings.css
+++ b/src/skins/vector/css/molecules/RoomSettings.css
@@ -39,7 +39,7 @@ limitations under the License.
     border-radius: 3px;
     border: 1px solid #c7c7c7;
     font-weight: 300;
-    font-size: 14px;
+    font-size: 13px;
     padding: 9px;
     margin-top: 6px;
 }
@@ -59,7 +59,7 @@ limitations under the License.
     height: 36px;
     border-radius: 36px;
     font-weight: 400;
-    font-size: 16px;
+    font-size: 15px;
     color: #fff;
     background-color: #76cfa6;
     width: auto;
diff --git a/src/skins/vector/css/molecules/RoomTile.css b/src/skins/vector/css/molecules/RoomTile.css
index 37d2e1b62e..ef907d25a1 100644
--- a/src/skins/vector/css/molecules/RoomTile.css
+++ b/src/skins/vector/css/molecules/RoomTile.css
@@ -18,19 +18,19 @@ limitations under the License.
     cursor: pointer;
     /* This fixes wrapping of long room names, but breaks drag & drop previews */
     /* display: table-row; */
-    font-size: 14px;
+    font-size: 13px;
 }
 
 .mx_RoomTile_avatar {
     display: table-cell;
     padding-right: 8px;
-    padding-top: 4px;
-    padding-bottom: 2px;
+    padding-top: 6px;
+    padding-bottom: 6px;
     padding-left: 18px;
-    vertical-align: middle;
     width: 24px;
     height: 24px;
     position: relative;
+    vertical-align: middle;
 }
 
 .mx_RoomTile_avatar img {
@@ -48,7 +48,8 @@ limitations under the License.
 }
 
 .mx_RoomTile_invite {
-    color: rgba(69, 69, 69, 0.5);
+/*    color: rgba(69, 69, 69, 0.5);
+*/
 }
 
 .collapsed .mx_RoomTile_name {
@@ -112,6 +113,10 @@ limitations under the License.
     color: #76cfa6 ! important;
 }
 
+.mx_RoomTile_highlight .mx_RoomTile_name {
+    color: #ff0064 ! important;
+}
+
 .mx_RoomTile.mx_RoomTile_selected .mx_RoomTile_name {
     background: url('img/selected.png');
     background-repeat: no-repeat;
diff --git a/src/skins/vector/css/molecules/SearchBar.css b/src/skins/vector/css/molecules/SearchBar.css
index b116674bc5..3698c852a9 100644
--- a/src/skins/vector/css/molecules/SearchBar.css
+++ b/src/skins/vector/css/molecules/SearchBar.css
@@ -21,24 +21,43 @@ limitations under the License.
     align-items: center;
 }
 
-.mx_SearchBar input {
+.mx_SearchBar_input {
     display: inline block;
-    border-radius: 3px;
+    border-radius: 3px 0px 0px 3px;
     border: 1px solid #f0f0f0;
-    font-size: 16px;
+    font-size: 15px;
     padding: 9px;
     padding-left: 11px;
-    margin-right: 17px;
     width: auto;
     flex: 1 1 0;
 }
 
+.mx_SearchBar_searchButton {
+    cursor: pointer;
+    margin-right: 10px;
+    width: 37px;
+    height: 37px;
+    border-radius: 0px 3px 3px 0px;
+    background-color: #76CFA6;
+}
+
+@keyframes pulsate {
+    0% { opacity: 1.0; }
+    50% { opacity: 0.25; }
+    100% { opacity: 1.0; }
+}
+
+.mx_SearchBar_searching img {
+    animation: pulsate 0.75s ease-out;
+    animation-iteration-count: infinite; 
+}
+
 .mx_SearchBar_button {
     display: inline;
     border: 0px;
     border-radius: 36px;
     font-weight: 400;
-    font-size: 16px;
+    font-size: 15px;
     color: #fff;
     background-color: #76cfa6;
     width: auto;
diff --git a/src/skins/vector/css/molecules/SenderProfile.css b/src/skins/vector/css/molecules/SenderProfile.css
index 45db913e68..fd88ee27f7 100644
--- a/src/skins/vector/css/molecules/SenderProfile.css
+++ b/src/skins/vector/css/molecules/SenderProfile.css
@@ -13,8 +13,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
-
-.mx_SenderProfile_zalgo {
-    font-family: Helvetica, Arial, Sans-Serif;
-    display: table-row ! important;
-}
diff --git a/src/skins/vector/css/molecules/ServerConfig.css b/src/skins/vector/css/molecules/ServerConfig.css
index db0572c841..58bdcfdf94 100644
--- a/src/skins/vector/css/molecules/ServerConfig.css
+++ b/src/skins/vector/css/molecules/ServerConfig.css
@@ -25,7 +25,7 @@ limitations under the License.
 
 .mx_ServerConfig_help:link {
     opacity: 0.8;
-    font-size: 14px;
+    font-size: 13px;
     font-weight: 300;
     color: #4a4a4a;
 }
\ No newline at end of file
diff --git a/src/skins/vector/css/organisms/CreateRoom.css b/src/skins/vector/css/organisms/CreateRoom.css
index 578c79e6ea..feb8bbff36 100644
--- a/src/skins/vector/css/organisms/CreateRoom.css
+++ b/src/skins/vector/css/organisms/CreateRoom.css
@@ -26,7 +26,7 @@ limitations under the License.
     border-radius: 3px;
     border: 1px solid #c7c7c7;
     font-weight: 300;
-    font-size: 14px;
+    font-size: 13px;
     padding: 9px;
     margin-top: 6px;
 }
diff --git a/src/skins/vector/css/organisms/LeftPanel.css b/src/skins/vector/css/organisms/LeftPanel.css
index 37de0f0e54..7451d1677f 100644
--- a/src/skins/vector/css/organisms/LeftPanel.css
+++ b/src/skins/vector/css/organisms/LeftPanel.css
@@ -69,11 +69,12 @@ limitations under the License.
 }
 
 .mx_LeftPanel .mx_BottomLeftMenu .mx_BottomLeftMenu_options {
-    margin-top: 17px;
+    margin-top: 15px;
     width: 100%;
 }
 
 .mx_LeftPanel .mx_BottomLeftMenu img {
     border-radius: 0px;
     background-color: transparent;
+    vertical-align: middle;
 }
\ No newline at end of file
diff --git a/src/skins/vector/css/organisms/MemberList.css b/src/skins/vector/css/organisms/MemberList.css
index df699e6490..ce936d2c85 100644
--- a/src/skins/vector/css/organisms/MemberList.css
+++ b/src/skins/vector/css/organisms/MemberList.css
@@ -45,13 +45,13 @@ limitations under the License.
 }
 
 .mx_MemberList_invite {
-    font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;
+    font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
     border-radius: 3px;
     border: 1px solid #f0f0f0;
     padding: 9px;
     color: #454545;
     margin-left: 3px;
-    font-size: 16px;
+    font-size: 15px;
     margin-bottom: 8px;
     width: 180px;
 }
@@ -69,7 +69,7 @@ limitations under the License.
     text-transform: uppercase;
     color: #3d3b39;
     font-weight: 600;
-    font-size: 14px;
+    font-size: 13px;
     padding-left: 3px;
     padding-right: 12px;
     margin-top: 8px;
diff --git a/src/skins/vector/css/organisms/RightPanel.css b/src/skins/vector/css/organisms/RightPanel.css
index bf473a4489..645a626314 100644
--- a/src/skins/vector/css/organisms/RightPanel.css
+++ b/src/skins/vector/css/organisms/RightPanel.css
@@ -66,7 +66,7 @@ limitations under the License.
 
 .mx_RightPanel_headerButton_badge {
     position: absolute;
-    top: 5px;
+    top: 4px;
     left: 28px;
     font-size: 12px;    
     background-color: #76cfa6;
@@ -75,7 +75,7 @@ limitations under the License.
     border-radius: 20px;
     padding-left: 4px;
     padding-right: 4px;
-    padding-top: 2px;
+    padding-top: 0px;
 }
 
 .mx_RightPanel .mx_MemberList,
diff --git a/src/skins/vector/css/organisms/RoomDirectory.css b/src/skins/vector/css/organisms/RoomDirectory.css
index f53f055657..61fcfa6e3b 100644
--- a/src/skins/vector/css/organisms/RoomDirectory.css
+++ b/src/skins/vector/css/organisms/RoomDirectory.css
@@ -50,7 +50,7 @@ limitations under the License.
     border-radius: 3px;
     border: 1px solid #c7c7c7;
     font-weight: 300;
-    font-size: 14px;
+    font-size: 13px;
     padding: 9px;
     margin-top: 12px;
     margin-bottom: 12px;
@@ -70,7 +70,7 @@ limitations under the License.
 
 .mx_RoomDirectory_table th {
     font-weight: 400;
-    font-size: 12px;
+    font-size: 11px;
 }
 
 .mx_RoomDirectory_table tbody {
@@ -79,7 +79,6 @@ limitations under the License.
 
 .mx_RoomDirectory_table td {
     font-weight: 300;
-    font-size: 16px;
     overflow-x: hidden;
     text-overflow: ellipsis;
 }
@@ -90,7 +89,7 @@ limitations under the License.
 
 .mx_RoomDirectory_table .mx_RoomDirectory_topic {
     font-weight: 400;
-    font-size: 12px;
+    font-size: 11px;
 }
 
 .mx_RoomDirectory_table td,
diff --git a/src/skins/vector/css/organisms/RoomList.css b/src/skins/vector/css/organisms/RoomList.css
index 7f5e2272cd..bb81686c38 100644
--- a/src/skins/vector/css/organisms/RoomList.css
+++ b/src/skins/vector/css/organisms/RoomList.css
@@ -25,3 +25,10 @@ limitations under the License.
     padding-left: 12px;
     padding-right: 12px;
 }
+
+/* Evil hacky override until Chrome fixes drop and drag table cells
+   and we can correctly fix horizontal wrapping in the sidebar again */
+.mx_RoomList_scrollbar .gm-scroll-view {
+    overflow-x: hidden ! important;
+    overflow-y: scroll ! important;
+}
diff --git a/src/skins/vector/css/organisms/RoomSubList.css b/src/skins/vector/css/organisms/RoomSubList.css
index 57d23a3837..b143c998c0 100644
--- a/src/skins/vector/css/organisms/RoomSubList.css
+++ b/src/skins/vector/css/organisms/RoomSubList.css
@@ -29,7 +29,7 @@ limitations under the License.
     text-transform: uppercase;
     color: #3d3b39;
     font-weight: 600;
-    font-size: 14px;
+    font-size: 13px;
     padding-left: 12px;
     padding-right: 12px;
     margin-top: 8px;
diff --git a/src/skins/vector/css/organisms/RoomView.css b/src/skins/vector/css/organisms/RoomView.css
index 94fff29068..2358bc095e 100644
--- a/src/skins/vector/css/organisms/RoomView.css
+++ b/src/skins/vector/css/organisms/RoomView.css
@@ -170,10 +170,35 @@ limitations under the License.
 
 .mx_RoomView_statusAreaBox_line {
     border-top: 1px solid #eee;
-    margin-left: 63px;
     height: 1px;
 }
 
+.mx_RoomView_inCall .mx_RoomView_statusAreaBox_line {
+    border-top: 1px hidden;
+}
+
+.mx_RoomView_inCall .mx_MessageComposer_wrapper {
+    border-top: 2px hidden;
+}
+
+.mx_RoomView_inCall .mx_RoomView_statusAreaBox {
+    background-color: #76CFA6;    
+    color: #fff;
+    position: relative;
+}
+
+.mx_RoomView_voipChevron {
+    position: absolute;
+    bottom: -10px;
+    right: 11px;
+}
+
+.mx_RoomView_voipButton {
+    float: right;
+    margin-right: 13px;
+    cursor: pointer;
+}
+
 .mx_RoomView_unreadMessagesBar {
     color: #ff0064;
     cursor: pointer;
@@ -186,6 +211,16 @@ limitations under the License.
     vertical-align: middle;
 }
 
+.mx_RoomView_callBar {
+    margin-top: 5px;
+}
+
+.mx_RoomView_callBar img {
+    padding-left: 13px;
+    padding-right: 30px;
+    vertical-align: middle;
+}
+
 .mx_RoomView_connectionLostBar {
     margin-top: 19px;
     height: 58px;
@@ -204,7 +239,7 @@ limitations under the License.
 
 .mx_RoomView_connectionLostBar_desc {
     color: #454545;
-    font-size: 14px;
+    font-size: 13px;
     opacity: 0.5;
 }
 
@@ -215,8 +250,8 @@ limitations under the License.
 }
 
 .mx_RoomView_typingBar {
-    margin-top: 10px;
-    margin-left: 63px;
+    margin-top: 6px;
+    margin-left: 65px;
     color: #4a4a4a;
     opacity: 0.5;
 }
@@ -228,6 +263,11 @@ limitations under the License.
     float: left;
 }
 
+.mx_RoomView_typingText {
+    overflow-y: hidden;
+    display: block;
+}
+
 .mx_RoomView .mx_MessageComposer {
     -webkit-box-ordinal-group: 5;
     -moz-box-ordinal-group: 5;
@@ -241,43 +281,6 @@ limitations under the License.
     margin-right: 2px;
 }
 
-.mx_RoomView_uploadProgressOuter {
-    height: 4px;
-    margin-left: 63px;
-    margin-top: -1px;
-}
-
-.mx_RoomView_uploadProgressInner {
-    background-color: #76cfa6;
-    height: 4px;
-}
-
-.mx_RoomView_uploadFilename {
-    margin-top: 5px;
-    margin-left: 65px;
-    opacity: 0.5;
-    color: #4a4a4a;
-}
-
-.mx_RoomView_uploadIcon {
-    float: left;
-    margin-top: 1px;
-    margin-left: 14px;
-}
-
-.mx_RoomView_uploadCancel {
-    float: right;
-    margin-top: 5px;
-    margin-right: 10px;
-}
-
-.mx_RoomView_uploadBytes {
-    float: right;    
-    margin-top: 5px;
-    margin-right: 30px;
-    color: #76cfa6;
-}
-
 .mx_RoomView_ongoingConfCallNotification {
     width: 100%;
     text-align: center;
diff --git a/src/skins/vector/css/pages/MatrixChat.css b/src/skins/vector/css/pages/MatrixChat.css
index b95f6a415c..2190e49601 100644
--- a/src/skins/vector/css/pages/MatrixChat.css
+++ b/src/skins/vector/css/pages/MatrixChat.css
@@ -14,6 +14,18 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+.mx_MatrixChat_splash {
+    position: relative;
+    height: 100%;
+}
+
+.mx_MatrixChat_splashButtons {
+    text-align: center;
+    width: 100%;
+    position: absolute;
+    bottom: 30px;
+}
+
 .mx_MatrixChat_wrapper {
     display: -webkit-box;
     display: -moz-box;
diff --git a/src/skins/vector/css/templates/Login.css b/src/skins/vector/css/templates/Login.css
index 11fba43fbc..d1b28b1e59 100644
--- a/src/skins/vector/css/templates/Login.css
+++ b/src/skins/vector/css/templates/Login.css
@@ -55,7 +55,7 @@ limitations under the License.
     border-radius: 3px;
     border: 1px solid #c7c7c7;
     font-weight: 300;
-    font-size: 14px;
+    font-size: 13px;
     padding: 9px;
     margin-bottom: 14px;
 }
@@ -68,12 +68,12 @@ limitations under the License.
     height: 40px;
     border: 0px;
     background-color: #76cfa6;
-    font-size: 16px;
+    font-size: 15px;
     color: #fff;
 }
 
 .mx_Login_label {
-    font-size: 14px;
+    font-size: 13px;
     opacity: 0.8;
 }
 
@@ -85,7 +85,7 @@ limitations under the License.
     display: block;
     text-align: center;
     width: 100%;
-    font-size: 14px;
+    font-size: 13px;
     opacity: 0.8;
 }
 
@@ -97,7 +97,7 @@ limitations under the License.
     display: block;
     text-align: center;
     width: 100%;
-    font-size: 14px;
+    font-size: 13px;
     opacity: 0.8;
 }
 
diff --git a/src/skins/vector/fonts/OpenSans.css b/src/skins/vector/fonts/OpenSans.css
new file mode 100644
index 0000000000..05be90d52c
--- /dev/null
+++ b/src/skins/vector/fonts/OpenSans.css
@@ -0,0 +1,12 @@
+@font-face {
+  font-family: 'Open Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Open Sans'), local('OpenSans'), url(u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf) format('truetype');
+}
+@font-face {
+  font-family: 'Open Sans';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Open Sans Bold'), local('OpenSans-Bold'), url(k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf) format('truetype');
+}
diff --git a/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf
new file mode 100644
index 0000000000..bffc5fcf61
Binary files /dev/null and b/src/skins/vector/fonts/k3k702ZOKiLJc3WVjuplzNqQynqKV_9Plp7mupa0S4g.ttf differ
diff --git a/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf b/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf
new file mode 100644
index 0000000000..89ce23b4b4
Binary files /dev/null and b/src/skins/vector/fonts/u-WUoqrET9fUeobQW7jkRaCWcynf_cDxXwCLxiixG1c.ttf differ
diff --git a/src/skins/vector/img/call.svg b/src/skins/vector/img/call.svg
new file mode 100644
index 0000000000..f528f9a24e
--- /dev/null
+++ b/src/skins/vector/img/call.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="30px" height="22px" viewBox="-1 -1 30 22" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_video</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -723.000000)" stroke="#76CFA6">
+            <g id="icons_video" sketch:type="MSLayerGroup" transform="translate(910.000000, 722.000000)">
+                <g id="Rectangle-20-+-Path-16" transform="translate(0.000000, 0.464286)" sketch:type="MSShapeGroup">
+                    <rect id="Rectangle-20" fill="#FFFFFF" x="0" y="0.535714286" width="20" height="20" rx="4"></rect>
+                    <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/cancel.svg b/src/skins/vector/img/cancel.svg
new file mode 100644
index 0000000000..e32060025e
--- /dev/null
+++ b/src/skins/vector/img/cancel.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="18px" height="18px" viewBox="0 0 18 18" 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.4.2 (15857) - http://www.bohemiancoding.com/sketch -->
+    <title>Slice 1</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">
+        <path d="M9.74464309,-3.02908503 L8.14106175,-3.02908503 L8.14106175,8.19448443 L-3.03028759,8.19448443 L-3.03028759,9.7978515 L8.14106175,9.7978515 L8.14106175,20.9685098 L9.74464309,20.9685098 L9.74464309,9.7978515 L20.9697124,9.7978515 L20.9697124,8.19448443 L9.74464309,8.19448443 L9.74464309,-3.02908503" id="Fill-108" opacity="0.9" fill="#454545" sketch:type="MSShapeGroup" transform="translate(8.969712, 8.969712) rotate(-315.000000) translate(-8.969712, -8.969712) "></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/files.svg b/src/skins/vector/img/files.svg
new file mode 100644
index 0000000000..20aba851ea
--- /dev/null
+++ b/src/skins/vector/img/files.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="17px" height="22px" viewBox="0 0 17 22" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_browse_files</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-1025.000000, -33.000000)">
+            <g id="icons_browse_files" sketch:type="MSLayerGroup" transform="translate(1025.000000, 32.000000)">
+                <g id="Rectangle-5-+-Rectangle-6-Copy" transform="translate(0.000000, 1.000000)" sketch:type="MSShapeGroup">
+                    <path d="M0,4.00955791 C0,1.79514022 1.78163126,0 3.99825563,0 L9.59161955,0 C9.59161955,0 16.3225806,6.49234232 16.3225806,6.49234232 L16.3225806,18.0063928 C16.3225806,20.2120012 14.5290874,22 12.3296282,22 L3.99295243,22 C1.7877057,22 0,20.1996477 0,17.9904421 L0,4.00955791 Z" id="Rectangle-5" stroke="#76CFA6"></path>
+                    <path d="M15.6804916,7.49527496 L11.5273266,7.49527496 C10.3308881,7.49527496 9.3609831,6.52527676 9.3609831,5.3289315 L9.3609831,1.88544393 L15.6804916,7.49527496 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path>
+                    <path d="M16.3225806,7.09677419 L11.4129801,7.09677419 C10.2050375,7.09677419 9.22580645,6.11744908 9.22580645,4.90960051 L9.22580645,0" id="Rectangle-6" stroke="#76CFA6"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/fullscreen.svg b/src/skins/vector/img/fullscreen.svg
new file mode 100644
index 0000000000..e333abb6fb
--- /dev/null
+++ b/src/skins/vector/img/fullscreen.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="29px" height="22px" viewBox="-1 -1 29 22" 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.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+    <title>Zoom</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-824.000000, -667.000000)">
+            <g id="Zoom" sketch:type="MSLayerGroup" transform="translate(824.000000, 667.000000)">
+                <rect id="Rectangle-20" stroke="#FFFFFF" sketch:type="MSShapeGroup" x="0" y="0" width="27" height="20" rx="4"></rect>
+                <rect id="Rectangle-20" stroke="#FFFFFF" sketch:type="MSShapeGroup" x="6" y="2" width="15" height="11" rx="4"></rect>
+                <g id="Line-+-Triangle-3" transform="translate(4.474874, 15.181981) rotate(-45.000000) translate(-4.474874, -15.181981) translate(0.974874, 13.181981)" sketch:type="MSShapeGroup">
+                    <path d="M6.53553391,2 L3,2" id="Line" stroke="#FFFFFF" stroke-linecap="square"></path>
+                    <path d="M3.26960678,3.51960678 L0.269606781,2.01960678 L3.26960678,0.519606781 L3.26960678,3.51960678 Z" id="Triangle-3" fill="#FFFFFF"></path>
+                </g>
+                <g id="Line-+-Triangle-3-Copy" transform="translate(22.681981, 15.181981) scale(-1, 1) rotate(-45.000000) translate(-22.681981, -15.181981) translate(19.181981, 13.181981)" sketch:type="MSShapeGroup">
+                    <path d="M6.53553391,2 L3,2" id="Line" stroke="#FFFFFF" stroke-linecap="square"></path>
+                    <path d="M3.375,0.5 L3.375,3.5 L0.375,2 L3.375,0.5 Z" id="Triangle-3" fill="#FFFFFF"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/hangup.svg b/src/skins/vector/img/hangup.svg
new file mode 100644
index 0000000000..be038d2b30
--- /dev/null
+++ b/src/skins/vector/img/hangup.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="25px" height="26px" viewBox="-1 -1 25 26" 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.4.3 (16618) - http://www.bohemiancoding.com/sketch -->
+    <title>Fill 72 + Path 98</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -719.000000)" stroke="#FF0064">
+            <g id="Fill-72-+-Path-98" sketch:type="MSLayerGroup" transform="translate(910.000000, 719.000000)">
+                <path d="M17.8404444,24 C15.8137778,24 10.8875556,21.408 6.75022222,15.8448889 C2.88088889,10.6413333 1,6.88222222 1,4.35244444 C1,2.36088889 2.37511111,1.41022222 3.11422222,0.9 L3.29644444,0.772888889 C4.11288889,0.188888889 5.38222222,0 5.86888889,0 C6.72222222,0 7.08177778,0.499555556 7.29955556,0.935111111 C7.48488889,1.30311111 9.01777778,4.59511111 9.17288889,5.00444444 C9.41111111,5.63377778 9.33288889,6.55111111 8.596,7.07822222 L8.46622222,7.16844444 C8.10044444,7.42222222 7.42,7.89333333 7.32577778,8.46622222 C7.28,8.74488889 7.37333333,9.03644444 7.61111111,9.35688889 C8.79777778,10.956 12.5862222,15.6506667 13.2693333,16.2884444 C13.8044444,16.7884444 14.4826667,16.8595556 14.9444444,16.4702222 C15.4222222,16.0675556 15.6342222,15.8297778 15.6364444,15.8271111 L15.6857778,15.7795556 C15.7257778,15.7457778 16.0991111,15.4497778 16.7093333,15.4497778 C17.1497778,15.4497778 17.5973333,15.6017778 18.04,15.9008889 C19.1884444,16.6768889 21.7808889,18.4106667 21.7808889,18.4106667 L21.8226667,18.4426667 C22.1542222,18.7266667 22.6333333,19.5453333 22.0751111,20.6106667 C21.496,21.7168889 19.6986667,24 17.8404444,24 L17.8404444,24 Z" id="Fill-72" sketch:type="MSShapeGroup"></path>
+                <path d="M19.8035085,4 L0,22.8035085" id="Path-98" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/leave.svg b/src/skins/vector/img/leave.svg
new file mode 100644
index 0000000000..8485607360
--- /dev/null
+++ b/src/skins/vector/img/leave.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
+	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-1 -1 20 26"
+	 xml:space="preserve">
+<style type="text/css">
+	.st0{fill:none;stroke:#76CFA6;}
+	.st1{fill:#76CFA6;}
+</style>
+<g id="Page-1" sketch:type="MSPage">
+	<g id="Exit-Copy-6" sketch:type="MSLayerGroup">
+		<path id="Rectangle-140_1_" sketch:type="MSShapeGroup" class="st0" d="M4.4,0.5h7.5c2.2,0,4,1.8,4,4v14c0,2.2-1.8,4-4,4H4.4
+			c-2.2,0-4-1.8-4-4v-14C0.4,2.3,2.2,0.5,4.4,0.5z"/>
+		<g id="Rectangle-140_2_">
+			<g>
+				<path class="st1" d="M12,23H4.5C2,23,0,21,0,18.5v-14C0,2,2,0,4.5,0H12c2.5,0,4.5,2,4.5,4.5v14C16.4,21,14.4,23,12,23z M4.4,1
+					C2.5,1,0.9,2.6,0.9,4.5v14c0,1.9,1.6,3.5,3.5,3.5h7.5c1.9,0,3.5-1.6,3.5-3.5v-14c0-1.9-1.6-3.5-3.5-3.5H4.4z"/>
+				<g>
+					<path class="st0" d="M3.2,20.6l8.3-1.8c1.1-0.2,2-1,2-1.7V5.7c0-0.7-0.9-1.4-2-1.7L3.2,2.2"/>
+				</g>
+			</g>
+		</g>
+		<circle id="Oval-605" sketch:type="MSShapeGroup" class="st1" cx="10.7" cy="11.1" r="1">
+		</circle>
+	</g>
+</g>
+</svg>
diff --git a/src/skins/vector/img/list-close.svg b/src/skins/vector/img/list-close.svg
new file mode 100644
index 0000000000..eb60864e2c
--- /dev/null
+++ b/src/skins/vector/img/list-close.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="20px" height="20px" viewBox="0 0 20 20" 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.4.2 (15857) - http://www.bohemiancoding.com/sketch -->
+    <title>Slice 1</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">
+        <path d="M0,5 L20,5 L10,15 L0,5 Z" id="Triangle-1" fill="#76CFA6" sketch:type="MSShapeGroup" transform="translate(10.000000, 10.000000) rotate(-90.000000) translate(-10.000000, -10.000000) "></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/list-open.svg b/src/skins/vector/img/list-open.svg
new file mode 100644
index 0000000000..a682ec9051
--- /dev/null
+++ b/src/skins/vector/img/list-open.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="20px" height="20px" viewBox="0 0 20 20" 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.4.2 (15857) - http://www.bohemiancoding.com/sketch -->
+    <title>Slice 1</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">
+        <path d="M0,5 L20,5 L10,15 L0,5 Z" id="Triangle-1" fill="#76CFA6" sketch:type="MSShapeGroup"></path>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/members.svg b/src/skins/vector/img/members.svg
new file mode 100644
index 0000000000..0f115966ab
--- /dev/null
+++ b/src/skins/vector/img/members.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="17px" height="22px" viewBox="0 0 17 22" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_people</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-978.000000, -32.000000)" fill="#76CFA6">
+            <g id="icons_people" sketch:type="MSLayerGroup" transform="translate(978.000000, 32.000000)">
+                <path d="M7.79396172,11.1845854 C10.8910264,11.1845854 13.4110283,8.6755122 13.4110283,5.59121951 C13.4110283,2.50853659 10.8910264,0 7.79396172,0 C4.69851351,0 2.18012797,2.50853659 2.18012797,5.59121951 C2.18012797,8.6755122 4.69851351,11.1845854 7.79396172,11.1845854 L7.79396172,11.1845854 Z M7.79396172,0.804878049 C10.4454327,0.804878049 12.6028173,2.95229268 12.6028173,5.59121951 C12.6028173,8.2317561 10.4454327,10.3797073 7.79396172,10.3797073 C5.14410719,10.3797073 2.98833899,8.2317561 2.98833899,5.59121951 C2.98833899,2.95229268 5.14410719,0.804878049 7.79396172,0.804878049 L7.79396172,0.804878049 Z M0.885806936,21.4634146 C1.33849039,19.4978231 2.46183493,15.4298815 3.13651101,14.4663415 C4.32835286,12.76 6.45287023,12.8072195 6.48358225,12.8034634 L9.03699027,12.8013171 C9.06393064,12.7980976 11.7369539,12.5421463 12.9228689,14.5377073 C12.9320286,14.5532683 12.9422659,14.5682927 12.9535809,14.5827805 C13.6376414,15.4425239 14.755176,19.1307382 15.2598605,21.1908293 L15.2598605,21.1908293 L14.9096797,21.1908293 L1.53746094,21.1908293 L1.15172387,21.1908293 L1.15172387,21.9957073 L1.53746094,21.9957073 L14.9096797,21.9957073 L15.2954167,21.9957073 L15.2954167,21.3375005 C15.3143567,21.416496 15.3322794,21.4927245 15.3491183,21.5659024 C15.3922229,21.7520976 15.5587144,21.8781951 15.7429865,21.8781951 C15.7726209,21.8781951 15.8027941,21.8749756 15.8335061,21.8685366 C16.0506455,21.8186341 16.1869638,21.6029268 16.1368547,21.3856098 C15.7844747,19.8558049 14.5355192,15.312 13.6044602,14.1052195 C12.144831,11.6825366 9.08063366,11.9857073 8.99388568,11.9985854 L6.49759124,11.9985854 C6.38767454,11.996439 3.92209212,11.9320488 2.47323917,14.0059512 C1.66987741,15.1531707 0.489889321,19.5553171 0.0750076642,21.3872195 C0.0254373883,21.6045366 0.162294454,21.8197073 0.379972623,21.8685366 C0.460649718,21.8870317 0.541104777,21.8798767 0.61291652,21.8524806 L0.61291652,22 L1.15172387,22 L1.15172387,21.4634146 L0.885804128,21.4634146 Z" id="Fill-92" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/search-button.svg b/src/skins/vector/img/search-button.svg
new file mode 100644
index 0000000000..f4808842ff
--- /dev/null
+++ b/src/skins/vector/img/search-button.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="21px" height="19px" viewBox="-8 -8 37 37" 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.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+    <title>icon_search</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="icon_search" sketch:type="MSLayerGroup" transform="translate(-8.000000, -8.000000)" stroke="#FFFFFF">
+            <path d="M21.328421,19.3333333 L27.5542961,24.7357275 C27.9574623,25.085568 28.0116667,25.70516 27.6700827,26.1261351 L27.6700827,26.1261351 C27.3308636,26.5441955 26.72562,26.5965299 26.3258751,26.2496583 L20.1,20.8472641" id="Rectangle-9" sketch:type="MSShapeGroup"></path>
+            <g id="search" transform="translate(15.617851, 15.853553) rotate(-45.000000) translate(-15.617851, -15.853553) translate(8.117851, 7.853553)" sketch:type="MSShapeGroup">
+                <ellipse id="Search" cx="7.64433504" cy="7.90518519" rx="7.1665641" ry="7.41111111"></ellipse>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/search.svg b/src/skins/vector/img/search.svg
new file mode 100644
index 0000000000..bd4cd9200c
--- /dev/null
+++ b/src/skins/vector/img/search.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="21px" height="19px" viewBox="0 0 21 19" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_search</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -34.000000)" stroke="#76CFA6">
+            <g id="icons_search" sketch:type="MSLayerGroup" transform="translate(906.000000, 30.000000)">
+                <path d="M17.328421,15.3333333 L23.5542961,20.7357275 C23.9574623,21.085568 24.0116667,21.70516 23.6700827,22.1261351 L23.6700827,22.1261351 C23.3308636,22.5441955 22.72562,22.5965299 22.3258751,22.2496583 L16.1,16.8472641" id="Rectangle-9" sketch:type="MSShapeGroup"></path>
+                <g id="search" transform="translate(11.617851, 11.853553) rotate(-45.000000) translate(-11.617851, -11.853553) translate(4.117851, 3.853553)" sketch:type="MSShapeGroup">
+                    <ellipse id="Search" cx="7.64433504" cy="7.90518519" rx="7.1665641" ry="7.41111111"></ellipse>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/settings.svg b/src/skins/vector/img/settings.svg
new file mode 100644
index 0000000000..4190c7b8de
--- /dev/null
+++ b/src/skins/vector/img/settings.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="12px" height="12px" viewBox="0 0 12 12" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icon_settings_small</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="06a-Room-settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="06a_1-Room-settings-hover" sketch:type="MSArtboardGroup" transform="translate(-452.000000, -27.000000)" fill="#76CFA6">
+            <path d="M460,33 C460,32.4479139 459.804689,31.9765645 459.414062,31.5859375 C459.023436,31.1953105 458.552086,31 458,31 C457.447914,31 456.976564,31.1953105 456.585938,31.5859375 C456.195311,31.9765645 456,32.4479139 456,33 C456,33.5520861 456.195311,34.0234355 456.585938,34.4140625 C456.976564,34.8046895 457.447914,35 458,35 C458.552086,35 459.023436,34.8046895 459.414062,34.4140625 C459.804689,34.0234355 460,33.5520861 460,33 L460,33 Z M464,32.1484375 L464,33.8828125 C464,33.9453128 463.979167,34.005208 463.9375,34.0625 C463.895833,34.119792 463.84375,34.1536458 463.78125,34.1640625 L462.335938,34.3828125 C462.236979,34.6640639 462.135417,34.9010407 462.03125,35.09375 C462.213543,35.354168 462.492186,35.7135394 462.867188,36.171875 C462.919271,36.2343753 462.945312,36.2994788 462.945312,36.3671875 C462.945312,36.4348962 462.921875,36.4947914 462.875,36.546875 C462.734374,36.7395843 462.476564,37.0208315 462.101562,37.390625 C461.726561,37.7604185 461.481771,37.9453125 461.367188,37.9453125 C461.304687,37.9453125 461.23698,37.9218752 461.164062,37.875 L460.085938,37.03125 C459.85677,37.1510423 459.619793,37.2499996 459.375,37.328125 C459.291666,38.0364619 459.216146,38.520832 459.148438,38.78125 C459.111979,38.9270841 459.01823,39 458.867188,39 L457.132812,39 C457.059895,39 456.996094,38.9778648 456.941406,38.9335938 C456.886718,38.8893227 456.856771,38.8333337 456.851562,38.765625 L456.632812,37.328125 C456.377603,37.2447912 456.14323,37.148438 455.929688,37.0390625 L454.828125,37.875 C454.776041,37.9218752 454.710938,37.9453125 454.632812,37.9453125 C454.559895,37.9453125 454.494792,37.916667 454.4375,37.859375 C453.781247,37.265622 453.351564,36.8281264 453.148438,36.546875 C453.111979,36.4947914 453.09375,36.4348962 453.09375,36.3671875 C453.09375,36.3046872 453.114583,36.244792 453.15625,36.1875 C453.234375,36.0781245 453.367187,35.9049491 453.554688,35.6679688 C453.742188,35.4309884 453.882812,35.2473965 453.976562,35.1171875 C453.835937,34.8567695 453.729167,34.5989596 453.65625,34.34375 L452.226562,34.1328125 C452.158854,34.1223958 452.104167,34.089844 452.0625,34.0351562 C452.020833,33.9804685 452,33.9192712 452,33.8515625 L452,32.1171875 C452,32.0546872 452.020833,31.994792 452.0625,31.9375 C452.104167,31.880208 452.153646,31.8463542 452.210938,31.8359375 L453.664062,31.6171875 C453.73698,31.377603 453.838541,31.138022 453.96875,30.8984375 C453.760416,30.601561 453.481773,30.2421896 453.132812,29.8203125 C453.080729,29.7578122 453.054688,29.6953128 453.054688,29.6328125 C453.054688,29.5807289 453.078125,29.5208337 453.125,29.453125 C453.260417,29.2656241 453.516925,28.9856789 453.894531,28.6132812 C454.272137,28.2408836 454.518229,28.0546875 454.632812,28.0546875 C454.700521,28.0546875 454.768229,28.0807289 454.835938,28.1328125 L455.914062,28.96875 C456.14323,28.8489577 456.380207,28.7500004 456.625,28.671875 C456.708334,27.9635381 456.783854,27.479168 456.851562,27.21875 C456.888021,27.0729159 456.98177,27 457.132812,27 L458.867188,27 C458.940105,27 459.003906,27.0221352 459.058594,27.0664062 C459.113282,27.1106773 459.143229,27.1666663 459.148438,27.234375 L459.367188,28.671875 C459.622397,28.7552088 459.85677,28.851562 460.070312,28.9609375 L461.179688,28.125 C461.226563,28.0781248 461.289062,28.0546875 461.367188,28.0546875 C461.434896,28.0546875 461.5,28.0807289 461.5625,28.1328125 C462.234378,28.7526073 462.664062,29.1953112 462.851562,29.4609375 C462.888021,29.5026044 462.90625,29.5598955 462.90625,29.6328125 C462.90625,29.6953128 462.885417,29.755208 462.84375,29.8125 C462.765625,29.9218755 462.632813,30.0950509 462.445312,30.3320312 C462.257812,30.5690116 462.117188,30.7526035 462.023438,30.8828125 C462.158855,31.1432305 462.265625,31.3984362 462.34375,31.6484375 L463.773438,31.8671875 C463.841146,31.8776042 463.895833,31.910156 463.9375,31.9648438 C463.979167,32.0195315 464,32.0807288 464,32.1484375 L464,32.1484375 Z" id="icon_settings_small" sketch:type="MSShapeGroup"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/sound-indicator.svg b/src/skins/vector/img/sound-indicator.svg
new file mode 100644
index 0000000000..9b8de53d81
--- /dev/null
+++ b/src/skins/vector/img/sound-indicator.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="23px" height="20px" viewBox="-1 -1 23 20" 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.4.3 (16618) - http://www.bohemiancoding.com/sketch -->
+    <title>sound_indicator</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-247.000000, -668.000000)" fill="#FFFFFF">
+            <g id="sound_indicator" sketch:type="MSLayerGroup" transform="translate(247.000000, 668.000000)">
+                <path d="M5.769,6.7284 L7.8,4.6968 L7.8,13.0626 L5.4174,10.68 L1.2,10.68 L1.2,7.08 L5.4174,7.08 L5.769,6.7284 L5.769,6.7284 Z M0,5.88 L0,11.88 L4.9206,11.88 L9,15.9594 L9,1.8 L4.9206,5.88 L0,5.88 L0,5.88 Z" id="Fill-28" sketch:type="MSShapeGroup"></path>
+                <path d="M17.1672,0 L16.2,0.72 C17.9172,3.021 18.9354,5.8728 18.9354,8.9586 C18.9354,12.0438 17.9172,14.8956 16.2,17.196 L17.1672,17.9166 C19.0314,15.417 20.1354,12.3168 20.1354,8.9586 C20.1354,5.6004 19.0314,2.4996 17.1672,0" id="Fill-29" sketch:type="MSShapeGroup"></path>
+                <path d="M14.1606,15.9948 C15.579,14.094 16.4304,11.7462 16.4304,9.1974 C16.4304,6.6486 15.579,4.3008 14.1606,2.4 L13.2,3.1152 C14.469,4.8156 15.2304,6.9168 15.2304,9.1974 C15.2304,11.478 14.4696,13.5786 13.2,15.2796 L14.1606,15.9948" id="Fill-30" sketch:type="MSShapeGroup"></path>
+                <path d="M11.7606,13.5024 C12.732,12.2016 13.314,10.5954 13.314,8.8512 C13.314,7.107 12.732,5.5002 11.7606,4.2 L10.8,4.9152 C11.6214,6.0156 12.114,7.3752 12.114,8.8512 C12.114,10.3272 11.6214,11.6862 10.8,12.7866 L11.7606,13.5024" id="Fill-31" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/tab.svg b/src/skins/vector/img/tab.svg
new file mode 100644
index 0000000000..eae92dcf32
--- /dev/null
+++ b/src/skins/vector/img/tab.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="20px" height="14px" viewBox="0 0 20 14" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icon_eol</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="03-Input" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="03_2-Mentions-autocomplete" sketch:type="MSArtboardGroup" transform="translate(-772.000000, -670.000000)">
+            <g id="icon_eol" sketch:type="MSLayerGroup" transform="translate(772.000000, 670.000000)">
+                <path d="M0,7.5 L17,7.5" id="Path-118" stroke="#76CFA6" sketch:type="MSShapeGroup"></path>
+                <path d="M13,2 L18,7.38056399 L13,12.761128" id="Path-119" stroke="#76CFA6" sketch:type="MSShapeGroup"></path>
+                <path d="M19.5,0 L19.5,14" id="Path-120" stroke="#80CEF4" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/upload-big.svg b/src/skins/vector/img/upload-big.svg
new file mode 100644
index 0000000000..6099c2e976
--- /dev/null
+++ b/src/skins/vector/img/upload-big.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="45px" height="59px" viewBox="-1 -1 45 59" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_upload_drop</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="03-Input" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="03_05-File-drop" sketch:type="MSArtboardGroup" transform="translate(-570.000000, -368.000000)">
+            <g id="icons_upload_drop" sketch:type="MSLayerGroup" transform="translate(570.000000, 368.000000)">
+                <g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup">
+                    <path d="M0,4.00812931 C0,1.79450062 1.78537926,0 4.00241155,0 L24.8253683,0 C24.8253683,0 42.2466793,16.8210687 42.2466793,16.8210687 L42.2466793,53.000599 C42.2466793,55.2094072 40.4583762,57 38.2531894,57 L3.99348992,57 C1.78794634,57 0,55.1999609 0,52.9918707 L0,4.00812931 Z" id="Rectangle-5" stroke="#76CFA6"></path>
+                    <path d="M40.5848017,19.419576 L29.8354335,19.419576 C26.7387692,19.419576 24.2284269,16.9063989 24.2284269,13.8067771 L24.2284269,4.88501382 L40.5848017,19.419576 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path>
+                    <path d="M42.2466793,18.3870968 L29.539478,18.3870968 C26.4130381,18.3870968 23.8785579,15.8497544 23.8785579,12.7203286 L23.8785579,0" id="Rectangle-6" stroke="#76CFA6"></path>
+                </g>
+                <path d="M31.3419737,32.9284726 C31.701384,32.9284726 32.0607942,32.8000473 32.3359677,32.5414375 C32.8825707,32.0259772 32.8825707,31.1920926 32.3359677,30.6766323 L21.622922,20.6119619 C21.076319,20.0965016 20.187153,20.0982608 19.638678,20.6102026 L8.9125289,30.6607991 C8.36405391,31.1762594 8.36218198,32.0119032 8.90878504,32.5273635 C9.4553881,33.0445831 10.344554,33.0445831 10.893029,32.530882 L19.2399573,24.7092556 L19.2437012,46.487014 C19.2437012,47.2153435 19.874541,47.8064516 20.6476474,47.8064516 C21.4244976,47.8064516 22.0515936,47.2153435 22.0515936,46.487014 L22.0478497,24.7426814 L30.3498517,32.5414375 C30.6231533,32.8000473 30.9825635,32.9284726 31.3419737,32.9284726 L31.3419737,32.9284726 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/src/skins/vector/img/upload-original.svg b/src/skins/vector/img/upload-original.svg
new file mode 100644
index 0000000000..962fc49d77
--- /dev/null
+++ b/src/skins/vector/img/upload-original.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="17px" height="22px" viewBox="0 0 17 22" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_upload</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-873.000000, -722.000000)">
+            <g id="icons_upload" sketch:type="MSLayerGroup" transform="translate(873.000000, 722.000000)">
+                <g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup">
+                    <path d="M0,4.00955791 C0,1.79514022 1.78163126,0 3.99825563,0 L9.59161955,0 C9.59161955,0 16.3225806,6.49234232 16.3225806,6.49234232 L16.3225806,18.0063928 C16.3225806,20.2120012 14.5290874,22 12.3296282,22 L3.99295243,22 C1.7877057,22 0,20.1996477 0,17.9904421 L0,4.00955791 Z" id="Rectangle-5" stroke="#76CFA6"></path>
+                    <path d="M15.6804916,7.49527496 L11.5273266,7.49527496 C10.3308881,7.49527496 9.3609831,6.52527676 9.3609831,5.3289315 L9.3609831,1.88544393 L15.6804916,7.49527496 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path>
+                    <path d="M16.3225806,7.09677419 L11.4129801,7.09677419 C10.2050375,7.09677419 9.22580645,6.11744908 9.22580645,4.90960051 L9.22580645,0" id="Rectangle-6" stroke="#76CFA6"></path>
+                </g>
+                <path d="M12.3736951,12.709235 C12.5125582,12.709235 12.6514212,12.6596674 12.7577382,12.5598531 C12.9689258,12.3609035 12.9689258,12.0390533 12.7577382,11.8401037 L8.61860697,7.95549406 C8.40741942,7.75654446 8.06387804,7.75722347 7.85196724,7.95481505 L3.70777326,11.8339926 C3.49586247,12.0329422 3.49513923,12.3554714 3.70632677,12.554421 C3.91751432,12.7540496 4.2610557,12.7540496 4.4729665,12.555779 L7.69791605,9.53690567 L7.69936254,17.9423563 C7.69936254,18.2234659 7.94309612,18.4516129 8.24179631,18.4516129 C8.541943,18.4516129 8.78423008,18.2234659 8.78423008,17.9423563 L8.78278359,9.54980684 L11.9903753,12.5598531 C12.095969,12.6596674 12.2348321,12.709235 12.3736951,12.709235 L12.3736951,12.709235 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/upload.svg b/src/skins/vector/img/upload.svg
new file mode 100644
index 0000000000..039014a2f3
--- /dev/null
+++ b/src/skins/vector/img/upload.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="19px" height="24px" viewBox="-1 -1 19 24" 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: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_upload</title>
+    <desc>Created with bin/sketchtool.</desc>
+    <defs></defs>
+    <g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-873.000000, -722.000000)">
+            <g id="icons_upload" sketch:type="MSLayerGroup" transform="translate(873.000000, 722.000000)">
+                <g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup">
+                    <path d="M0,4.00955791 C0,1.79514022 1.78163126,0 3.99825563,0 L9.59161955,0 C9.59161955,0 16.3225806,6.49234232 16.3225806,6.49234232 L16.3225806,18.0063928 C16.3225806,20.2120012 14.5290874,22 12.3296282,22 L3.99295243,22 C1.7877057,22 0,20.1996477 0,17.9904421 L0,4.00955791 Z" id="Rectangle-5" stroke="#76CFA6"></path>
+                    <path d="M15.6804916,7.49527496 L11.5273266,7.49527496 C10.3308881,7.49527496 9.3609831,6.52527676 9.3609831,5.3289315 L9.3609831,1.88544393 L15.6804916,7.49527496 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path>
+                    <path d="M16.3225806,7.09677419 L11.4129801,7.09677419 C10.2050375,7.09677419 9.22580645,6.11744908 9.22580645,4.90960051 L9.22580645,0" id="Rectangle-6" stroke="#76CFA6"></path>
+                </g>
+                <path d="M12.3736951,12.709235 C12.5125582,12.709235 12.6514212,12.6596674 12.7577382,12.5598531 C12.9689258,12.3609035 12.9689258,12.0390533 12.7577382,11.8401037 L8.61860697,7.95549406 C8.40741942,7.75654446 8.06387804,7.75722347 7.85196724,7.95481505 L3.70777326,11.8339926 C3.49586247,12.0329422 3.49513923,12.3554714 3.70632677,12.554421 C3.91751432,12.7540496 4.2610557,12.7540496 4.4729665,12.555779 L7.69791605,9.53690567 L7.69936254,17.9423563 C7.69936254,18.2234659 7.94309612,18.4516129 8.24179631,18.4516129 C8.541943,18.4516129 8.78423008,18.2234659 8.78423008,17.9423563 L8.78278359,9.54980684 L11.9903753,12.5598531 C12.095969,12.6596674 12.2348321,12.709235 12.3736951,12.709235 L12.3736951,12.709235 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/video-mute.svg b/src/skins/vector/img/video-mute.svg
new file mode 100644
index 0000000000..6de60ba39b
--- /dev/null
+++ b/src/skins/vector/img/video-mute.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="31px" height="27px" viewBox="-2 -2 31 27" 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.4.3 (16618) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_video copy</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-871.000000, -667.000000)" stroke="#FFFFFF">
+            <g id="icons_video-copy" sketch:type="MSLayerGroup" transform="translate(871.000000, 666.000000)">
+                <g id="Rectangle-20-+-Path-16" transform="translate(0.000000, 0.464286)" sketch:type="MSShapeGroup">
+                    <rect id="Rectangle-20" x="0" y="0.535714286" width="20" height="20" rx="4"></rect>
+                    <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/video-unmute.svg b/src/skins/vector/img/video-unmute.svg
new file mode 100644
index 0000000000..a6c6c3b681
--- /dev/null
+++ b/src/skins/vector/img/video-unmute.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="31px" height="27px" viewBox="-1 -1 31 27" 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.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+    <title>icons_video copy</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-870.000000, -666.000000)" stroke="#FFFFFF">
+            <g id="icons_video-copy" sketch:type="MSLayerGroup" transform="translate(870.000000, 666.000000)">
+                <g id="Rectangle-20-+-Path-16" transform="translate(1.000000, 0.464286)" sketch:type="MSShapeGroup">
+                    <rect id="Rectangle-20" x="0" y="0.535714286" width="20" height="20" rx="4"></rect>
+                    <path d="M20.75,10.6964286 C20.75,14.0446429 24.188247,15.7371974 24.188247,15.7371974 C25.5057636,16.651593 26.5738219,16.0843085 26.5738219,14.4868066 L26.5738219,6.90605053 C26.5738219,5.30108314 25.4784055,4.70120148 24.188247,5.65565975 C24.188247,5.65565975 20.75,7.34821429 20.75,10.6964286 Z" id="Path-16"></path>
+                </g>
+                <path d="M24,1 L0.00856596586,23.6585766" id="Path-98" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/voice-mute.svg b/src/skins/vector/img/voice-mute.svg
new file mode 100644
index 0000000000..336641078e
--- /dev/null
+++ b/src/skins/vector/img/voice-mute.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="21px" height="26px" viewBox="-4 -1 21 26" 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.4.3 (16618) - http://www.bohemiancoding.com/sketch -->
+    <title>Audio</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-915.000000, -665.000000)" fill="#FFFFFF">
+            <g id="Audio" sketch:type="MSLayerGroup" transform="translate(915.000000, 665.000000)">
+                <path d="M3.51108197,4.57927869 C3.51108197,2.62701639 5.09940984,1.03868852 7.05206557,1.03868852 C9.00472131,1.03868852 10.5930492,2.62701639 10.5930492,4.57927869 L10.5930492,12.448918 C10.5930492,14.4015738 9.00472131,15.9899016 7.05206557,15.9899016 C5.09940984,15.9899016 3.51108197,14.4015738 3.51108197,12.448918 L3.51108197,4.57927869 L3.51108197,4.57927869 Z M7.05206557,16.7767869 C9.44183607,16.7767869 11.3799344,14.839082 11.3799344,12.448918 L11.3799344,4.57927869 C11.3799344,2.18911475 9.44183607,0.251803279 7.05206557,0.251803279 C4.66229508,0.251803279 2.72419672,2.18911475 2.72419672,4.57927869 L2.72419672,12.448918 C2.72419672,14.839082 4.66229508,16.7767869 7.05206557,16.7767869 Z M12.9517377,8.51409836 L12.9517377,12.448918 C12.9517377,15.703082 10.3042623,18.3505574 7.05009836,18.3505574 C3.79554098,18.3505574 1.14845902,15.703082 1.14845902,12.448918 L1.14845902,8.51409836 L0.36157377,8.51409836 L0.36157377,12.448918 C0.36157377,16.0217705 3.17980328,18.9407213 6.70898361,19.1201311 L6.65862295,19.1201311 L6.65862295,23.080918 L3.11567213,23.080918 L3.11567213,23.8678033 L10.9845246,23.8678033 L10.9845246,23.080918 L7.4455082,23.080918 L7.4455082,19.1201311 L7.39121311,19.1201311 C10.9203934,18.9407213 13.738623,16.0217705 13.738623,12.448918 L13.738623,8.51409836 L12.9517377,8.51409836 Z" id="Fill-16" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/voice-unmute.svg b/src/skins/vector/img/voice-unmute.svg
new file mode 100644
index 0000000000..0d7e6f429f
--- /dev/null
+++ b/src/skins/vector/img/voice-unmute.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="21px" height="26px" viewBox="-1 -1 21 26" 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.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+    <title>Audio</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-912.000000, -665.000000)">
+            <g id="Audio" sketch:type="MSLayerGroup" transform="translate(912.000000, 665.000000)">
+                <path d="M6.51108197,4.57927869 C6.51108197,2.62701639 8.09940984,1.03868852 10.0520656,1.03868852 C12.0047213,1.03868852 13.5930492,2.62701639 13.5930492,4.57927869 L13.5930492,12.448918 C13.5930492,14.4015738 12.0047213,15.9899016 10.0520656,15.9899016 C8.09940984,15.9899016 6.51108197,14.4015738 6.51108197,12.448918 L6.51108197,4.57927869 L6.51108197,4.57927869 Z M10.0520656,16.7767869 C12.4418361,16.7767869 14.3799344,14.839082 14.3799344,12.448918 L14.3799344,4.57927869 C14.3799344,2.18911475 12.4418361,0.251803279 10.0520656,0.251803279 C7.66229508,0.251803279 5.72419672,2.18911475 5.72419672,4.57927869 L5.72419672,12.448918 C5.72419672,14.839082 7.66229508,16.7767869 10.0520656,16.7767869 Z M15.9517377,8.51409836 L15.9517377,12.448918 C15.9517377,15.703082 13.3042623,18.3505574 10.0500984,18.3505574 C6.79554098,18.3505574 4.14845902,15.703082 4.14845902,12.448918 L4.14845902,8.51409836 L3.36157377,8.51409836 L3.36157377,12.448918 C3.36157377,16.0217705 6.17980328,18.9407213 9.70898361,19.1201311 L9.65862295,19.1201311 L9.65862295,23.080918 L6.11567213,23.080918 L6.11567213,23.8678033 L13.9845246,23.8678033 L13.9845246,23.080918 L10.4455082,23.080918 L10.4455082,19.1201311 L10.3912131,19.1201311 C13.9203934,18.9407213 16.738623,16.0217705 16.738623,12.448918 L16.738623,8.51409836 L15.9517377,8.51409836 Z" id="Fill-16" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
+                <path d="M19,3 L0.272141024,20.6874224" id="Path-98" stroke="#FFFFFF" sketch:type="MSShapeGroup"></path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/voice.png b/src/skins/vector/img/voice.png
new file mode 100644
index 0000000000..5ba765b0f4
Binary files /dev/null and b/src/skins/vector/img/voice.png differ
diff --git a/src/skins/vector/img/voice.svg b/src/skins/vector/img/voice.svg
new file mode 100644
index 0000000000..ff87270ba5
--- /dev/null
+++ b/src/skins/vector/img/voice.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="16px" height="26px" viewBox="0 0 16 26" 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.4.2 (15857) - http://www.bohemiancoding.com/sketch -->
+    <title>icon_voice</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="icon_voice" sketch:type="MSLayerGroup" fill="#76CFA6">
+            <path d="M15.690213,10.5130204 C15.690213,9.68172789 15.0096307,9.00560998 14.1739648,9.00560998 C13.3355096,9.00560998 12.6543693,9.68172789 12.6543693,10.5130204 L12.6554851,13.5200825 C12.6510222,13.5599846 11.9274851,17.4980943 7.84566437,17.4980943 C3.7599387,17.4980943 3.03472797,13.5815982 3.03361226,13.5500091 L3.03361226,10.5130204 C3.03361226,9.68172789 2.35302989,9.00560998 1.51680613,9.00560998 C0.680024521,9.00560998 0,9.68172789 0,10.5130204 L0,13.5500091 C0,15.5922177 1.86211801,19.602927 6.32830038,20.3771374 L6.32830038,22.9513732 L2.35358774,22.9513732 C1.51680613,22.9513732 0.836781609,23.6280454 0.836781609,24.4593379 C0.836781609,25.2900762 1.51680613,25.9661941 2.35358774,25.9661941 L13.3371831,25.9661941 C14.1717333,25.9661941 14.8506421,25.2900762 14.8506421,24.4593379 C14.8506421,23.6280454 14.1717333,22.9513732 13.3371831,22.9513732 L9.36079693,22.9513732 L9.36079693,20.3771374 C13.828095,19.602927 15.690213,15.5922177 15.690213,13.5500091 L15.690213,10.5130204 Z M13.3371831,23.7826658 C13.7098299,23.7826658 14.0138605,24.0863646 14.0138605,24.4593379 C14.0138605,24.8317569 13.7098299,25.1349016 13.3371831,25.1349016 L2.35358774,25.1349016 C1.97870958,25.1349016 1.67356322,24.8317569 1.67356322,24.4593379 C1.67356322,24.0863646 1.97870958,23.7826658 2.35358774,23.7826658 L6.74613333,23.7826658 C6.97764291,23.7826658 7.16508199,23.5970104 7.16508199,23.3670195 L7.16508199,20.0196816 C7.16508199,19.8113043 7.00888276,19.6350703 6.80024521,19.6079147 C2.63809349,19.0681288 0.836781609,15.334517 0.836781609,13.5500091 L0.836781609,10.5130204 C0.836781609,10.1400472 1.14192797,9.83690249 1.51680613,9.83690249 C1.89168429,9.83690249 2.19683065,10.1400472 2.19683065,10.5130204 L2.19683065,13.555551 C2.20519847,14.1812372 3.30696092,18.3293868 7.84566437,18.3293868 C12.2990161,18.3293868 13.491151,14.1175048 13.491151,13.5500091 L13.491151,10.5130204 C13.491151,10.1400472 13.797413,9.83690249 14.1739648,9.83690249 C14.5488429,9.83690249 14.8539893,10.1400472 14.8539893,10.5130204 L14.8539893,13.5500091 C14.8539893,15.334517 13.0532352,19.0681288 8.88940996,19.6079147 C8.68077241,19.6350703 8.52401533,19.8113043 8.52401533,20.0196816 L8.52401533,23.3670195 C8.52401533,23.5970104 8.71201226,23.7826658 8.94296398,23.7826658 L13.3371831,23.7826658 Z" id="Fill-200" sketch:type="MSShapeGroup"></path>
+            <path d="M3.04309579,10.1578404 L3.04309579,8.37998277 L3.04309579,7.54869025 L3.04309579,6.79997279 L3.04309579,5.96868027 L3.04309579,5.63727166 C3.04309579,5.49650612 3.05257931,5.35795737 3.06429425,5.21996281 L3.21324138,4.38867029 C3.76384368,2.3436907 5.62875096,0.831292517 7.84510651,0.831292517 C10.0609042,0.831292517 11.9263693,2.3436907 12.4775295,4.38867029 L12.6259188,5.21996281 C12.6376337,5.35795737 12.6476751,5.49650612 12.6476751,5.63727166 L12.6476751,5.96868027 L12.6476751,6.79997279 L12.6476751,7.54869025 L12.6476751,8.37998277 L12.6476751,10.1578404 C12.6476751,10.3872771 12.8351142,10.5734866 13.0660659,10.5734866 C13.2970176,10.5734866 13.4844567,10.3872771 13.4844567,10.1578404 L13.4844567,5.63727166 C13.4844567,2.52934603 10.954587,0 7.84510651,0 C4.7350682,0 2.20631418,2.52934603 2.20631418,5.63727166 L2.20631418,10.1578404 C2.20631418,10.3872771 2.39375326,10.5734866 2.62470498,10.5734866 C2.8556567,10.5734866 3.04309579,10.3872771 3.04309579,10.1578404 Z" id="Path" sketch:type="MSShapeGroup"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/voip-chevron.svg b/src/skins/vector/img/voip-chevron.svg
new file mode 100644
index 0000000000..5f7cbe7153
--- /dev/null
+++ b/src/skins/vector/img/voip-chevron.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="22px" height="17px" viewBox="-1 -1 22 17" 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.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
+    <title>Triangle 1</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="05-Voice-and-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="05_2-Video-call" sketch:type="MSArtboardGroup" transform="translate(-912.000000, -693.000000)" fill="#76CFA6">
+            <polygon id="Triangle-1" sketch:type="MSShapeGroup" transform="translate(922.000000, 700.500000) scale(1, -1) translate(-922.000000, -700.500000) " points="922 693 932 708 912 708 "></polygon>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/src/skins/vector/img/voip-mute.png b/src/skins/vector/img/voip-mute.png
new file mode 100644
index 0000000000..a16d1001e5
Binary files /dev/null and b/src/skins/vector/img/voip-mute.png differ
diff --git a/src/skins/vector/img/warning.svg b/src/skins/vector/img/warning.svg
new file mode 100644
index 0000000000..b9a96a88e5
--- /dev/null
+++ b/src/skins/vector/img/warning.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.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 24 23" style="enable-background:new 0 0 24 23;" xml:space="preserve">
+<style type="text/css">
+	.st0{clip-path:url(#SVGID_2_);fill:#FF0064;}
+	.st1{clip-path:url(#SVGID_4_);fill:#FFFFFF;}
+</style>
+<g>
+	<g>
+		<defs>
+			<path id="SVGID_1_" d="M9.2,2.2c1.6-2.9,4.1-2.9,5.7,0l8.3,15.4c1.6,2.9,0.2,5.3-3.2,5.3H4c-3.3,0-4.7-2.4-3.2-5.3L9.2,2.2
+				L9.2,2.2z M9.2,2.2"/>
+		</defs>
+		<clipPath id="SVGID_2_">
+			<use xlink:href="#SVGID_1_"  style="overflow:visible;"/>
+		</clipPath>
+		<rect x="-4.8" y="-5" class="st0" width="33.6" height="32.9"/>
+	</g>
+	<g>
+		<defs>
+			<path id="SVGID_3_" d="M12.7,15L13,5.4H11l0.3,9.6H12.7L12.7,15z M12,19.1c0.7,0,1.2-0.5,1.2-1.2c0-0.7-0.5-1.2-1.2-1.2
+				c-0.7,0-1.2,0.5-1.2,1.2C10.8,18.6,11.3,19.1,12,19.1L12,19.1L12,19.1z M12,19.1"/>
+		</defs>
+		<clipPath id="SVGID_4_">
+			<use xlink:href="#SVGID_3_"  style="overflow:visible;"/>
+		</clipPath>
+		<rect x="5.8" y="0.4" class="st1" width="12.4" height="23.7"/>
+	</g>
+</g>
+</svg>
diff --git a/src/skins/vector/skindex.js b/src/skins/vector/skindex.js
deleted file mode 100644
index cf279c872d..0000000000
--- a/src/skins/vector/skindex.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-/*
- * THIS FILE IS AUTO-GENERATED
- * You can edit it you like, but your changes will be overwritten,
- * so you'd just be trying to swim upstream like a salmon.
- * You are not a salmon.
- */
-
-var skin = {};
-
-skin['atoms.EditableText'] = require('./views/atoms/EditableText');
-skin['atoms.EnableNotificationsButton'] = require('./views/atoms/EnableNotificationsButton');
-skin['atoms.ImageView'] = require('./views/atoms/ImageView');
-skin['atoms.LogoutButton'] = require('./views/atoms/LogoutButton');
-skin['atoms.MemberAvatar'] = require('./views/atoms/MemberAvatar');
-skin['atoms.MessageTimestamp'] = require('./views/atoms/MessageTimestamp');
-skin['atoms.RoomAvatar'] = require('./views/atoms/RoomAvatar');
-skin['atoms.Spinner'] = require('./views/atoms/Spinner');
-skin['atoms.create_room.CreateRoomButton'] = require('./views/atoms/create_room/CreateRoomButton');
-skin['atoms.create_room.Presets'] = require('./views/atoms/create_room/Presets');
-skin['atoms.create_room.RoomAlias'] = require('./views/atoms/create_room/RoomAlias');
-skin['atoms.voip.VideoFeed'] = require('./views/atoms/voip/VideoFeed');
-skin['molecules.BottomLeftMenu'] = require('./views/molecules/BottomLeftMenu');
-skin['molecules.BottomLeftMenuTile'] = require('./views/molecules/BottomLeftMenuTile');
-skin['molecules.ChangeAvatar'] = require('./views/molecules/ChangeAvatar');
-skin['molecules.ChangeDisplayName'] = require('./views/molecules/ChangeDisplayName');
-skin['molecules.ChangePassword'] = require('./views/molecules/ChangePassword');
-skin['molecules.DateSeparator'] = require('./views/molecules/DateSeparator');
-skin['molecules.EventAsTextTile'] = require('./views/molecules/EventAsTextTile');
-skin['molecules.EventTile'] = require('./views/molecules/EventTile');
-skin['molecules.MEmoteTile'] = require('./views/molecules/MEmoteTile');
-skin['molecules.MFileTile'] = require('./views/molecules/MFileTile');
-skin['molecules.MImageTile'] = require('./views/molecules/MImageTile');
-skin['molecules.MNoticeTile'] = require('./views/molecules/MNoticeTile');
-skin['molecules.MRoomMemberTile'] = require('./views/molecules/MRoomMemberTile');
-skin['molecules.MTextTile'] = require('./views/molecules/MTextTile');
-skin['molecules.MatrixToolbar'] = require('./views/molecules/MatrixToolbar');
-skin['molecules.MemberInfo'] = require('./views/molecules/MemberInfo');
-skin['molecules.MemberTile'] = require('./views/molecules/MemberTile');
-skin['molecules.MessageComposer'] = require('./views/molecules/MessageComposer');
-skin['molecules.MessageContextMenu'] = require('./views/molecules/MessageContextMenu');
-skin['molecules.MessageTile'] = require('./views/molecules/MessageTile');
-skin['molecules.ProgressBar'] = require('./views/molecules/ProgressBar');
-skin['molecules.RoomCreate'] = require('./views/molecules/RoomCreate');
-skin['molecules.RoomDropTarget'] = require('./views/molecules/RoomDropTarget');
-skin['molecules.RoomHeader'] = require('./views/molecules/RoomHeader');
-skin['molecules.RoomSettings'] = require('./views/molecules/RoomSettings');
-skin['molecules.RoomTile'] = require('./views/molecules/RoomTile');
-skin['molecules.RoomTooltip'] = require('./views/molecules/RoomTooltip');
-skin['molecules.SearchBar'] = require('./views/molecules/SearchBar');
-skin['molecules.SenderProfile'] = require('./views/molecules/SenderProfile');
-skin['molecules.ServerConfig'] = require('./views/molecules/ServerConfig');
-skin['molecules.UnknownMessageTile'] = require('./views/molecules/UnknownMessageTile');
-skin['molecules.UserSelector'] = require('./views/molecules/UserSelector');
-skin['molecules.voip.CallView'] = require('./views/molecules/voip/CallView');
-skin['molecules.voip.IncomingCallBox'] = require('./views/molecules/voip/IncomingCallBox');
-skin['molecules.voip.VideoView'] = require('./views/molecules/voip/VideoView');
-skin['organisms.CasLogin'] = require('./views/organisms/CasLogin');
-skin['organisms.CreateRoom'] = require('./views/organisms/CreateRoom');
-skin['organisms.ErrorDialog'] = require('./views/organisms/ErrorDialog');
-skin['organisms.LeftPanel'] = require('./views/organisms/LeftPanel');
-skin['organisms.LogoutPrompt'] = require('./views/organisms/LogoutPrompt');
-skin['organisms.MemberList'] = require('./views/organisms/MemberList');
-skin['organisms.Notifier'] = require('./views/organisms/Notifier');
-skin['organisms.QuestionDialog'] = require('./views/organisms/QuestionDialog');
-skin['organisms.RightPanel'] = require('./views/organisms/RightPanel');
-skin['organisms.RoomDirectory'] = require('./views/organisms/RoomDirectory');
-skin['organisms.RoomList'] = require('./views/organisms/RoomList');
-skin['organisms.RoomSubList'] = require('./views/organisms/RoomSubList');
-skin['organisms.RoomView'] = require('./views/organisms/RoomView');
-skin['organisms.UserSettings'] = require('./views/organisms/UserSettings');
-skin['organisms.ViewSource'] = require('./views/organisms/ViewSource');
-skin['pages.CompatibilityPage'] = require('./views/pages/CompatibilityPage');
-skin['pages.MatrixChat'] = require('./views/pages/MatrixChat');
-skin['templates.Login'] = require('./views/templates/Login');
-skin['templates.Register'] = require('./views/templates/Register');
-
-module.exports = skin;
\ No newline at end of file
diff --git a/src/skins/vector/skinfo.json b/src/skins/vector/skinfo.json
deleted file mode 100644
index 287ff9e237..0000000000
--- a/src/skins/vector/skinfo.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "baseSkin": ""
-}
diff --git a/src/skins/vector/views/atoms/EditableText.js b/src/skins/vector/views/atoms/EditableText.js
deleted file mode 100644
index 1848b029a6..0000000000
--- a/src/skins/vector/views/atoms/EditableText.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var EditableTextController = require('matrix-react-sdk/lib/controllers/atoms/EditableText')
-
-module.exports = React.createClass({
-    displayName: 'EditableText',
-    mixins: [EditableTextController],
-
-    onKeyUp: function(ev) {
-        if (ev.key == "Enter") {
-            this.onFinish(ev);
-        } else if (ev.key == "Escape") {
-            this.cancelEdit();
-        }
-    },
-
-    onClickDiv: function() {
-        this.setState({
-            phase: this.Phases.Edit,
-        })
-    },
-
-    onFocus: function(ev) {
-        ev.target.setSelectionRange(0, ev.target.value.length);
-    },
-
-    onFinish: function(ev) {
-        if (ev.target.value) {
-            this.setValue(ev.target.value, ev.key === "Enter");
-        } else {
-            this.cancelEdit();
-        }
-    },
-
-    render: function() {
-        var editable_el;
-
-        if (this.state.phase == this.Phases.Display) {
-            if (this.state.value) {
-                editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.state.value}</div>;
-            } else {
-                editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.props.label}</div>;
-            }
-        } else if (this.state.phase == this.Phases.Edit) {
-            editable_el = (
-                <div>
-                    <input type="text" defaultValue={this.state.value} onKeyUp={this.onKeyUp} onFocus={this.onFocus} onBlur={this.onFinish} placeholder={this.props.placeHolder} autoFocus/>
-                </div>
-            );
-        }
-
-        return (
-            <div className="mx_EditableText">
-                {editable_el}
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/atoms/EnableNotificationsButton.js b/src/skins/vector/views/atoms/EnableNotificationsButton.js
deleted file mode 100644
index edef9edc68..0000000000
--- a/src/skins/vector/views/atoms/EnableNotificationsButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var EnableNotificationsButtonController = require('matrix-react-sdk/lib/controllers/atoms/EnableNotificationsButton')
-
-module.exports = React.createClass({
-    displayName: 'EnableNotificationsButton',
-    mixins: [EnableNotificationsButtonController],
-
-    render: function() {
-        if (this.enabled()) {
-            return (
-                <button className="mx_EnableNotificationsButton" onClick={this.onClick}>Disable Notifications</button>
-            );
-        } else {
-            return (
-                <button className="mx_EnableNotificationsButton" onClick={this.onClick}>Enable Notifications</button>
-            );
-        }
-    }
-});
diff --git a/src/skins/vector/views/atoms/LogoutButton.js b/src/skins/vector/views/atoms/LogoutButton.js
deleted file mode 100644
index 619160f61c..0000000000
--- a/src/skins/vector/views/atoms/LogoutButton.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var LogoutButtonController = require('matrix-react-sdk/lib/controllers/atoms/LogoutButton')
-
-module.exports = React.createClass({
-    displayName: 'LogoutButton',
-    mixins: [LogoutButtonController],
-
-    render: function() {
-        return (
-            <button className="mx_LogoutButton" onClick={this.onClick}>Sign out</button>
-        );
-    }
-});
diff --git a/src/skins/vector/views/atoms/MemberAvatar.js b/src/skins/vector/views/atoms/MemberAvatar.js
deleted file mode 100644
index c8606cd72e..0000000000
--- a/src/skins/vector/views/atoms/MemberAvatar.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var Avatar = require('../../../../Avatar');
-
-var MemberAvatarController = require('matrix-react-sdk/lib/controllers/atoms/MemberAvatar')
-
-module.exports = React.createClass({
-    displayName: 'MemberAvatar',
-    mixins: [MemberAvatarController],
-
-    avatarUrlForMember: function(member) {
-        return Avatar.avatarUrlForMember(
-            member,
-            this.props.member,
-            this.props.width,
-            this.props.height,
-            this.props.resizeMethod
-        );
-    },
-
-    skinnedDefaultAvatarUrl: function(member, width, height, resizeMethod) {
-        return Avatar.defaultAvatarUrlForString(member.userId);
-    },
-
-    render: function() {
-        // XXX: recalculates default avatar url constantly
-        if (this.state.imageUrl === this.defaultAvatarUrl(this.props.member)) {
-            var initial;
-            if (this.props.member.name[0])
-                initial = this.props.member.name[0].toUpperCase();
-            if (initial === '@' && this.props.member.name[1])
-                initial = this.props.member.name[1].toUpperCase();
-         
-            return (
-                <span className="mx_MemberAvatar">
-                    <span className="mx_MemberAvatar_initial" aria-hidden="true"
-                          style={{ fontSize: (this.props.width * 0.75) + "px",
-                                   width: this.props.width + "px",
-                                   lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>
-                    <img className="mx_MemberAvatar_image" src={this.state.imageUrl}
-                         onError={this.onError} width={this.props.width} height={this.props.height} />
-                </span>
-            );            
-        }
-        return (
-            <img className="mx_MemberAvatar mx_MemberAvatar_image" src={this.state.imageUrl}
-                onError={this.onError}
-                width={this.props.width} height={this.props.height} />
-        );
-    }
-});
diff --git a/src/skins/vector/views/atoms/RoomAvatar.js b/src/skins/vector/views/atoms/RoomAvatar.js
deleted file mode 100644
index bdd28bad59..0000000000
--- a/src/skins/vector/views/atoms/RoomAvatar.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var RoomAvatarController = require('matrix-react-sdk/lib/controllers/atoms/RoomAvatar')
-
-module.exports = React.createClass({
-    displayName: 'RoomAvatar',
-    mixins: [RoomAvatarController],
-
-    getUrlList: function() {
-        return [
-            this.roomAvatarUrl(),
-            this.getOneToOneAvatar(),
-            this.getFallbackAvatar()
-        ];
-    },
-
-    getFallbackAvatar: function() {
-        var images = [ '76cfa6', '50e2c2', 'f4c371' ];
-        var total = 0;
-        for (var i = 0; i < this.props.room.roomId.length; ++i) {
-            total += this.props.room.roomId.charCodeAt(i);
-        }
-        return 'img/' + images[total % images.length] + '.png';
-    },
-
-    render: function() {
-        var style = {
-            width: this.props.width,
-            height: this.props.height,
-        };
-
-        // XXX: recalculates fallback avatar constantly
-        if (this.state.imageUrl === this.getFallbackAvatar()) {
-            var initial;
-            if (this.props.room.name[0])
-                initial = this.props.room.name[0].toUpperCase();
-            if ((initial === '@' || initial === '#') && this.props.room.name[1])
-                initial = this.props.room.name[1].toUpperCase();
-         
-            return (
-                <span>
-                    <span className="mx_RoomAvatar_initial" aria-hidden="true"
-                          style={{ fontSize: (this.props.width * 0.75) + "px",
-                                   width: this.props.width + "px",
-                                   lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>
-                    <img className="mx_RoomAvatar" src={this.state.imageUrl}
-                            onError={this.onError} style={style} />
-                </span>
-            );
-        }
-        else {
-            return <img className="mx_RoomAvatar" src={this.state.imageUrl}
-                        onError={this.onError} style={style} />
-        }
-
-    }
-});
diff --git a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js b/src/skins/vector/views/atoms/create_room/CreateRoomButton.js
deleted file mode 100644
index 2fc9d057a0..0000000000
--- a/src/skins/vector/views/atoms/create_room/CreateRoomButton.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var CreateRoomButtonController = require('matrix-react-sdk/lib/controllers/atoms/create_room/CreateRoomButton')
-
-module.exports = React.createClass({
-    displayName: 'CreateRoomButton',
-    mixins: [CreateRoomButtonController],
-
-    render: function() {
-        return (
-            <button className="mx_CreateRoomButton" onClick={this.onClick}>Create Room</button>
-        );
-    }
-});
diff --git a/src/skins/vector/views/atoms/create_room/Presets.js b/src/skins/vector/views/atoms/create_room/Presets.js
deleted file mode 100644
index a098a7d7e8..0000000000
--- a/src/skins/vector/views/atoms/create_room/Presets.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var PresetsController = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets')
-
-module.exports = React.createClass({
-    displayName: 'CreateRoomPresets',
-    mixins: [PresetsController],
-
-    onValueChanged: function(ev) {
-        this.props.onChange(ev.target.value)
-    },
-
-    render: function() {
-        return (
-            <select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
-                <option value={this.Presets.PrivateChat}>Private Chat</option>
-                <option value={this.Presets.PublicChat}>Public Chat</option>
-                <option value={this.Presets.Custom}>Custom</option>
-            </select>
-        );
-    }
-});
diff --git a/src/skins/vector/views/atoms/create_room/RoomAlias.js b/src/skins/vector/views/atoms/create_room/RoomAlias.js
deleted file mode 100644
index 0a8cadc888..0000000000
--- a/src/skins/vector/views/atoms/create_room/RoomAlias.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var RoomAliasController = require('matrix-react-sdk/lib/controllers/atoms/create_room/RoomAlias')
-
-module.exports = React.createClass({
-    displayName: 'RoomAlias',
-    mixins: [RoomAliasController],
-
-    onValueChanged: function(ev) {
-        this.props.onChange(ev.target.value);
-    },
-
-    onFocus: function(ev) {
-        var target = ev.target;
-        var curr_val = ev.target.value;
-
-        if (this.props.homeserver) {
-            if (curr_val == "") {
-                setTimeout(function() {
-                    target.value = "#:" + this.props.homeserver;
-                    target.setSelectionRange(1, 1);
-                }, 0);
-            } else {
-                var suffix = ":" + this.props.homeserver;
-                setTimeout(function() {
-                    target.setSelectionRange(
-                        curr_val.startsWith("#") ? 1 : 0,
-                        curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
-                    );
-                }, 0);
-            }
-        }
-    },
-
-    onBlur: function(ev) {
-        var curr_val = ev.target.value;
-
-        if (this.props.homeserver) {
-            if (curr_val == "#:" + this.props.homeserver) {
-                ev.target.value = "";
-                return;
-            }
-
-            if (curr_val != "") {
-                var new_val = ev.target.value;
-                var suffix = ":" + this.props.homeserver;
-                if (!curr_val.startsWith("#")) new_val = "#" + new_val;
-                if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
-                ev.target.value = new_val;
-            }
-        }
-    },
-
-    render: function() {
-        return (
-            <input type="text" className="mx_RoomAlias" placeholder="Alias (optional)"
-                onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
-                value={this.props.alias}/>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/ChangeAvatar.js b/src/skins/vector/views/molecules/ChangeAvatar.js
deleted file mode 100644
index 7afac77fd5..0000000000
--- a/src/skins/vector/views/molecules/ChangeAvatar.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var sdk = require('matrix-react-sdk')
-var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
-
-module.exports = React.createClass({
-    displayName: 'ChangeAvatar',
-    mixins: [ChangeAvatarController],
-
-    onFileSelected: function(ev) {
-        this.avatarSet = true;
-        this.setAvatarFromFile(ev.target.files[0]);
-    },
-
-    onError: function(error) {
-        this.setState({
-            errorText: "Failed to upload profile picture!"
-        });
-    },
-
-    render: function() {
-        var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
-        var avatarImg;
-        // Having just set an avatar we just display that since it will take a little
-        // time to propagate through to the RoomAvatar.
-        if (this.props.room && !this.avatarSet) {
-            avatarImg = <RoomAvatar room={this.props.room} width='320' height='240' resizeMethod='scale' />;
-        } else {
-            var style = {
-                maxWidth: 320,
-                maxHeight: 240,
-            };
-            avatarImg = <img src={this.state.avatarUrl} style={style} />;
-        }
-
-        switch (this.state.phase) {
-            case this.Phases.Display:
-            case this.Phases.Error:
-                return (
-                    <div>
-                        <div className="mx_Dialog_content">
-                            {avatarImg}
-                        </div>
-                        <div className="mx_Dialog_content">
-                            Upload new:
-                            <input type="file" onChange={this.onFileSelected}/>
-                            {this.state.errorText}
-                        </div>    
-                    </div>
-                );
-            case this.Phases.Uploading:
-                var Loader = sdk.getComponent("atoms.Spinner");
-                return (
-                    <Loader />
-                );
-        }
-    }
-});
diff --git a/src/skins/vector/views/molecules/ChangeDisplayName.js b/src/skins/vector/views/molecules/ChangeDisplayName.js
deleted file mode 100644
index a10ba2a754..0000000000
--- a/src/skins/vector/views/molecules/ChangeDisplayName.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sdk = require('matrix-react-sdk');
-
-var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName");
-
-module.exports = React.createClass({
-    displayName: 'ChangeDisplayName',
-    mixins: [ChangeDisplayNameController],
-
-    edit: function() {
-        this.refs.displayname_edit.edit()
-    },
-
-    onValueChanged: function(new_value, shouldSubmit) {
-        if (shouldSubmit) {
-            this.changeDisplayname(new_value);
-        }
-    },
-
-    render: function() {
-        if (this.state.busy) {
-            var Loader = sdk.getComponent("atoms.Spinner");
-            return (
-                <Loader />
-            );
-        } else if (this.state.errorString) {
-            return (
-                <div className="error">{this.state.errorString}</div>
-            );
-        } else {
-            var EditableText = sdk.getComponent('atoms.EditableText');
-            return (
-                <EditableText ref="displayname_edit" initialValue={this.state.displayName} label="Click to set display name." onValueChanged={this.onValueChanged}/>
-            );
-        }
-    }
-});
diff --git a/src/skins/vector/views/molecules/ChangePassword.js b/src/skins/vector/views/molecules/ChangePassword.js
deleted file mode 100644
index b1d8f28e6f..0000000000
--- a/src/skins/vector/views/molecules/ChangePassword.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var ChangePasswordController = require('matrix-react-sdk/lib/controllers/molecules/ChangePassword')
-
-module.exports = React.createClass({
-    displayName: 'ChangePassword',
-    mixins: [ChangePasswordController],
-
-    onClickChange: function() {
-        var old_password = this.refs.old_input.value;
-        var new_password = this.refs.new_input.value;
-        var confirm_password = this.refs.confirm_input.value;
-        if (new_password != confirm_password) {
-            this.setState({
-                state: this.Phases.Error,
-                errorString: "Passwords don't match"
-            });
-        } else if (new_password == '' || old_password == '') {
-            this.setState({
-                state: this.Phases.Error,
-                errorString: "Passwords can't be empty"
-            });
-        } else {
-            this.changePassword(old_password, new_password);
-        }
-    },
-
-    render: function() {
-        switch (this.state.phase) {
-            case this.Phases.Edit:
-            case this.Phases.Error:
-                return (
-                    <div>
-                        <div className="mx_Dialog_content">
-                            <div>{this.state.errorString}</div>
-                            <div><label>Old password <input type="password" ref="old_input"/></label></div>
-                            <div><label>New password <input type="password" ref="new_input"/></label></div>
-                            <div><label>Confirm password <input type="password" ref="confirm_input"/></label></div>
-                        </div>
-                        <div className="mx_Dialog_buttons">
-                            <button onClick={this.onClickChange}>Change Password</button>
-                            <button onClick={this.props.onFinished}>Cancel</button>
-                        </div>
-                    </div>
-                );
-            case this.Phases.Uploading:
-                var Loader = sdk.getComponent("atoms.Spinner");
-                return (
-                    <div className="mx_Dialog_content">
-                        <Loader />
-                    </div>
-                );
-            case this.Phases.Success:
-                return (
-                    <div>
-                        <div className="mx_Dialog_content">
-                            Success!
-                        </div>
-                        <div className="mx_Dialog_buttons">
-                            <button onClick={this.props.onFinished}>Ok</button>
-                        </div>
-                    </div>
-                )
-        }
-    }
-});
diff --git a/src/skins/vector/views/molecules/EventTile.js b/src/skins/vector/views/molecules/EventTile.js
deleted file mode 100644
index c5cb81951b..0000000000
--- a/src/skins/vector/views/molecules/EventTile.js
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var classNames = require("classnames");
-
-var sdk = require('matrix-react-sdk')
-
-var EventTileController = require('matrix-react-sdk/lib/controllers/molecules/EventTile')
-var ContextualMenu = require('../../../../ContextualMenu');
-
-var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
-
-var eventTileTypes = {
-    'm.room.message': 'molecules.MessageTile',
-    'm.room.member' : 'molecules.EventAsTextTile',
-    'm.call.invite' : 'molecules.EventAsTextTile',
-    'm.call.answer' : 'molecules.EventAsTextTile',
-    'm.call.hangup' : 'molecules.EventAsTextTile',
-    'm.room.name'   : 'molecules.EventAsTextTile',
-    'm.room.topic'  : 'molecules.EventAsTextTile',
-};
-
-module.exports = React.createClass({
-    displayName: 'EventTile',
-    mixins: [EventTileController],
-
-    statics: {
-        haveTileForEvent: function(e) {
-            if (eventTileTypes[e.getType()] == undefined) return false;
-            if (eventTileTypes[e.getType()] == 'molecules.EventAsTextTile') {
-                return TextForEvent.textForEvent(e) !== '';
-            } else {
-                return true;
-            }
-        }
-    },
-
-    getInitialState: function() {
-        return {menu: false};
-    },
-
-    onEditClicked: function(e) {
-        var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu');
-        var buttonRect = e.target.getBoundingClientRect()
-        var x = buttonRect.right;
-        var y = buttonRect.top + (e.target.height / 2);
-        var self = this;
-        ContextualMenu.createMenu(MessageContextMenu, {
-            mxEvent: this.props.mxEvent,
-            left: x,
-            top: y,
-            onFinished: function() {
-                self.setState({menu: false});
-            }
-        });
-        this.setState({menu: true});
-    },
-
-    render: function() {
-        var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
-        var SenderProfile = sdk.getComponent('molecules.SenderProfile');
-        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
-
-        var content = this.props.mxEvent.getContent();
-        var msgtype = content.msgtype;
-
-        var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]);
-        // This shouldn't happen: the caller should check we support this type
-        // before trying to instantiate us
-        if (!EventTileType) {
-            throw new Error("Event type not supported");
-        }
-
-        var classes = classNames({
-            mx_EventTile: true,
-            mx_EventTile_sending: ['sending', 'queued'].indexOf(
-                this.props.mxEvent.status
-            ) !== -1,
-            mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent',
-            mx_EventTile_highlight: this.shouldHighlight(),
-            mx_EventTile_continuation: this.props.continuation,
-            mx_EventTile_last: this.props.last,
-            mx_EventTile_contextual: this.props.contextual,
-            menu: this.state.menu,
-        });
-        var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
-        var editButton = (
-            <input
-                type="image" src="img/edit.png" alt="Edit" width="14" height="14"
-                className="mx_EventTile_editButton" onClick={this.onEditClicked}
-            />
-        );
-
-        var aux = null;
-        if (msgtype === 'm.image') aux = "sent an image";
-        else if (msgtype === 'm.video') aux = "sent a video";
-        else if (msgtype === 'm.file') aux = "uploaded a file";
-
-        var avatar, sender;
-        if (!this.props.continuation) {
-            if (this.props.mxEvent.sender) {
-                avatar = (
-                    <div className="mx_EventTile_avatar">
-                        <MemberAvatar member={this.props.mxEvent.sender} width={24} height={24} />
-                    </div>
-                );
-            }
-            if (EventTileType.needsSenderProfile()) {
-                sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
-            }
-        }
-        return (
-            <div className={classes}>
-                { avatar }
-                { sender }
-                <div className="mx_EventTile_line">
-                    { timestamp }
-                    { editButton }
-                    <EventTileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />
-                </div>
-            </div>
-        );
-    },
-});
diff --git a/src/skins/vector/views/molecules/MFileTile.js b/src/skins/vector/views/molecules/MFileTile.js
deleted file mode 100644
index 9180bd6b83..0000000000
--- a/src/skins/vector/views/molecules/MFileTile.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var MFileTileController = require('matrix-react-sdk/lib/controllers/molecules/MFileTile')
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-
-module.exports = React.createClass({
-    displayName: 'MFileTile',
-    mixins: [MFileTileController],
-
-    render: function() {
-        var content = this.props.mxEvent.getContent();
-        var cli = MatrixClientPeg.get();
-
-        var httpUrl = cli.mxcUrlToHttp(content.url);
-        var text = this.presentableTextForFile(content);
-
-        if (httpUrl) {
-            return (
-                <span className="mx_MFileTile">
-                    <div className="mx_MImageTile_download">
-                        <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
-                            <img src="img/download.png" width="10" height="12"/>
-                            Download {text}
-                        </a>
-                    </div>
-                </span>
-            );
-        } else {
-            var extra = text ? ': '+text : '';
-            return <span className="mx_MFileTile">
-                Invalid file{extra}
-            </span>
-        }
-    },
-});
diff --git a/src/skins/vector/views/molecules/MImageTile.js b/src/skins/vector/views/molecules/MImageTile.js
deleted file mode 100644
index 2f3b7a55db..0000000000
--- a/src/skins/vector/views/molecules/MImageTile.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var filesize = require('filesize');
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var Modal = require('matrix-react-sdk/lib/Modal');
-var sdk = require('matrix-react-sdk')
-
-module.exports = React.createClass({
-    displayName: 'MImageTile',
-
-    thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
-        if (!fullWidth || !fullHeight) {
-            // Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
-            // log this because it's spammy
-            return undefined;
-        }
-        if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
-            // no scaling needs to be applied
-            return fullHeight;
-        }
-        var widthMulti = thumbWidth / fullWidth;
-        var heightMulti = thumbHeight / fullHeight;
-        if (widthMulti < heightMulti) {
-            // width is the dominant dimension so scaling will be fixed on that
-            return Math.floor(widthMulti * fullHeight);
-        }
-        else {
-            // height is the dominant dimension so scaling will be fixed on that
-            return Math.floor(heightMulti * fullHeight);
-        }
-    },
-
-    onClick: function(ev) {
-        if (ev.button == 0 && !ev.metaKey) {
-            ev.preventDefault();
-            var content = this.props.mxEvent.getContent();
-            var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(content.url);
-            var ImageView = sdk.getComponent("atoms.ImageView");
-            Modal.createDialog(ImageView, {
-                src: httpUrl,
-                width: content.info.w,
-                height: content.info.h,
-                mxEvent: this.props.mxEvent,
-            }, "mx_Dialog_lightbox");
-        }
-    },
-
-    render: function() {
-        var content = this.props.mxEvent.getContent();
-        var cli = MatrixClientPeg.get();
-
-        var thumbHeight = null;
-        if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 480, 360);
-
-        var imgStyle = {};
-        if (thumbHeight) imgStyle['height'] = thumbHeight;
-
-        var thumbUrl = cli.mxcUrlToHttp(content.url, 480, 360);
-        if (thumbUrl) {
-            return (
-                <span className="mx_MImageTile">
-                    <a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
-                        <img className="mx_MImageTile_thumbnail" src={thumbUrl} alt={content.body} style={imgStyle} />
-                    </a>
-                    <div className="mx_MImageTile_download">
-                        <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
-                            <img src="img/download.png" width="10" height="12"/>
-                            Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
-                        </a>
-                    </div>
-                </span>
-            );
-        } else if (content.body) {
-            return (
-                <span className="mx_MImageTile">
-                    Image '{content.body}' cannot be displayed.
-                </span>
-            );
-        } else {
-            return (
-                <span className="mx_MImageTile">
-                    This image cannot be displayed.
-                </span>
-            );
-        }
-    },
-});
diff --git a/src/skins/vector/views/molecules/MNoticeTile.js b/src/skins/vector/views/molecules/MNoticeTile.js
deleted file mode 100644
index a0cedb1ddd..0000000000
--- a/src/skins/vector/views/molecules/MNoticeTile.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sanitizeHtml = require('sanitize-html');
-
-var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile')
-
-var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
-allowedAttributes['font'] = ['color'];
-var sanitizeHtmlParams = {
-    allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
-    allowedAttributes: allowedAttributes,
-};
-
-module.exports = React.createClass({
-    displayName: 'MNoticeTile',
-    mixins: [MNoticeTileController],
-
-    // FIXME: this entire class is copy-pasted from MTextTile :(        
-    render: function() {
-        var content = this.props.mxEvent.getContent();
-        var originalBody = content.body;
-        var body;
-
-        if (this.props.searchTerm) {
-            var lastOffset = 0;
-            var bodyList = [];
-            var k = 0;
-            var offset;
-
-            // XXX: rather than searching for the search term in the body,
-            // we should be looking at the match delimiters returned by the FTS engine
-            if (content.format === "org.matrix.custom.html") {
-                var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
-                var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
-                while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
-                    // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
-                    // hooking into the sanitizer parser rather than treating it as a string.  Otherwise
-                    // the act of highlighting a <b/> or whatever will break the HTML badly.
-                    bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
-                    bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
-                    lastOffset = offset + safeSearchTerm.length;
-                }
-                bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
-            }
-            else {
-                while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
-                    bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
-                    bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
-                    lastOffset = offset + this.props.searchTerm.length;
-                }
-                bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
-            }
-            body = bodyList;
-        }
-        else {
-            if (content.format === "org.matrix.custom.html") {
-                var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
-                body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
-            }
-            else {
-                body = originalBody;
-            }
-        }
-
-        return (
-            <span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
-                { body }
-            </span>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/molecules/MRoomMemberTile.js b/src/skins/vector/views/molecules/MRoomMemberTile.js
deleted file mode 100644
index 0048306d39..0000000000
--- a/src/skins/vector/views/molecules/MRoomMemberTile.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var sdk = require('matrix-react-sdk')
-var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
-
-module.exports = React.createClass({
-    displayName: 'MRoomMemberTile',
-
-    getMemberEventText: function() {
-        return TextForEvent.textForEvent(this.props.mxEvent);
-    },
-
-    render: function() {
-        // XXX: for now, just cheekily borrow the css from message tile...
-        var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
-        var text = this.getMemberEventText();
-        if (!text) return <div/>;
-        var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
-        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
-        return (
-            <div className="mx_MessageTile mx_MessageTile_notice">
-                <div className="mx_MessageTile_avatar">
-                    <MemberAvatar member={this.props.mxEvent.sender} />
-                </div>            
-                { timestamp }
-                <span className="mx_SenderProfile"></span>
-                <span className="mx_MessageTile_content">
-                    { text }
-                </span>
-            </div>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/molecules/MTextTile.js b/src/skins/vector/views/molecules/MTextTile.js
deleted file mode 100644
index 12bafa37ba..0000000000
--- a/src/skins/vector/views/molecules/MTextTile.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sanitizeHtml = require('sanitize-html');
-
-var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile')
-
-var allowedAttributes = sanitizeHtml.defaults.allowedAttributes;
-allowedAttributes['font'] = ['color'];
-var sanitizeHtmlParams = {
-    allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]),
-    allowedAttributes: allowedAttributes,
-};
-
-module.exports = React.createClass({
-    displayName: 'MTextTile',
-    mixins: [MTextTileController],
-
-    // FIXME: this entire class is copy-pasted from MTextTile :(        
-    render: function() {
-        var content = this.props.mxEvent.getContent();
-        var originalBody = content.body;
-        var body;
-
-        if (this.props.searchTerm) {
-            var lastOffset = 0;
-            var bodyList = [];
-            var k = 0;
-            var offset;
-
-            // XXX: rather than searching for the search term in the body,
-            // we should be looking at the match delimiters returned by the FTS engine
-            if (content.format === "org.matrix.custom.html") {
-                var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
-                var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams);
-                while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
-                    // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
-                    // hooking into the sanitizer parser rather than treating it as a string.  Otherwise
-                    // the act of highlighting a <b/> or whatever will break the HTML badly.
-                    bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
-                    bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
-                    lastOffset = offset + safeSearchTerm.length;
-                }
-                bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
-            }
-            else {
-                while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) {
-                    bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
-                    bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ this.props.searchTerm }</span>);
-                    lastOffset = offset + this.props.searchTerm.length;
-                }
-                bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
-            }
-            body = bodyList;
-        }
-        else {
-            if (content.format === "org.matrix.custom.html") {
-                var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
-                body = <span dangerouslySetInnerHTML={{ __html: safeBody }} />;
-            }
-            else {
-                body = originalBody;
-            }
-        }
-
-        return (
-            <span ref="content" className="mx_MTextTile mx_MessageTile_content">
-                { body }
-            </span>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/molecules/MemberInfo.js b/src/skins/vector/views/molecules/MemberInfo.js
deleted file mode 100644
index 24fa1e91a4..0000000000
--- a/src/skins/vector/views/molecules/MemberInfo.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher');
-var MemberInfoController = require('matrix-react-sdk/lib/controllers/molecules/MemberInfo')
-
-// FIXME: this should probably be an organism, to match with MemberList, not a molecule
-
-module.exports = React.createClass({
-    displayName: 'MemberInfo',
-    mixins: [MemberInfoController],
-
-    onCancel: function(e) {
-        dis.dispatch({
-            action: "view_user",
-            member: null
-        });
-    },
-
-    render: function() {
-        var interactButton, kickButton, banButton, muteButton, giveModButton, spinner;
-        if (this.props.member.userId === MatrixClientPeg.get().credentials.userId) {
-            interactButton = <div className="mx_MemberInfo_field" onClick={this.onLeaveClick}>Leave room</div>;
-        }
-        else {
-            interactButton = <div className="mx_MemberInfo_field" onClick={this.onChatClick}>Start chat</div>;
-        }
-
-        if (this.state.creatingRoom) {
-            var Loader = sdk.getComponent("atoms.Spinner");
-            spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
-        }
-
-        if (this.state.can.kick) {
-            kickButton = <div className="mx_MemberInfo_field" onClick={this.onKick}>
-                Kick
-            </div>;
-        }
-        if (this.state.can.ban) {
-            banButton = <div className="mx_MemberInfo_field" onClick={this.onBan}>
-                Ban
-            </div>;
-        }
-        if (this.state.can.mute) {
-            var muteLabel = this.state.muted ? "Unmute" : "Mute";
-            muteButton = <div className="mx_MemberInfo_field" onClick={this.onMuteToggle}>
-                {muteLabel}
-            </div>;
-        }
-        if (this.state.can.modifyLevel) {
-            var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
-            giveModButton = <div className="mx_MemberInfo_field" onClick={this.onModToggle}>
-                {giveOpLabel}
-            </div>
-        }
-
-        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
-        return (
-            <div className="mx_MemberInfo">
-                <img className="mx_MemberInfo_cancel" src="img/cancel-black.png" width="18" height="18" onClick={this.onCancel}/>
-                <div className="mx_MemberInfo_avatar">
-                    <MemberAvatar member={this.props.member} width={48} height={48} />
-                </div>
-                <h2>{ this.props.member.name }</h2>
-                <div className="mx_MemberInfo_profileField">
-                    { this.props.member.userId }
-                </div>
-                <div className="mx_MemberInfo_profileField">
-                    power: { this.props.member.powerLevelNorm }%
-                </div>
-                <div className="mx_MemberInfo_buttons">
-                    {interactButton}
-                    {muteButton}
-                    {kickButton}
-                    {banButton}
-                    {giveModButton}
-                    {spinner}
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/MemberTile.js b/src/skins/vector/views/molecules/MemberTile.js
deleted file mode 100644
index 25ba3db94c..0000000000
--- a/src/skins/vector/views/molecules/MemberTile.js
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher');
-var MemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MemberTile')
-
-// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
-// Revert to Arial when this happens, which on OSX works at least.
-var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
-
-module.exports = React.createClass({
-    displayName: 'MemberTile',
-    mixins: [MemberTileController],
-
-    shouldComponentUpdate: function(nextProps, nextState) {
-        if (this.state.hover !== nextState.hover) return true;
-        if (
-            this.member_last_modified_time === undefined ||
-            this.member_last_modified_time < nextProps.member.getLastModifiedTime()
-        ) {
-            return true
-        }
-        if (
-            nextProps.member.user &&
-            (this.user_last_modified_time === undefined ||
-            this.user_last_modified_time < nextProps.member.user.getLastModifiedTime())
-        ) {
-            return true
-        }
-        return false;
-    },
-
-    mouseEnter: function(e) {
-        this.setState({ 'hover': true });
-    },
-
-    mouseLeave: function(e) {
-        this.setState({ 'hover': false });
-    },
-
-    onClick: function(e) {
-        dis.dispatch({
-            action: 'view_user',
-            member: this.props.member,
-        });
-    },
-
-    getDuration: function(time) {
-        if (!time) return;
-        var t = parseInt(time / 1000);
-        var s = t % 60;
-        var m = parseInt(t / 60) % 60;
-        var h = parseInt(t / (60 * 60)) % 24;
-        var d = parseInt(t / (60 * 60 * 24));
-        if (t < 60) {
-            if (t < 0) {
-                return "0s";
-            }
-            return s + "s";
-        }
-        if (t < 60 * 60) {
-            return m + "m";
-        }
-        if (t < 24 * 60 * 60) {
-            return h + "h";
-        }
-        return d + "d ";
-    },
-
-    getPrettyPresence: function(user) {
-        if (!user) return "Unknown";
-        var presence = user.presence;
-        if (presence === "online") return "Online";
-        if (presence === "unavailable") return "Idle"; // XXX: is this actually right?
-        if (presence === "offline") return "Offline";
-        return "Unknown";
-    },
-
-    getPowerLabel: function() {
-        var label = this.props.member.userId;
-        if (this.state.isTargetMod) {
-            label += " - Mod (" + this.props.member.powerLevelNorm + "%)";
-        }
-        return label;
-    },
-
-    render: function() {
-        this.member_last_modified_time = this.props.member.getLastModifiedTime();
-        if (this.props.member.user) {
-            this.user_last_modified_time = this.props.member.user.getLastModifiedTime();
-        }
-
-        var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
-
-        var power;
-        // if (this.props.member && this.props.member.powerLevelNorm > 0) {
-        //     var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
-        //     power = <img src={ img } className="mx_MemberTile_power" width="44" height="44" alt=""/>;
-        // }
-        var presenceClass = "mx_MemberTile_offline";
-        var mainClassName = "mx_MemberTile ";
-        if (this.props.member.user) {
-            if (this.props.member.user.presence === "online") {
-                presenceClass = "mx_MemberTile_online";
-            }
-            else if (this.props.member.user.presence === "unavailable") {
-                presenceClass = "mx_MemberTile_unavailable";
-            }
-        }
-        mainClassName += presenceClass;
-        if (this.state.hover) {
-            mainClassName += " mx_MemberTile_hover";
-        }
-
-        var name = this.props.member.name;
-        // if (isMyUser) name += " (me)"; // this does nothing other than introduce line wrapping and pain
-        //var leave = isMyUser ? <img className="mx_MemberTile_leave" src="img/delete.png" width="10" height="10" onClick={this.onLeaveClick}/> : null;
-
-        var nameClass = "mx_MemberTile_name";
-        if (zalgo.test(name)) {
-            nameClass += " mx_MemberTile_zalgo";
-        }
-
-        var nameEl;
-        if (this.state.hover) {
-            var presence;
-            // FIXME: make presence data update whenever User.presence changes...
-            var active = this.props.member.user ? ((Date.now() - (this.props.member.user.lastPresenceTs - this.props.member.user.lastActiveAgo)) || -1) : -1;
-            if (active >= 0) {
-                presence = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) } { this.getDuration(active) } ago</div>;
-            }
-            else {
-                presence = <div className="mx_MemberTile_presence">{ this.getPrettyPresence(this.props.member.user) }</div>;
-            }
-
-            nameEl =
-                <div className="mx_MemberTile_details">
-                    <img className="mx_MemberTile_chevron" src="img/member_chevron.png" width="8" height="12"/>
-                    <div className="mx_MemberTile_userId">{ name }</div>
-                    { presence }
-                </div>
-        }
-        else {
-            nameEl =
-                <div className={nameClass}>
-                    { name }
-                </div>
-        }
-
-        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
-        return (
-            <div className={mainClassName} title={ this.getPowerLabel() } onClick={ this.onClick } onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
-                <div className="mx_MemberTile_avatar">
-                    <MemberAvatar member={this.props.member} width={36} height={36} />
-                     { power }
-                </div>
-                { nameEl }
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/MessageComposer.js b/src/skins/vector/views/molecules/MessageComposer.js
deleted file mode 100644
index 2f0e7ac57f..0000000000
--- a/src/skins/vector/views/molecules/MessageComposer.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var MessageComposerController = require('matrix-react-sdk/lib/controllers/molecules/MessageComposer')
-
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher')
-
-module.exports = React.createClass({
-    displayName: 'MessageComposer',
-    mixins: [MessageComposerController],
-
-    onUploadClick: function(ev) {
-        this.refs.uploadInput.click();
-    },
-
-    onUploadFileSelected: function(ev) {
-        var files = ev.target.files;
-        // MessageComposer shouldn't have to rely on it's parent passing in a callback to upload a file
-        if (files && files.length > 0) {
-            this.props.uploadFile(files[0]);
-        }
-        this.refs.uploadInput.value = null;
-    },
-
-    onCallClick: function(ev) {
-        dis.dispatch({
-            action: 'place_call',
-            type: ev.shiftKey ? "screensharing" : "video",
-            room_id: this.props.room.roomId
-        });
-    },
-
-    render: function() {
-        var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
-        var uploadInputStyle = {display: 'none'};
-        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
-        return (
-            <div className="mx_MessageComposer">
-                <div className="mx_MessageComposer_wrapper">
-                    <div className="mx_MessageComposer_row">
-                        <div className="mx_MessageComposer_avatar">
-                            <MemberAvatar member={me} width={24} height={24} />
-                        </div>
-                        <div className="mx_MessageComposer_input">
-                            <textarea ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder="Type a message..." />
-                        </div>
-                        <div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
-                            <img src="img/upload.png" width="17" height="22"/>
-                            <input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
-                        </div>
-                        <div className="mx_MessageComposer_call" onClick={this.onCallClick}>
-                            <img src="img/call.png" width="28" height="20"/>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/molecules/MessageTile.js b/src/skins/vector/views/molecules/MessageTile.js
deleted file mode 100644
index 6ea4441308..0000000000
--- a/src/skins/vector/views/molecules/MessageTile.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var sdk = require('matrix-react-sdk')
-
-var MessageTileController = require('matrix-react-sdk/lib/controllers/molecules/MessageTile')
-
-module.exports = React.createClass({
-    displayName: 'MessageTile',
-    mixins: [MessageTileController],
-
-    statics: {
-        needsSenderProfile: function() {
-            return true;
-        }
-    },
-
-    render: function() {
-        var UnknownMessageTile = sdk.getComponent('molecules.UnknownMessageTile');
-
-        var tileTypes = {
-            'm.text': sdk.getComponent('molecules.MTextTile'),
-            'm.notice': sdk.getComponent('molecules.MNoticeTile'),
-            'm.emote': sdk.getComponent('molecules.MEmoteTile'),
-            'm.image': sdk.getComponent('molecules.MImageTile'),
-            'm.file': sdk.getComponent('molecules.MFileTile')
-        };
-
-        var content = this.props.mxEvent.getContent();
-        var msgtype = content.msgtype;
-        var TileType = UnknownMessageTile;
-        if (msgtype && tileTypes[msgtype]) {
-            TileType = tileTypes[msgtype];
-        }
-
-        return <TileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />;
-    },
-});
diff --git a/src/skins/vector/views/molecules/ProgressBar.js b/src/skins/vector/views/molecules/ProgressBar.js
deleted file mode 100644
index 18d1440ad8..0000000000
--- a/src/skins/vector/views/molecules/ProgressBar.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var ProgressBarController = require('matrix-react-sdk/lib/controllers/molecules/ProgressBar')
-
-module.exports = React.createClass({
-    displayName: 'ProgressBar',
-    mixins: [ProgressBarController],
-
-    render: function() {
-        // Would use an HTML5 progress tag but if that doesn't animate if you
-        // use the HTML attributes rather than styles
-        var progressStyle = {
-            width: ((this.props.value / this.props.max) * 100)+"%"
-        };
-        return (
-            <div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/RoomCreate.js b/src/skins/vector/views/molecules/RoomCreate.js
deleted file mode 100644
index d66e014d8b..0000000000
--- a/src/skins/vector/views/molecules/RoomCreate.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-module.exports = React.createClass({
-    displayName: 'RoomCreate',
-
-    render: function() {
-        return (
-            <div className="mx_RoomCreate">
-                <div className="mx_RoomCreate_table">
-                    <div className="mx_RoomTile">
-                        <div className="mx_RoomTile_avatar">
-                            <img src="img/create.png" width="32" height="32"/>
-                        </div>
-                        <div className="mx_RoomTile_name">Create new room</div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/RoomHeader.js b/src/skins/vector/views/molecules/RoomHeader.js
deleted file mode 100644
index 47ac9cbeaf..0000000000
--- a/src/skins/vector/views/molecules/RoomHeader.js
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher')
-
-var CallHandler = require('matrix-react-sdk/lib/CallHandler');
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var RoomHeaderController = require('matrix-react-sdk/lib/controllers/molecules/RoomHeader')
-
-module.exports = React.createClass({
-    displayName: 'RoomHeader',
-    mixins: [RoomHeaderController],
-
-    onNameChange: function(new_name) {
-        if (this.props.room.name != new_name && new_name) {
-            MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name);
-        }
-    },
-
-    getRoomName: function() {
-        return this.refs.name_edit.value;
-    },
-
-    onFullscreenClick: function() {
-        dis.dispatch({action: 'video_fullscreen', fullscreen: true}, true);
-    },
-    
-    render: function() {
-        var EditableText = sdk.getComponent("atoms.EditableText");
-        var RoomAvatar = sdk.getComponent('atoms.RoomAvatar');
-
-        var header;
-        if (this.props.simpleHeader) {
-            var cancel;
-            if (this.props.onCancelClick) {
-                cancel = <img className="mx_RoomHeader_simpleHeaderCancel" src="img/cancel-black.png" onClick={ this.props.onCancelClick } alt="Close" width="18" height="18"/>
-            }
-            header =
-                <div className="mx_RoomHeader_wrapper">
-                    <div className="mx_RoomHeader_simpleHeader">
-                        { this.props.simpleHeader }
-                        { cancel }
-                    </div>
-                </div>
-        }
-        else {
-            var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
-
-            var call_buttons;
-            if (this.state && this.state.call_state != 'ended') {
-                //var muteVideoButton;
-                var activeCall = (
-                    CallHandler.getCallForRoom(this.props.room.roomId)
-                );
-/*                
-                if (activeCall && activeCall.type === "video") {
-                    muteVideoButton = (
-                        <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton"
-                                onClick={this.onMuteVideoClick}>
-                            {
-                                (activeCall.isLocalVideoMuted() ?
-                                    "Unmute" : "Mute") + " video"
-                            }
-                        </div>
-                    );
-                }
-                        {muteVideoButton}
-                        <div className="mx_RoomHeader_textButton mx_RoomHeader_voipButton"
-                                onClick={this.onMuteAudioClick}>
-                            {
-                                (activeCall && activeCall.isMicrophoneMuted() ?
-                                    "Unmute" : "Mute") + " audio"
-                            }
-                        </div>
-*/                
-
-                call_buttons = (
-                    <div className="mx_RoomHeader_textButton"
-                            onClick={this.onHangupClick}>
-                        End call
-                    </div>
-                );
-            }
-
-            var name = null;
-            var topic_el = null;
-            var cancel_button = null;
-            var save_button = null;
-            var settings_button = null;
-            var actual_name = this.props.room.currentState.getStateEvents('m.room.name', '');
-            if (actual_name) actual_name = actual_name.getContent().name;
-            if (this.props.editing) {
-                name = 
-                    <div className="mx_RoomHeader_nameEditing">
-                        <input className="mx_RoomHeader_nameInput" type="text" defaultValue={actual_name} placeholder="Name" ref="name_edit"/>
-                    </div>
-                // if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div>
-                cancel_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onCancelClick}>Cancel</div>
-                save_button = <div className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>Save Changes</div>
-            } else {
-                // <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
-                name =
-                    <div className="mx_RoomHeader_name" onClick={this.props.onSettingsClick}>
-                        <div className="mx_RoomHeader_nametext">{ this.props.room.name }</div>
-                        <div className="mx_RoomHeader_settingsButton">
-                            <img src="img/settings.png" width="12" height="12"/>
-                        </div>
-                    </div>
-                if (topic) topic_el = <div className="mx_RoomHeader_topic" title={topic.getContent().topic}>{ topic.getContent().topic }</div>;
-            }
-
-            var roomAvatar = null;
-            if (this.props.room) {
-                roomAvatar = (
-                    <RoomAvatar room={this.props.room} width="48" height="48" />
-                );
-            }
-
-            var zoom_button, video_button, voice_button;
-            if (activeCall) {
-                if (activeCall.type == "video") {
-                    zoom_button = (
-                        <div className="mx_RoomHeader_button" onClick={this.onFullscreenClick}>
-                            <img src="img/zoom.png" title="Fullscreen" alt="Fullscreen" width="32" height="32" style={{ 'marginTop': '-5px' }}/>
-                        </div>
-                    );
-                }
-                video_button = 
-                        <div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={activeCall && activeCall.type === "video" ? this.onMuteVideoClick : this.onVideoClick}>
-                            <img src="img/video.png" title="Video call" alt="Video call" width="32" height="32" style={{ 'marginTop': '-8px' }}/>
-                        </div>;
-                voice_button =
-                        <div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={activeCall ? this.onMuteAudioClick : this.onVoiceClick}>
-                            <img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32" style={{ 'marginTop': '-8px' }}/>
-                        </div>;
-            }
-
-            header =
-                <div className="mx_RoomHeader_wrapper">
-                    <div className="mx_RoomHeader_leftRow">
-                        <div className="mx_RoomHeader_avatar">
-                            { roomAvatar }
-                        </div>
-                        <div className="mx_RoomHeader_info">
-                            { name }
-                            { topic_el }
-                        </div>
-                    </div>
-                    {call_buttons}
-                    {cancel_button}
-                    {save_button}
-                    <div className="mx_RoomHeader_rightRow">
-                        { video_button }
-                        { voice_button }
-                        { zoom_button }
-                        <div className="mx_RoomHeader_button">
-                            <img src="img/search.png" title="Search" alt="Search" width="21" height="19" onClick={this.props.onSearchClick}/>
-                        </div>
-                    </div>
-                </div>
-        }
-
-        return (
-            <div className="mx_RoomHeader">
-                { header }
-            </div>
-        );
-    },
-});
diff --git a/src/skins/vector/views/molecules/RoomSettings.js b/src/skins/vector/views/molecules/RoomSettings.js
deleted file mode 100644
index 4fdd40d9e1..0000000000
--- a/src/skins/vector/views/molecules/RoomSettings.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var sdk = require('matrix-react-sdk');
-
-var RoomSettingsController = require('matrix-react-sdk/lib/controllers/molecules/RoomSettings')
-
-module.exports = React.createClass({
-    displayName: 'RoomSettings',
-    mixins: [RoomSettingsController],
-
-    getTopic: function() {
-        return this.refs.topic.value;
-    },
-
-    getJoinRules: function() {
-        return this.refs.is_private.checked ? "invite" : "public";
-    },
-
-    getHistoryVisibility: function() {
-        return this.refs.share_history.checked ? "shared" : "invited";
-    },
-
-    getPowerLevels: function() {
-        if (!this.state.power_levels_changed) return undefined;
-
-        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
-        power_levels = power_levels.getContent();
-
-        var new_power_levels = {
-            ban: parseInt(this.refs.ban.value),
-            kick: parseInt(this.refs.kick.value),
-            redact: parseInt(this.refs.redact.value),
-            invite: parseInt(this.refs.invite.value),
-            events_default: parseInt(this.refs.events_default.value),
-            state_default: parseInt(this.refs.state_default.value),
-            users_default: parseInt(this.refs.users_default.value),
-            users: power_levels.users,
-            events: power_levels.events,
-        };
-
-        return new_power_levels;
-    },
-
-    onPowerLevelsChanged: function() {
-        this.setState({
-            power_levels_changed: true
-        });
-    },
-
-    render: function() {
-        var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
-
-        var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
-        if (topic) topic = topic.getContent().topic;
-
-        var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', '');
-        if (join_rule) join_rule = join_rule.getContent().join_rule;
-
-        var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', '');
-        if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;
-
-        var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
-
-        var events_levels = power_levels.events || {};
-
-        if (power_levels) {
-            power_levels = power_levels.getContent();
-
-            var ban_level = parseInt(power_levels.ban);
-            var kick_level = parseInt(power_levels.kick);
-            var redact_level = parseInt(power_levels.redact);
-            var invite_level = parseInt(power_levels.invite || 0);
-            var send_level = parseInt(power_levels.events_default || 0);
-            var state_level = parseInt(power_levels.state_default || 0);
-            var default_user_level = parseInt(power_levels.users_default || 0);
-
-            if (power_levels.ban == undefined) ban_level = 50;
-            if (power_levels.kick == undefined) kick_level = 50;
-            if (power_levels.redact == undefined) redact_level = 50;
-
-            var user_levels = power_levels.users || {};
-
-            var user_id = MatrixClientPeg.get().credentials.userId;
-
-            var current_user_level = user_levels[user_id];
-            if (current_user_level == undefined) current_user_level = default_user_level;
-
-            var power_level_level = events_levels["m.room.power_levels"];
-            if (power_level_level == undefined) {
-                power_level_level = state_level;
-            }
-
-            var can_change_levels = current_user_level >= power_level_level;
-        } else {
-            var ban_level = 50;
-            var kick_level = 50;
-            var redact_level = 50;
-            var invite_level = 0;
-            var send_level = 0;
-            var state_level = 0;
-            var default_user_level = 0;
-
-            var user_levels = [];
-            var events_levels = [];
-
-            var current_user_level = 0;
-
-            var power_level_level = 0;
-
-            var can_change_levels = false;
-        }
-
-        var room_avatar_level = parseInt(power_levels.state_default || 0);
-        if (events_levels['m.room.avatar'] !== undefined) {
-            room_avatar_level = events_levels['m.room.avatar'];
-        }
-        var can_set_room_avatar = current_user_level >= room_avatar_level;
-
-        var change_avatar;
-        if (can_set_room_avatar) {
-            change_avatar = <div>
-                <h3>Room Icon</h3>
-                <ChangeAvatar room={this.props.room} />
-            </div>;
-        }
-
-        var banned = this.props.room.getMembersWithMembership("ban");
-
-        return (
-            <div className="mx_RoomSettings">
-                <textarea className="mx_RoomSettings_description" placeholder="Topic" defaultValue={topic} ref="topic"/> <br/>
-                <label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/>
-                <label><input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users</label> <br/>
-                <label className="mx_RoomSettings_encrypt"><input type="checkbox" /> Encrypt room</label> <br/>
-
-                <h3>Power levels</h3>
-                <div className="mx_RoomSettings_power_levels mx_RoomSettings_settings">
-                    <div>
-                        <label htmlFor="mx_RoomSettings_ban_level">Ban level</label>
-                        <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level"
-                            disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_kick_level">Kick level</label>
-                        <input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level"
-                            disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_redact_level">Redact level</label>
-                        <input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level"
-                            disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_invite_level">Invite level</label>
-                        <input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level"
-                            disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_event_level">Send event level</label>
-                        <input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level"
-                            disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_state_level">Set state level</label>
-                        <input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level"
-                            disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                    <div>
-                        <label htmlFor="mx_RoomSettings_user_level">Default user level</label>
-                        <input type="text" defaultValue={default_user_level} size="3" ref="users_default"
-                            id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}
-                            onChange={this.onPowerLevelsChanged}/>
-                    </div>
-                </div>
-
-                <h3>User levels</h3>
-                <div className="mx_RoomSettings_user_levels mx_RoomSettings_settings">
-                    {Object.keys(user_levels).map(function(user, i) {
-                        return (
-                            <div key={user}>
-                                <label htmlFor={"mx_RoomSettings_user_"+i}>{user}</label>
-                                <input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/>
-                            </div>
-                        );
-                    })}
-                </div>
-
-                <h3>Event levels</h3>
-                <div className="mx_RoomSettings_event_lvels mx_RoomSettings_settings">
-                    {Object.keys(events_levels).map(function(event_type, i) {
-                        return (
-                            <div key={event_type}>
-                                <label htmlFor={"mx_RoomSettings_event_"+i}>{event_type}</label>
-                                <input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/>
-                            </div>
-                        );
-                    })}
-                </div>
-
-                <h3>Banned users</h3>
-                <div className="mx_RoomSettings_banned">
-                    {banned.map(function(member, i) {
-                        return (
-                            <div key={i}>
-                                {member.userId}
-                            </div>
-                        );
-                    })}
-                </div>
-                {change_avatar}
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/SenderProfile.js b/src/skins/vector/views/molecules/SenderProfile.js
deleted file mode 100644
index c09685aad8..0000000000
--- a/src/skins/vector/views/molecules/SenderProfile.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var classNames = require("classnames");
-
-// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
-// Revert to Arial when this happens, which on OSX works at least.
-var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
-
-module.exports = React.createClass({
-    displayName: 'SenderProfile',
-
-    render: function() {
-        var mxEvent = this.props.mxEvent;
-        var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
-
-        var classes = classNames({
-            mx_SenderProfile: true,
-            // taken from https://en.wikipedia.org/wiki/Combining_character
-            mx_SenderProfile_zalgo: zalgo.test(name),
-        });
-
-        var msgtype = mxEvent.getContent().msgtype;
-        if (msgtype && msgtype == 'm.emote') {
-            name = ''; // emote message must include the name so don't duplicate it
-        }
-        return (
-            <span className={classes}>
-                {name} { this.props.aux }
-            </span>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/molecules/ServerConfig.js b/src/skins/vector/views/molecules/ServerConfig.js
deleted file mode 100644
index d6947727c8..0000000000
--- a/src/skins/vector/views/molecules/ServerConfig.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var Modal = require('matrix-react-sdk/lib/Modal');
-var sdk = require('matrix-react-sdk')
-
-var ServerConfigController = require('matrix-react-sdk/lib/controllers/molecules/ServerConfig')
-
-module.exports = React.createClass({
-    displayName: 'ServerConfig',
-    mixins: [ServerConfigController],
-
-    showHelpPopup: function() {
-        var ErrorDialog = sdk.getComponent('organisms.ErrorDialog');
-        Modal.createDialog(ErrorDialog, {
-            title: 'Custom Server Options',
-            description: <span>
-                You can use the custom server options to log into other Matrix servers by specifying a different Home server URL.<br/>
-                This allows you to use Vector with an existing Matrix account on a different Home server.<br/>
-                <br/>
-                You can also set a custom Identity server but this will affect people's ability to find you
-                if you use a server in a group other than the main Matrix.org group.
-            </span>,
-            button: "Dismiss",
-            focus: true,
-        });
-    },
-
-    render: function() {
-        return (
-            <div className="mx_ServerConfig">
-                <label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label>
-                <input className="mx_Login_field" id="hsurl" type="text" placeholder={this.state.original_hs_url} value={this.state.hs_url} onChange={this.hsChanged} />
-                <label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label>
-                <input className="mx_Login_field" id="isurl" type="text" placeholder={this.state.original_is_url} value={this.state.is_url} onChange={this.isChanged} />
-                <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/UnknownMessageTile.js b/src/skins/vector/views/molecules/UnknownMessageTile.js
deleted file mode 100644
index e8cd322aa4..0000000000
--- a/src/skins/vector/views/molecules/UnknownMessageTile.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-module.exports = React.createClass({
-    displayName: 'UnknownMessageTile',
-
-    render: function() {
-        var content = this.props.mxEvent.getContent();
-        return (
-            <span className="mx_UnknownMessageTile">
-                {content.body}
-            </span>
-        );
-    },
-});
diff --git a/src/skins/vector/views/molecules/UserSelector.js b/src/skins/vector/views/molecules/UserSelector.js
deleted file mode 100644
index 58cb7d215a..0000000000
--- a/src/skins/vector/views/molecules/UserSelector.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var UserSelectorController = require('matrix-react-sdk/lib/controllers/molecules/UserSelector')
-
-module.exports = React.createClass({
-    displayName: 'UserSelector',
-    mixins: [UserSelectorController],
-
-    onAddUserId: function() {
-        this.addUser(this.refs.user_id_input.value);
-        this.refs.user_id_input.value = "";
-    },
-
-    render: function() {
-        var self = this;
-        return (
-            <div>
-                <ul className="mx_UserSelector_UserIdList" ref="list">
-                    {this.props.selected_users.map(function(user_id, i) {
-                        return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>
-                    })}
-                </ul>
-                <input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/>
-                <button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">Add User</button>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/voip/CallView.js b/src/skins/vector/views/molecules/voip/CallView.js
deleted file mode 100644
index 52297bbc1d..0000000000
--- a/src/skins/vector/views/molecules/voip/CallView.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var sdk = require('matrix-react-sdk')
-var CallViewController = require(
-    "../../../../../controllers/molecules/voip/CallView"
-);
-
-module.exports = React.createClass({
-    displayName: 'CallView',
-    mixins: [CallViewController],
-
-    getVideoView: function() {
-        return this.refs.video;
-    },
-
-    render: function(){
-        var VideoView = sdk.getComponent('molecules.voip.VideoView');
-        return (
-            <VideoView ref="video" onClick={ this.props.onClick }/>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/voip/IncomingCallBox.js b/src/skins/vector/views/molecules/voip/IncomingCallBox.js
deleted file mode 100644
index bf12990402..0000000000
--- a/src/skins/vector/views/molecules/voip/IncomingCallBox.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var IncomingCallBoxController = require(
-    "matrix-react-sdk/lib/controllers/molecules/voip/IncomingCallBox"
-);
-
-module.exports = React.createClass({
-    displayName: 'IncomingCallBox',
-    mixins: [IncomingCallBoxController],
-
-    getRingAudio: function() {
-        return this.refs.ringAudio;
-    },
-
-    render: function() {
-
-        // NB: This block MUST have a "key" so React doesn't clobber the elements
-        // between in-call / not-in-call.
-        var audioBlock = (
-            <audio ref="ringAudio" key="voip_ring_audio" loop>
-                <source src="media/ring.ogg" type="audio/ogg" />
-                <source src="media/ring.mp3" type="audio/mpeg" />
-            </audio>
-        );
-
-        if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
-            return (
-                <div>
-                    {audioBlock}
-                </div>
-            );
-        }
-        var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
-        return (
-            <div className="mx_IncomingCallBox">
-                {audioBlock}
-                <img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
-                <div className="mx_IncomingCallBox_title">
-                    Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
-                </div>
-                <div className="mx_IncomingCallBox_buttons">
-                    <div className="mx_IncomingCallBox_buttons_cell">
-                        <div className="mx_IncomingCallBox_buttons_decline" onClick={this.onRejectClick}>
-                            Decline
-                        </div>
-                    </div>
-                    <div className="mx_IncomingCallBox_buttons_cell">
-                        <div className="mx_IncomingCallBox_buttons_accept" onClick={this.onAnswerClick}>
-                            Accept
-                        </div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/molecules/voip/VideoView.js b/src/skins/vector/views/molecules/voip/VideoView.js
deleted file mode 100644
index 75a2500d10..0000000000
--- a/src/skins/vector/views/molecules/voip/VideoView.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher')
-
-module.exports = React.createClass({
-    displayName: 'VideoView',
-
-    componentWillMount: function() {
-        dis.register(this.onAction);
-    },
-
-    getRemoteVideoElement: function() {
-        return ReactDOM.findDOMNode(this.refs.remote);
-    },
-
-    getRemoteAudioElement: function() {
-        return this.refs.remoteAudio;
-    },
-
-    getLocalVideoElement: function() {
-        return ReactDOM.findDOMNode(this.refs.local);
-    },
-
-    setContainer: function(c) {
-        this.container = c;
-    },
-
-    onAction: function(payload) {
-        switch (payload.action) {
-            case 'video_fullscreen':
-                if (!this.container) {
-                    return;
-                }
-                var element = this.container;
-                if (payload.fullscreen) {
-                    var requestMethod = (
-                        element.requestFullScreen ||
-                        element.webkitRequestFullScreen ||
-                        element.mozRequestFullScreen ||
-                        element.msRequestFullscreen
-                    );
-                    requestMethod.call(element);
-                }
-                else {
-                    var exitMethod = (
-                        document.exitFullscreen ||
-                        document.mozCancelFullScreen ||
-                        document.webkitExitFullscreen ||
-                        document.msExitFullscreen
-                    );
-                    if (exitMethod) {
-                        exitMethod.call(document);
-                    }
-                }
-                break;
-        }
-    },
-
-    render: function() {
-        var VideoFeed = sdk.getComponent('atoms.voip.VideoFeed');
-        return (
-            <div className="mx_VideoView" ref={this.setContainer} onClick={ this.props.onClick }>
-                <div className="mx_VideoView_remoteVideoFeed">
-                    <VideoFeed ref="remote"/>
-                    <audio ref="remoteAudio"/>
-                </div>
-                <div className="mx_VideoView_localVideoFeed">                
-                    <VideoFeed ref="local"/>
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/organisms/CasLogin.js b/src/skins/vector/views/organisms/CasLogin.js
deleted file mode 100644
index ad9dbed955..0000000000
--- a/src/skins/vector/views/organisms/CasLogin.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var CasLoginController = require('matrix-react-sdk/lib/controllers/organisms/CasLogin');
-
-module.exports = React.createClass({
-    displayName: 'CasLogin',
-    mixins: [CasLoginController],
-
-    render: function() {
-        return (
-            <div>
-                <button onClick={this.onCasClicked}>Sign in with CAS</button>
-            </div>
-        );
-    },
-
-});
diff --git a/src/skins/vector/views/organisms/CreateRoom.js b/src/skins/vector/views/organisms/CreateRoom.js
deleted file mode 100644
index b63b477deb..0000000000
--- a/src/skins/vector/views/organisms/CreateRoom.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var CreateRoomController = require('matrix-react-sdk/lib/controllers/organisms/CreateRoom')
-
-var sdk = require('matrix-react-sdk')
-
-var PresetValues = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets').Presets;
-
-module.exports = React.createClass({
-    displayName: 'CreateRoom',
-    mixins: [CreateRoomController],
-
-    getPreset: function() {
-        return this.refs.presets.getPreset();
-    },
-
-    getName: function() {
-        return this.refs.name_textbox.getName();
-    },
-
-    getTopic: function() {
-        return this.refs.topic.getTopic();
-    },
-
-    getAliasLocalpart: function() {
-        return this.refs.alias.getAliasLocalpart();
-    },
-
-    getInvitedUsers: function() {
-        return this.refs.user_selector.getUserIds();
-    },
-
-    onPresetChanged: function(preset) {
-        switch (preset) {
-            case PresetValues.PrivateChat:
-                this.setState({
-                    preset: preset,
-                    is_private: true,
-                    share_history: false,
-                });
-                break;
-            case PresetValues.PublicChat:
-                this.setState({
-                    preset: preset,
-                    is_private: false,
-                    share_history: true,
-                });
-                break;
-            case PresetValues.Custom:
-                this.setState({
-                    preset: preset,
-                });
-                break;
-        }
-    },
-
-    onPrivateChanged: function(ev) {
-        this.setState({
-            preset: PresetValues.Custom,
-            is_private: ev.target.checked,
-        });
-    },
-
-    onShareHistoryChanged: function(ev) {
-        this.setState({
-            preset: PresetValues.Custom,
-            share_history: ev.target.checked,
-        });
-    },
-
-    onTopicChange: function(ev) {
-        this.setState({
-            topic: ev.target.value,
-        });
-    },
-
-    onNameChange: function(ev) {
-        this.setState({
-            room_name: ev.target.value,
-        });
-    },
-
-    onInviteChanged: function(invited_users) {
-        this.setState({
-            invited_users: invited_users,
-        });
-    },
-
-    onAliasChanged: function(alias) {
-        this.setState({
-            alias: alias
-        })
-    },
-
-    onEncryptChanged: function(ev) {
-        this.setState({
-            encrypt: ev.target.checked,
-        });
-    },
-
-    render: function() {
-        var curr_phase = this.state.phase;
-        if (curr_phase == this.phases.CREATING) {
-            var Loader = sdk.getComponent("atoms.Spinner");
-            return (
-                <Loader/>
-            );
-        } else {
-            var error_box = "";
-            if (curr_phase == this.phases.ERROR) {
-                error_box = (
-                    <div className="mx_Error">
-                        An error occured: {this.state.error_string}
-                    </div>
-                );
-            }
-
-            var CreateRoomButton = sdk.getComponent("atoms.create_room.CreateRoomButton");
-            var RoomAlias = sdk.getComponent("atoms.create_room.RoomAlias");
-            var Presets = sdk.getComponent("atoms.create_room.Presets");
-            var UserSelector = sdk.getComponent("molecules.UserSelector");
-            var RoomHeader = sdk.getComponent("molecules.RoomHeader");
-
-            return (
-                <div className="mx_CreateRoom">
-                    <RoomHeader simpleHeader="Create room" />
-                    <div className="mx_CreateRoom_body">
-                        <input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br />
-                        <textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Topic"/> <br />
-                        <RoomAlias ref="alias" alias={this.state.alias} onChange={this.onAliasChanged}/> <br />
-                        <UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
-                        <Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
-                        <div>
-                            <label><input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/> Make this room private</label>
-                        </div>
-                        <div>
-                            <label><input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/> Share message history with new users</label>
-                        </div>
-                        <div className="mx_CreateRoom_encrypt">
-                            <label><input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/> Encrypt room</label>
-                        </div>
-                        <div>
-                            <CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br />
-                        </div>
-                        {error_box}
-                    </div>
-                </div>
-            );
-        }
-    }
-});
diff --git a/src/skins/vector/views/organisms/LogoutPrompt.js b/src/skins/vector/views/organisms/LogoutPrompt.js
deleted file mode 100644
index 6e347a4ef7..0000000000
--- a/src/skins/vector/views/organisms/LogoutPrompt.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var LogoutPromptController = require('matrix-react-sdk/lib/controllers/organisms/LogoutPrompt')
-
-module.exports = React.createClass({
-    displayName: 'LogoutPrompt',
-    mixins: [LogoutPromptController],
-
-    render: function() {
-        return (
-            <div>
-                <div className="mx_Dialog_content">
-                    Sign out?
-                </div>
-                <div className="mx_Dialog_buttons">
-                    <button onClick={this.logOut}>Sign Out</button>
-                    <button onClick={this.cancelPrompt}>Cancel</button>
-                </div>
-            </div>
-        );
-    },
-});
-
diff --git a/src/skins/vector/views/organisms/MemberList.js b/src/skins/vector/views/organisms/MemberList.js
deleted file mode 100644
index b39d675f31..0000000000
--- a/src/skins/vector/views/organisms/MemberList.js
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var classNames = require('classnames');
-
-var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList')
-var GeminiScrollbar = require('react-gemini-scrollbar');
-
-var sdk = require('matrix-react-sdk')
-
-
-module.exports = React.createClass({
-    displayName: 'MemberList',
-    mixins: [MemberListController],
-
-    getInitialState: function() {
-    },
-
-    memberSort: function(userIdA, userIdB) {
-        var userA = this.memberDict[userIdA].user;
-        var userB = this.memberDict[userIdB].user;
-
-        var presenceMap = {
-            online: 3,
-            unavailable: 2,
-            offline: 1
-        };
-
-        var presenceOrdA = userA ? presenceMap[userA.presence] : 0;
-        var presenceOrdB = userB ? presenceMap[userB.presence] : 0;
-
-        if (presenceOrdA != presenceOrdB) {
-            return presenceOrdB - presenceOrdA;
-        }
-
-        var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0;
-        var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0;
-
-        return latB - latA;
-    },
-
-    makeMemberTiles: function(membership) {
-        var MemberTile = sdk.getComponent("molecules.MemberTile");
-
-        var self = this;
-        return self.state.members.filter(function(userId) {
-            var m = self.memberDict[userId];
-            return m.membership == membership;
-        }).map(function(userId) {
-            var m = self.memberDict[userId];
-            return (
-                <MemberTile key={userId} member={m} ref={userId} />
-            );
-        });
-    },
-
-    onPopulateInvite: function(e) {
-        this.onInvite(this.refs.invite.value);
-        e.preventDefault();
-    },
-
-    inviteTile: function() {
-        if (this.state.inviting) {
-            var Loader = sdk.getComponent("atoms.Spinner");
-            return (
-                <Loader />
-            );
-        } else {
-            return (
-                <form onSubmit={this.onPopulateInvite}>
-                    <input className="mx_MemberList_invite" ref="invite" placeholder="Invite another user"/>
-                </form>
-            );
-        }
-    },
-
-    render: function() {
-        var invitedSection = null;
-        var invitedMemberTiles = this.makeMemberTiles('invite');
-        if (invitedMemberTiles.length > 0) {
-            invitedSection = (
-                <div className="mx_MemberList_invited">
-                    <h2>Invited</h2>
-                    <div className="mx_MemberList_wrapper">
-                        {invitedMemberTiles}
-                    </div>
-                </div>
-            );
-        }
-        return (
-            <div className="mx_MemberList">
-                <GeminiScrollbar autoshow={true} className="mx_MemberList_border">
-                    {this.inviteTile()}
-                    <div>
-                        <div className="mx_MemberList_wrapper">
-                            {this.makeMemberTiles('join')}
-                        </div>
-                    </div>
-                    {invitedSection}
-                </GeminiScrollbar>
-            </div>
-        );
-    }
-});
-
diff --git a/src/skins/vector/views/organisms/Notifier.js b/src/skins/vector/views/organisms/Notifier.js
deleted file mode 100644
index b214b4cd16..0000000000
--- a/src/skins/vector/views/organisms/Notifier.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var NotifierController = require('matrix-react-sdk/lib/controllers/organisms/Notifier')
-
-var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');
-var extend = require('matrix-react-sdk/lib/extend');
-var dis = require('matrix-react-sdk/lib/dispatcher');
-
-var Avatar = require('../../../../Avatar');
-
-
-var NotifierView = {
-    notificationMessageForEvent: function(ev) {
-        return TextForEvent.textForEvent(ev);
-    },
-
-    displayNotification: function(ev, room) {
-        if (!global.Notification || global.Notification.permission != 'granted') {
-            return;
-        }
-        if (global.document.hasFocus()) {
-            return;
-        }
-
-        var msg = this.notificationMessageForEvent(ev);
-        if (!msg) return;
-
-        var title;
-        if (!ev.sender ||  room.name == ev.sender.name) {
-            title = room.name;
-            // notificationMessageForEvent includes sender,
-            // but we already have the sender here
-            if (ev.getContent().body) msg = ev.getContent().body;
-        } else if (ev.getType() == 'm.room.member') {
-            // context is all in the message here, we don't need
-            // to display sender info
-            title = room.name;
-        } else if (ev.sender) {
-            title = ev.sender.name + " (" + room.name + ")";
-            // notificationMessageForEvent includes sender,
-            // but we've just out sender in the title
-            if (ev.getContent().body) msg = ev.getContent().body;
-        }
-
-        var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
-            ev.sender, 40, 40, 'crop'
-        ) : null;
-
-        var notification = new global.Notification(
-            title,
-            {
-                "body": msg,
-                "icon": avatarUrl,
-                "tag": "vector"
-            }
-        );
-
-        notification.onclick = function() {
-            dis.dispatch({
-                action: 'view_room',
-                room_id: room.roomId
-            });
-            global.focus();
-        };
-        
-        /*var audioClip;
-        
-        if (audioNotification) {
-            audioClip = playAudio(audioNotification);
-        }*/
-
-        global.setTimeout(function() {
-            notification.close();
-        }, 5 * 1000);
-        
-    }
-};
-
-var NotifierClass = function() {};
-extend(NotifierClass.prototype, NotifierController);
-extend(NotifierClass.prototype, NotifierView);
-
-module.exports = new NotifierClass();
-
diff --git a/src/skins/vector/views/organisms/QuestionDialog.js b/src/skins/vector/views/organisms/QuestionDialog.js
deleted file mode 100644
index 3941b1f925..0000000000
--- a/src/skins/vector/views/organisms/QuestionDialog.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-/*
- * Usage:
- * Modal.createDialog(ErrorDialog, {
- *   title: "some text", (default: "Error")
- *   description: "some more text",
- *   button: "Button Text",
- *   onClose: someFunction,
- *   focus: true|false (default: true)
- * });
- */
-
-var React = require('react');
-var QuestionDialogController = require('matrix-react-sdk/lib/controllers/organisms/QuestionDialog')
-
-module.exports = React.createClass({
-    displayName: 'QuestionDialog',
-    mixins: [QuestionDialogController],
-
-    onOk: function() {
-        this.props.onFinished(true);
-    },
-
-    onCancel: function() {
-        this.props.onFinished(false);
-    },
-
-    render: function() {
-        return (
-            <div className="mx_QuestionDialog">
-                <div className="mx_QuestionDialogTitle">
-                    {this.props.title}
-                </div>
-                <div className="mx_Dialog_content">
-                    {this.props.description}
-                </div>
-                <div className="mx_Dialog_buttons">
-                    <button onClick={this.onOk} autoFocus={this.props.focus}>
-                        {this.props.button}
-                    </button>
-
-                    <button onClick={this.onCancel}>
-                        Cancel
-                    </button>
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/organisms/RoomList.js b/src/skins/vector/views/organisms/RoomList.js
deleted file mode 100644
index 018cc9b0db..0000000000
--- a/src/skins/vector/views/organisms/RoomList.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher');
-
-var GeminiScrollbar = require('react-gemini-scrollbar');
-var RoomListController = require('../../../../controllers/organisms/RoomList')
-
-module.exports = React.createClass({
-    displayName: 'RoomList',
-    mixins: [RoomListController],
-
-    onShowClick: function() {
-        dis.dispatch({
-            action: 'show_left_panel',
-        });
-    },
-
-    render: function() {
-        var expandButton = this.props.collapsed ? 
-                           <img className="mx_RoomList_expandButton" onClick={ this.onShowClick } src="img/menu.png" width="20" alt=">"/> :
-                           null;
-
-        var RoomSubList = sdk.getComponent('organisms.RoomSubList');
-        var self = this;
-
-        return (
-            <GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._repositionTooltip}>
-            <div className="mx_RoomList">
-                { expandButton }
-
-                <RoomSubList list={ self.state.lists['m.invite'] }
-                             label="Invites"
-                             editable={ false }
-                             order="recent"
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-
-                <RoomSubList list={ self.state.lists['m.favourite'] }
-                             label="Favourites"
-                             tagName="m.favourite"
-                             verb="favourite"
-                             editable={ true }
-                             order="manual"
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-
-                <RoomSubList list={ self.state.lists['m.recent'] }
-                             label="Conversations"
-                             editable={ true }
-                             verb="restore"
-                             order="recent"
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-
-                { Object.keys(self.state.lists).map(function(tagName) {
-                    if (!tagName.match(/^m\.(invite|favourite|recent|lowpriority|archived)$/)) {
-                        return <RoomSubList list={ self.state.lists[tagName] }
-                             key={ tagName }
-                             label={ tagName }
-                             tagName={ tagName }
-                             verb={ "tag as " + tagName }
-                             editable={ true }
-                             order="manual"
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-
-                    }
-                }) }
-
-                <RoomSubList list={ self.state.lists['m.lowpriority'] }
-                             label="Low priority"
-                             tagName="m.lowpriority"
-                             verb="demote"
-                             editable={ true }
-                             order="recent"
-                             bottommost={ self.state.lists['m.archived'].length === 0 }
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-
-                <RoomSubList list={ self.state.lists['m.archived'] }
-                             label="Historical"
-                             editable={ false }
-                             order="recent"
-                             bottommost={ true }
-                             activityMap={ self.state.activityMap }
-                             selectedRoom={ self.props.selectedRoom }
-                             collapsed={ self.props.collapsed } />
-            </div>
-            </GeminiScrollbar>
-        );
-    }
-});
-
diff --git a/src/skins/vector/views/organisms/RoomView.js b/src/skins/vector/views/organisms/RoomView.js
deleted file mode 100644
index e569a35387..0000000000
--- a/src/skins/vector/views/organisms/RoomView.js
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var dis = require('matrix-react-sdk/lib/dispatcher');
-
-var sdk = require('matrix-react-sdk')
-var classNames = require("classnames");
-var filesize = require('filesize');
-
-var GeminiScrollbar = require('react-gemini-scrollbar');
-var RoomViewController = require('../../../../controllers/organisms/RoomView')
-
-module.exports = React.createClass({
-    displayName: 'RoomView',
-    mixins: [RoomViewController],
-
-    onSettingsClick: function() {
-        this.setState({editingRoomSettings: true});
-    },
-
-    onSaveClick: function() {
-        this.setState({
-            editingRoomSettings: false,
-            uploadingRoomSettings: true,
-        });
-
-        var new_name = this.refs.header.getRoomName();
-        var new_topic = this.refs.room_settings.getTopic();
-        var new_join_rule = this.refs.room_settings.getJoinRules();
-        var new_history_visibility = this.refs.room_settings.getHistoryVisibility();
-        var new_power_levels = this.refs.room_settings.getPowerLevels();
-
-        this.uploadNewState(
-            new_name,
-            new_topic,
-            new_join_rule,
-            new_history_visibility,
-            new_power_levels
-        );
-    },
-
-    onCancelClick: function() {
-        this.setState(this.getInitialState());
-    },
-
-    onRejectButtonClicked: function(ev) {
-        var self = this;
-        this.setState({
-            rejecting: true
-        });
-        MatrixClientPeg.get().leave(this.props.roomId).done(function() {
-            dis.dispatch({ action: 'view_next_room' });
-            self.setState({
-                rejecting: false
-            });
-        }, function(err) {
-            console.error("Failed to reject invite: %s", err);
-            self.setState({
-                rejecting: false,
-                rejectError: err
-            });
-        });
-    },
-
-    onSearchClick: function() {
-        this.setState({ searching: true });
-    },
-
-    onConferenceNotificationClick: function() {
-        dis.dispatch({
-            action: 'place_call',
-            type: "video",
-            room_id: this.props.roomId
-        });
-    },
-
-    getUnreadMessagesString: function() {
-        if (!this.state.numUnreadMessages) {
-            return "";
-        }
-        return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : "");
-    },
-
-    scrollToBottom: function() {
-        var scrollNode = this._getScrollNode();
-        if (!scrollNode) return;
-        scrollNode.scrollTop = scrollNode.scrollHeight;
-    },
-
-    render: function() {
-        var RoomHeader = sdk.getComponent('molecules.RoomHeader');
-        var MessageComposer = sdk.getComponent('molecules.MessageComposer');
-        var CallView = sdk.getComponent("molecules.voip.CallView");
-        var RoomSettings = sdk.getComponent("molecules.RoomSettings");
-        var SearchBar = sdk.getComponent("molecules.SearchBar");
-
-        if (!this.state.room) {
-            if (this.props.roomId) {
-                return (
-                    <div>
-                    <button onClick={this.onJoinButtonClicked}>Join Room</button>
-                    </div>
-                );
-            } else {
-                return (
-                    <div />
-                );
-            }
-        }
-
-        var myUserId = MatrixClientPeg.get().credentials.userId;
-        if (this.state.room.currentState.members[myUserId].membership == 'invite') {
-            if (this.state.joining || this.state.rejecting) {
-                var Loader = sdk.getComponent("atoms.Spinner");
-                return (
-                    <div className="mx_RoomView">
-                        <Loader />
-                    </div>
-                );
-            } else {
-                var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event;
-                // XXX: Leaving this intentionally basic for now because invites are about to change totally
-                var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
-                var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : "";
-                return (
-                    <div className="mx_RoomView">
-                        <RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/>
-                        <div className="mx_RoomView_invitePrompt">
-                            <div>{inviteEvent.user_id} has invited you to a room</div>
-                            <br/>
-                            <button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button>
-                            <button onClick={this.onRejectButtonClicked}>Reject</button>
-                            <div className="error">{joinErrorText}</div>
-                            <div className="error">{rejectErrorText}</div>
-                        </div>
-                    </div>
-                );
-            }
-        } else {
-            var scrollheader_classes = classNames({
-                mx_RoomView_scrollheader: true,
-                loading: this.state.paginating
-            });
-
-            var statusBar = (
-                <div />
-            );
-
-            // for testing UI...
-            // this.state.upload = {
-            //     uploadedBytes: 123493,
-            //     totalBytes: 347534,
-            //     fileName: "testing_fooble.jpg",
-            // }
-
-            if (this.state.upload) {
-                var innerProgressStyle = {
-                    width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
-                };
-                var uploadedSize = filesize(this.state.upload.uploadedBytes);
-                var totalSize = filesize(this.state.upload.totalBytes);
-                if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) {
-                    uploadedSize = uploadedSize.replace(/ .*/, '');
-                }
-                statusBar = (
-                    <div className="mx_RoomView_uploadBar">
-                        <div className="mx_RoomView_uploadProgressOuter">
-                            <div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
-                        </div>
-                        <img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="17" height="22"/>
-                        <img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="18" height="18"/>
-                        <div className="mx_RoomView_uploadBytes">
-                            { uploadedSize } / { totalSize }
-                        </div>
-                        <div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div>
-                    </div>
-                );
-            } else {
-                var typingString = this.getWhoIsTypingString();
-                //typingString = "Testing typing...";
-                var unreadMsgs = this.getUnreadMessagesString();
-                // no conn bar trumps unread count since you can't get unread messages
-                // without a connection! (technically may already have some but meh)
-                // It also trumps the "some not sent" msg since you can't resend without
-                // a connection!
-                if (this.state.syncState === "ERROR") {
-                    statusBar = (
-                        <div className="mx_RoomView_connectionLostBar">
-                            <img src="img/warning2.png" width="30" height="30" alt="/!\"/>
-                            <div className="mx_RoomView_connectionLostBar_textArea">
-                                <div className="mx_RoomView_connectionLostBar_title">
-                                    Connectivity to the server has been lost.
-                                </div>
-                                <div className="mx_RoomView_connectionLostBar_desc">
-                                    Sent messages will be stored until your connection has returned.
-                                </div>
-                            </div>
-                        </div>
-                    );
-                }
-                else if (this.state.hasUnsentMessages) {
-                    statusBar = (
-                        <div className="mx_RoomView_connectionLostBar">
-                            <img src="img/warning2.png" width="30" height="30" alt="/!\"/>
-                            <div className="mx_RoomView_connectionLostBar_textArea">
-                                <div className="mx_RoomView_connectionLostBar_title">
-                                    Some of your messages have not been sent.
-                                </div>
-                                <div className="mx_RoomView_connectionLostBar_desc">
-                                    <a className="mx_RoomView_resend_link"
-                                        onClick={ this.onResendAllClick }>
-                                    Resend all now
-                                    </a> or select individual messages to re-send.
-                                </div>
-                            </div>
-                        </div>
-                    );
-                }
-                // unread count trumps who is typing since the unread count is only
-                // set when you've scrolled up
-                else if (unreadMsgs) {
-                    statusBar = (
-                        <div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }>
-                            <img src="img/newmessages.png" width="24" height="24" alt=""/>
-                            {unreadMsgs}
-                        </div>
-                    );
-                }
-                else if (typingString) {
-                    statusBar = (
-                        <div className="mx_RoomView_typingBar">
-                            <div className="mx_RoomView_typingImage">...</div>
-                            {typingString}
-                        </div>
-                    );
-                }
-            }
-
-            var aux = null;
-            if (this.state.editingRoomSettings) {
-                aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
-            }
-            else if (this.state.uploadingRoomSettings) {
-                var Loader = sdk.getComponent("atoms.Spinner");                
-                aux = <Loader/>;
-            }
-            else if (this.state.searching) {
-                aux = <SearchBar ref="search_bar" onCancelClick={this.onCancelClick} onSearch={this.onSearch}/>;
-            }
-
-            var conferenceCallNotification = null;
-            if (this.state.displayConfCallNotification) {
-                var supportedText;
-                if (!MatrixClientPeg.get().supportsVoip()) {
-                    supportedText = " (unsupported)";
-                }
-                conferenceCallNotification = (
-                    <div className="mx_RoomView_ongoingConfCallNotification" onClick={this.onConferenceNotificationClick}>
-                        Ongoing conference call {supportedText}
-                    </div>
-                );
-            }
-
-            var fileDropTarget = null;
-            if (this.state.draggingFile) {
-                fileDropTarget = <div className="mx_RoomView_fileDropTarget">
-                                    <div className="mx_RoomView_fileDropTargetLabel">
-                                        <img src="img/upload-big.png" width="43" height="57" alt="Drop File Here"/><br/>
-                                        Drop File Here
-                                    </div>
-                                 </div>;
-            }
-
-            return (
-                <div className="mx_RoomView">
-                    <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick}
-                        onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} />
-                    <div className="mx_RoomView_auxPanel">
-                        <CallView room={this.state.room}/>
-                        { conferenceCallNotification }
-                        { aux }
-                    </div>
-                    <GeminiScrollbar autoshow={true} ref="messagePanel" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
-                        <div className="mx_RoomView_messageListWrapper">
-                            { fileDropTarget }    
-                            <ol className="mx_RoomView_MessageList" aria-live="polite">
-                                <li className={scrollheader_classes}>
-                                </li>
-                                {this.getEventTiles()}
-                            </ol>
-                        </div>
-                    </GeminiScrollbar>
-                    <div className="mx_RoomView_statusArea">
-                        <div className="mx_RoomView_statusAreaBox">
-                            <div className="mx_RoomView_statusAreaBox_line"></div>
-                            {statusBar}
-                        </div>
-                    </div>
-                    <MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} />
-                </div>
-            );
-        }
-    },
-});
diff --git a/src/skins/vector/views/organisms/UserSettings.js b/src/skins/vector/views/organisms/UserSettings.js
deleted file mode 100644
index ea38c1bc85..0000000000
--- a/src/skins/vector/views/organisms/UserSettings.js
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-    http://www.apache.org/licenses/LICENSE-2.0
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sdk = require('matrix-react-sdk')
-var dis = require('matrix-react-sdk/lib/dispatcher')
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-var q = require('q');
-
-var version = require('../../../../../package.json').version;
-
-var Modal = require('matrix-react-sdk/lib/Modal');
-var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore');
-
-module.exports = React.createClass({
-    displayName: 'UserSettings',
-
-    Phases: {
-        Loading: "loading",
-        Saving: "saving",
-        Display: "display",
-    },
-
-    getInitialState: function() {
-        return {
-            avatarUrl: null,
-            displayName: null,
-            threePids: [],
-            clientVersion: version,
-            phase: this.Phases.Loading,
-        };
-    },
-
-    componentWillMount: function() {
-        var self = this;
-
-        var profilePromise = UserSettingsStore.loadProfileInfo();
-        var threepidPromise = UserSettingsStore.loadThreePids();
-
-        q.all([profilePromise, threepidPromise]).then(
-            function(resps) {
-                self.setState({
-                    avatarUrl: resps[0].avatar_url,
-                    displayName: resps[0].displayname,
-                    threepids: resps[1].threepids,
-                    phase: self.Phases.Display,
-                });
-
-                // keep a copy of the original state in order to track changes
-                self.setState({
-                    originalState: self.state
-                });
-            },
-            function(error) {
-                var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
-                Modal.createDialog(ErrorDialog, {
-                    title: "Can't load user settings",
-                    description: error.toString()
-                });
-            }
-        );
-    },
-
-    componentDidMount: function() {
-        this.dispatcherRef = dis.register(this.onAction);
-    },
-
-    componentWillUnmount: function() {
-        dis.unregister(this.dispatcherRef);
-    },
-
-    onSaveClicked: function(ev) {
-        var self = this;
-        var savePromises = [];
-
-        // XXX: this is managed in ChangeAvatar.js, although could be moved out here in order
-        // to allow for the change to be staged alongside the rest of the form.
-        //
-        // if (this.state.originalState.avatarUrl !== this.state.avatarUrl) {
-        //     savePromises.push( UserSettingsStore.saveAvatarUrl(this.state.avatarUrl) );
-        // }
-
-        if (this.state.originalState.displayName !== this.state.displayName) {
-            savePromises.push( UserSettingsStore.saveDisplayName(this.state.displayName) );
-        }
-
-        if (this.state.originalState.threepids.length !== this.state.threepids.length ||
-            this.state.originalState.threepids.every(function(element, index) {
-                    return element === this.state.threepids[index];
-            }))
-        {
-            savePromises.push( UserSettingsStore.saveThreePids(this.state.threepids) );
-        }
-
-        // TODO: do the password check
-
-        self.setState({
-            phase: self.Phases.Saving,
-        });
-
-        q.all(savePromises).then(
-            function(resps) {
-                self.setState({
-                    phase: self.Phases.Display,
-                });
-                self.onClose();
-            },
-            function(error) {
-                self.setState({
-                    phase: self.Phases.Display,
-                });
-                var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
-                Modal.createDialog(ErrorDialog, {
-                    title: "Can't save user settings",
-                    description: error.toString()
-                });
-            }
-        );        
-    },
-
-    onClose: function(ev) {
-        // XXX: use browser history instead to find the previous room?
-        if (this.props.roomId) {
-            dis.dispatch({
-                action: 'view_room',
-                room_id: this.props.roomId,
-            });
-        }
-        else {
-            dis.dispatch({
-                action: 'view_indexed_room',
-                roomIndex: 0,
-            });
-        }
-    },
-
-    onAction: function(payload) {
-        if (payload.action === "notifier_enabled") {
-            this.setState({
-                enableNotifications : UserSettingsStore.getEnableNotifications()
-            });
-        }
-    },    
-
-    editAvatar: function() {
-        var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
-        var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
-        var avatarDialog = (
-            <div>
-                <ChangeAvatar initialAvatarUrl={url} />
-                <div className="mx_Dialog_buttons">
-                    <button onClick={this.onAvatarDialogCancel}>Cancel</button>
-                </div>
-            </div>
-        );
-        this.avatarDialog = Modal.createDialogWithElement(avatarDialog);
-    },
-
-    onAvatarDialogCancel: function() {
-        this.avatarDialog.close();
-    },
-
-    onLogoutClicked: function(event) {
-        var LogoutPrompt = sdk.getComponent('organisms.LogoutPrompt');
-        this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel});
-    },
-
-    onLogoutPromptCancel: function() {
-        this.logoutModal.closeDialog();
-    },
-
-    onDisplayNameChange: function(event) {
-        this.setState({ displayName: event.target.value });
-    },
-
-    onEnableNotificationsChange: function(event) {
-        // don't bother waiting for Save to be clicked, as that'd be silly
-        UserSettingsStore.setEnableNotifications( this.refs.enableNotifications.value );
-
-        this.setState({
-            enableNotifications : UserSettingsStore.getEnableNotifications()
-        });
-    },
-
-    render: function() {
-        var Loader = sdk.getComponent("atoms.Spinner");
-        var saving;
-        switch (this.state.phase) {
-            case this.Phases.Loading:
-                return <Loader />
-            case this.Phases.Saving:
-                saving = <Loader />
-            case this.Phases.Display:
-                var RoomHeader = sdk.getComponent('molecules.RoomHeader');
-                return (
-                    <div className="mx_UserSettings">
-                        <RoomHeader simpleHeader="Settings" onCancelClick={ this.onClose } />
-
-                        <h2>Profile</h2>
-
-                        <div className="mx_UserSettings_section">
-                            <div className="mx_UserSettings_profileTable">
-                                <div className="mx_UserSettings_profileTableRow">
-                                    <div className="mx_UserSettings_profileLabelCell">
-                                        <label htmlFor="displayName">Display name</label>
-                                    </div>
-                                    <div className="mx_UserSettings_profileInputCell">
-                                        <input id="displayName" ref="displayName" value={ this.state.displayName } onChange={ this.onDisplayNameChange } />
-                                    </div>
-                                </div>
-
-                                {this.state.threepids.map(function(val) {
-                                    var id = "email-" + val.address;
-                                    return (
-                                        <div className="mx_UserSettings_profileTableRow">
-                                            <div className="mx_UserSettings_profileLabelCell">
-                                                <label htmlFor={ id }>Email</label>
-                                            </div>
-                                            <div className="mx_UserSettings_profileInputCell">
-                                                <input key={val.address} id={ id } value={ val.address } disabled />
-                                            </div>
-                                        </div>
-                                    );
-                                })}
-
-                                <div className="mx_UserSettings_profileTableRow">
-                                    <div className="mx_UserSettings_profileLabelCell">
-                                        <label htmlFor="password1">New password</label>
-                                    </div>
-                                    <div className="mx_UserSettings_profileInputCell">
-                                        <input id="password1" ref="password1" value={ this.state.password1 } />
-                                    </div>
-                                </div>
-                                <div className="mx_UserSettings_profileTableRow">
-                                    <div className="mx_UserSettings_profileLabelCell">
-                                        <label htmlFor="password2">Confirm new password</label>
-                                    </div>
-                                    <div className="mx_UserSettings_profileInputCell">
-                                        <input id="password2" ref="password2" value={ this.state.password2 } />
-                                    </div>
-                                </div>
-
-                            </div>                        
-
-                            <div className="mx_UserSettings_avatarPicker">
-                                <div className="mx_UserSettings_avatarPicker_edit" onClick={this.editAvatar}></div>
-                            </div>
-                        </div>
-
-                        <div className="mx_UserSettings_logout">
-                            <div className="mx_UserSettings_button" onClick={this.onLogoutClicked}>Log out</div>
-                        </div>
-
-                        <h2>Notifications</h2>
-
-                        <div className="mx_UserSettings_section">
-                            <div className="mx_UserSettings_notifTable">
-                                <div className="mx_UserSettings_notifTableRow">
-                                    <div className="mx_UserSettings_notifInputCell">
-                                        <input id="enableNotifications" ref="enableNotifications" type="checkbox" checked={ this.state.enableNotifications } onChange={ this.onEnableNotificationsChange } />
-                                    </div>
-                                    <div className="mx_UserSettings_notifLabelCell">
-                                        <label htmlFor="enableNotifications">Enable desktop notifications</label>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-
-                        <h2>Advanced</h2>
-
-                        <div className="mx_UserSettings_section">
-                            <div className="mx_UserSettings_advanced">
-                                Version {this.state.clientVersion}
-                            </div>
-                        </div>
-
-                        <div className="mx_UserSettings_save">
-                            <div className="mx_UserSettings_spinner">{ saving }</div>
-                            <div className="mx_UserSettings_button" onClick={this.onSaveClicked}>Save and close</div>
-                        </div>
-                    </div>
-                );
-        }
-    }
-});
diff --git a/src/skins/vector/views/pages/MatrixChat.js b/src/skins/vector/views/pages/MatrixChat.js
deleted file mode 100644
index 8083902ce7..0000000000
--- a/src/skins/vector/views/pages/MatrixChat.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var sdk = require('matrix-react-sdk')
-
-var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat')
-
-var dis = require('matrix-react-sdk/lib/dispatcher');
-var Matrix = require("matrix-js-sdk");
-var ContextualMenu = require("../../../../ContextualMenu");
-
-module.exports = React.createClass({
-    displayName: 'MatrixChat',
-    mixins: [MatrixChatController],
-
-    getInitialState: function() {
-        return {
-            width: 10000,
-        }
-    },
-
-    componentDidMount: function() {
-        window.addEventListener('resize', this.handleResize);
-        this.handleResize();
-    },
-
-    componentWillUnmount: function() {
-        window.removeEventListener('resize', this.handleResize);
-    },
-
-    onAliasClick: function(event, alias) {
-        event.preventDefault();
-        dis.dispatch({action: 'view_room_alias', room_alias: alias});
-    },
-
-    onUserClick: function(event, userId) {
-        event.preventDefault();
-        var MemberInfo = sdk.getComponent('molecules.MemberInfo');
-        var member = new Matrix.RoomMember(null, userId);
-        ContextualMenu.createMenu(MemberInfo, {
-            member: member,
-            right: window.innerWidth - event.pageX,
-            top: event.pageY
-        });
-    },
-
-    handleResize: function(e) {
-        var hideLhsThreshold = 1000;
-        var showLhsThreshold = 1000;
-        var hideRhsThreshold = 820;
-        var showRhsThreshold = 820;
-
-        if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) {
-            dis.dispatch({ action: 'hide_left_panel' });
-        }
-        if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) {
-            dis.dispatch({ action: 'show_left_panel' });
-        }
-        if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) {
-            dis.dispatch({ action: 'hide_right_panel' });
-        }
-        if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) {
-            dis.dispatch({ action: 'show_right_panel' });
-        }
-
-        this.setState({width: window.innerWidth});
-    },
-
-    onRoomCreated: function(room_id) {
-        dis.dispatch({
-            action: "view_room",
-            room_id: room_id,
-        });
-    },
-
-    render: function() {
-        var LeftPanel = sdk.getComponent('organisms.LeftPanel');
-        var RoomView = sdk.getComponent('organisms.RoomView');
-        var RightPanel = sdk.getComponent('organisms.RightPanel');
-        var Login = sdk.getComponent('templates.Login');
-        var UserSettings = sdk.getComponent('organisms.UserSettings');
-        var Register = sdk.getComponent('templates.Register');
-        var CreateRoom = sdk.getComponent('organisms.CreateRoom');
-        var RoomDirectory = sdk.getComponent('organisms.RoomDirectory');
-        var MatrixToolbar = sdk.getComponent('molecules.MatrixToolbar');
-        var Notifier = sdk.getComponent('organisms.Notifier');
-
-        if (this.state.logged_in && this.state.ready) {
-            var page_element;
-            var right_panel = "";
-
-            switch (this.state.page_type) {
-                case this.PageTypes.RoomView:
-                    page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
-                    right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} />
-                    break;
-                case this.PageTypes.UserSettings:
-                    page_element = <UserSettings roomId={this.state.currentRoom} />
-                    right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
-                    break;
-                case this.PageTypes.CreateRoom:
-                    page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/>
-                    right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
-                    break;
-                case this.PageTypes.RoomDirectory:
-                    page_element = <RoomDirectory />
-                    right_panel = <RightPanel collapsed={this.state.collapse_rhs}/>
-                    break;
-            }
-
-            // TODO: Fix duplication here and do conditionals like we do above
-            if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) {
-                return (
-                        <div className="mx_MatrixChat_wrapper">
-                            <MatrixToolbar />
-                            <div className="mx_MatrixChat mx_MatrixChat_toolbarShowing">
-                                <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} />
-                                <main className="mx_MatrixChat_middlePanel">
-                                    {page_element}
-                                </main>
-                                {right_panel}
-                            </div>
-                        </div>
-                );
-            }
-            else {
-                return (
-                        <div className="mx_MatrixChat">
-                            <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} />
-                            <main className="mx_MatrixChat_middlePanel">
-                                {page_element}
-                            </main>
-                            {right_panel}
-                        </div>
-                );
-            }
-        } else if (this.state.logged_in) {
-            var Spinner = sdk.getComponent('atoms.Spinner');
-            return (
-                <Spinner />
-            );
-        } else if (this.state.screen == 'register') {
-            return (
-                <Register onLoggedIn={this.onLoggedIn} clientSecret={this.state.register_client_secret}
-                    sessionId={this.state.register_session_id} idSid={this.state.register_id_sid}
-                    hsUrl={this.state.register_hs_url} isUrl={this.state.register_is_url}
-                    registrationUrl={this.props.registrationUrl}
-                />
-            );
-        } else {
-            return (
-                <Login onLoggedIn={this.onLoggedIn} />
-            );
-        }
-    }
-});
\ No newline at end of file
diff --git a/src/skins/vector/views/templates/Login.js b/src/skins/vector/views/templates/Login.js
deleted file mode 100644
index 8bd9334e27..0000000000
--- a/src/skins/vector/views/templates/Login.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-var ReactDOM = require('react-dom');
-
-var sdk = require('matrix-react-sdk')
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
-
-var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login')
-
-var config = require('../../../../../config.json');
-
-module.exports = React.createClass({
-    displayName: 'Login',
-    mixins: [LoginController],
-
-    getInitialState: function() {
-        // TODO: factor out all localstorage stuff into its own home.
-        // This is common to Login, Register and MatrixClientPeg
-        var localStorage = window.localStorage;
-        var hs_url, is_url;
-        if (localStorage) {
-            hs_url = localStorage.getItem("mx_hs_url");
-            is_url = localStorage.getItem("mx_is_url");
-        }
-
-        return {
-            customHsUrl: hs_url || config.default_hs_url,
-            customIsUrl: is_url || config.default_is_url,
-            serverConfigVisible: (hs_url && hs_url !== config.default_hs_url ||
-                                  is_url && is_url !== config.default_is_url)
-        };
-    },
-
-    componentDidMount: function() {
-        this.onHSChosen();
-    },
-
-    componentDidUpdate: function() {
-        if (!this.state.focusFired && this.refs.user) {
-            this.refs.user.focus();
-            this.setState({ focusFired: true });
-        }
-    },
-
-    getHsUrl: function() {
-        if (this.state.serverConfigVisible) {
-            return this.state.customHsUrl;
-        } else {
-            return config.default_hs_url;
-        }
-    },
-
-    getIsUrl: function() {
-        if (this.state.serverConfigVisible) {
-            return this.state.customIsUrl;
-        } else {
-            return config.default_is_url;
-        }
-    },
-
-    onServerConfigVisibleChange: function(ev) {
-        this.setState({
-            serverConfigVisible: ev.target.checked
-        }, this.onHSChosen);
-    },
-
-    /**
-     * Gets the form field values for the current login stage
-     */
-    getFormVals: function() {
-        return {
-            'username': this.refs.user.value.trim(),
-            'password': this.refs.pass.value.trim()
-        };
-    },
-
-    onHsUrlChanged: function() {
-        var newHsUrl = this.refs.serverConfig.getHsUrl().trim();
-        var newIsUrl = this.refs.serverConfig.getIsUrl().trim();
-
-        if (newHsUrl == this.state.customHsUrl &&
-            newIsUrl == this.state.customIsUrl)
-        {
-            return;
-        }
-        else {
-            this.setState({
-                customHsUrl: newHsUrl,
-                customIsUrl: newIsUrl,
-            });
-        }
-
-        // XXX: why are we replacing the MatrixClientPeg here when we're about
-        // to do it again 1s later in the setTimeout to onHSChosen? -- matthew
-        // Commenting it out for now to see what breaks.
-        /*
-        MatrixClientPeg.replaceUsingUrls(
-            this.getHsUrl(),
-            this.getIsUrl()
-        );
-        this.setState({
-            hs_url: this.getHsUrl(),
-            is_url: this.getIsUrl()
-        });
-        */
-
-        // XXX: HSes do not have to offer password auth, so we
-        // need to update and maybe show a different component
-        // when a new HS is entered.
-        if (this.updateHsTimeout) {
-            clearTimeout(this.updateHsTimeout);
-        }
-        var self = this;
-        this.updateHsTimeout = setTimeout(function() {
-            self.onHSChosen();
-        }, 1000);
-    },
-
-    componentForStep: function(step) {
-        switch (step) {
-            case 'choose_hs':
-            case 'fetch_stages':
-                var serverConfigStyle = {};
-                serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
-                var ServerConfig = sdk.getComponent("molecules.ServerConfig");
-
-                return (
-                    <div>
-                        <input className="mx_Login_checkbox" id="advanced" type="checkbox" checked={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
-                        <label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label>
-                        <div style={serverConfigStyle}>
-                            <ServerConfig ref="serverConfig"
-                                defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
-                                onHsUrlChanged={this.onHsUrlChanged}
-                            />
-                        </div>
-                    </div>
-                );
-            // XXX: clearly these should be separate organisms
-            case 'stage_m.login.password':
-                return (
-                    <div>
-                        <form onSubmit={this.onUserPassEntered}>
-                        <input className="mx_Login_field" ref="user" type="text" value={this.state.username} onChange={this.onUsernameChanged} placeholder="Email or user name" /><br />
-                        <input className="mx_Login_field" ref="pass" type="password" value={this.state.password} onChange={this.onPasswordChanged} placeholder="Password" /><br />
-                        { this.componentForStep('choose_hs') }
-                        <input className="mx_Login_submit" type="submit" value="Log in" />
-                        </form>
-                    </div>
-                );
-            case 'stage_m.login.cas':
-                var CasLogin = sdk.getComponent('organisms.CasLogin');
-                return (
-                    <CasLogin />
-                );
-        }
-    },
-
-    onUsernameChanged: function(ev) {
-        this.setState({username: ev.target.value});
-    },
-
-    onPasswordChanged: function(ev) {
-        this.setState({password: ev.target.value});
-    },
-
-    loginContent: function() {
-        var Loader = sdk.getComponent("atoms.Spinner");
-        var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
-        return (
-            <div>
-                <h2>Sign in</h2>
-                {this.componentForStep(this.state.step)}
-                <div className="mx_Login_error">
-                        { loader }
-                        {this.state.errorText}
-                </div>
-                <a className="mx_Login_create" onClick={this.showRegister} href="#">Create a new account</a>
-                <br/>
-                <div className="mx_Login_links">
-                    <a href="https://medium.com/@Vector">blog</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
-                    <a href="https://twitter.com/@VectorCo">twitter</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
-                    <a href="https://github.com/vector-im/vector-web">github</a>&nbsp;&nbsp;&middot;&nbsp;&nbsp;
-                    <a href="https://matrix.org">powered by Matrix</a>
-                </div>
-            </div>
-        );
-    },
-
-    render: function() {
-        return (
-            <div className="mx_Login">
-                <div className="mx_Login_box">
-                    <div className="mx_Login_logo">
-                        <img  src="img/logo.png" width="249" height="78" alt="vector"/>
-                    </div>
-                    {this.loginContent()}
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/skins/vector/views/templates/Register.js b/src/skins/vector/views/templates/Register.js
deleted file mode 100644
index 945d607c9e..0000000000
--- a/src/skins/vector/views/templates/Register.js
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var React = require('react');
-
-var sdk = require('matrix-react-sdk')
-var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
-
-var RegisterController = require('../../../../controllers/templates/Register')
-
-var config = require('../../../../../config.json');
-
-module.exports = React.createClass({
-    displayName: 'Register',
-    mixins: [RegisterController],
-
-    getInitialState: function() {
-        // TODO: factor out all localstorage stuff into its own home.
-        // This is common to Login, Register and MatrixClientPeg
-        var localStorage = window.localStorage;
-        var hs_url, is_url;
-        if (localStorage) {
-            hs_url = localStorage.getItem("mx_hs_url");
-            is_url = localStorage.getItem("mx_is_url");
-        }
-
-        // make sure we have our MatrixClient set up whatever
-        // Useful for debugging only.
-        // MatrixClientPeg.replaceUsingUrls(
-        //     hs_url || config.default_hs_url,
-        //     is_url || config.default_is_url
-        // );
-
-        return {
-            customHsUrl: hs_url || config.default_hs_url,
-            customIsUrl: is_url || config.default_is_url,
-            serverConfigVisible: (hs_url && hs_url !== config.default_hs_url ||
-                                  is_url && is_url !== config.default_is_url)
-        }
-    },
-
-    getRegFormVals: function() {
-        return {
-            email: this.refs.email.value.trim(),
-            username: this.refs.username.value.trim(),
-            password: this.refs.password.value.trim(),
-            confirmPassword: this.refs.confirmPassword.value.trim()
-        };
-    },
-
-    getHsUrl: function() {
-        if (this.state.serverConfigVisible) {
-            return this.state.customHsUrl;
-        } else {
-            return config.default_hs_url;
-        }
-    },
-
-    getIsUrl: function() {
-        if (this.state.serverConfigVisible) {
-            return this.state.customIsUrl;
-        } else {
-            return config.default_is_url;
-        }
-    },
-
-    onServerConfigVisibleChange: function(ev) {
-        this.setState({
-            serverConfigVisible: ev.target.checked
-        });
-    },
-
-    onServerUrlChanged: function(newUrl) {
-        this.setState({
-            customHsUrl: this.refs.serverConfig.getHsUrl(),
-            customIsUrl: this.refs.serverConfig.getIsUrl(),
-        });
-        this.forceUpdate();
-    },
-
-    onProfileContinueClicked: function() {
-        this.onAccountReady();
-    },
-
-    componentForStep: function(step) {
-        switch (step) {
-            case 'initial':
-                var serverConfigStyle = {};
-                serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
-                var ServerConfig = sdk.getComponent("molecules.ServerConfig");
-                return (
-                    <div>
-                        <form onSubmit={this.onInitialStageSubmit}>
-                        <input className="mx_Login_field" type="text" ref="email" autoFocus={true} placeholder="Email address" defaultValue={this.savedParams.email} /><br />
-                        <input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} /><br />
-                        <input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br />
-                        <input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br />
-
-                        <input className="mx_Login_checkbox" id="advanced" type="checkbox" checked={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
-                        <label htmlFor="advanced">Use custom server options (advanced)</label>
-                        <div style={serverConfigStyle}>
-                        <ServerConfig ref="serverConfig"
-                            defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
-                            onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} />
-                        </div>
-                        <br />
-                        <input className="mx_Login_submit" type="submit" value="Register" />
-                        </form>
-                    </div>
-                );
-            // XXX: clearly these should be separate organisms
-            case 'stage_m.login.email.identity':
-                return (
-                    <div>
-                        Please check your email to continue registration.
-                    </div>
-                );
-            case 'stage_m.login.recaptcha':
-                return (
-                    <div ref="recaptchaContainer">
-                        This Home Server would like to make sure you are not a robot
-                        <div id="mx_recaptcha"></div>
-                    </div>
-                );
-        }
-    },
-
-    registerContent: function() {
-        if (this.state.busy) {
-            var Loader = sdk.getComponent("atoms.Spinner");            
-            return (
-                <Loader />
-            );
-        } else if (this.state.step == 'profile') {
-            var ChangeDisplayName = sdk.getComponent('molecules.ChangeDisplayName');
-            var ChangeAvatar = sdk.getComponent('molecules.ChangeAvatar');
-            return (
-                <div className="mx_Login_profile">
-                    Set a display name:
-                    <ChangeDisplayName />
-                    Upload an avatar:
-                    <ChangeAvatar initialAvatarUrl={MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl)} />
-                    <button onClick={this.onProfileContinueClicked}>Continue</button>
-                </div>
-            );
-        } else {
-            return (
-                <div>
-                    <h2>Create an account</h2>
-                    {this.componentForStep(this.state.step)}
-                    <div className="mx_Login_error">{this.state.errorText}</div>
-                    <a className="mx_Login_create" onClick={this.showLogin} href="#">I already have an account</a>
-                </div>
-            );
-        }
-    },
-
-    onBadFields: function(bad) {
-        var keys = Object.keys(bad);
-        var strings = [];
-        for (var i = 0; i < keys.length; ++i) {
-            switch (bad[keys[i]]) {
-                case this.FieldErrors.PasswordMismatch:
-                    strings.push("Passwords don't match");
-                    break;
-                case this.FieldErrors.Missing:
-                    strings.push("Missing "+keys[i]);
-                    break;
-                case this.FieldErrors.TooShort:
-                    strings.push(keys[i]+" is too short");
-                    break;
-                case this.FieldErrors.InUse:
-                    strings.push(keys[i]+" is already taken");
-                    break;
-                case this.FieldErrors.Length:
-                    strings.push(keys[i] + " is not long enough.");
-                    break;
-                default:
-                    console.error("Unhandled FieldError: %s", bad[keys[i]]);
-                    break;
-            }
-        }
-        var errtxt = strings.join(', ');
-        this.setState({
-            errorText: errtxt
-        });
-    },
-
-    render: function() {
-        return (
-            <div className="mx_Login">
-                <div className="mx_Login_box">
-                    <div className="mx_Login_logo">
-                        <img src="img/logo.png" width="249" height="78" alt="vector"/>
-                    </div>
-                    {this.registerContent()}
-                </div>
-            </div>
-        );
-    }
-});
diff --git a/src/vector/index.js b/src/vector/index.js
index 87cbd0b661..efca1e8243 100644
--- a/src/vector/index.js
+++ b/src/vector/index.js
@@ -16,17 +16,28 @@ limitations under the License.
 
 'use strict';
 
+// CSS requires: just putting them here for now as CSS is going to be
+// refactored soon anyway
+require('../../vector/components.css');
+require('gemini-scrollbar/gemini-scrollbar.css');
+require('gfm.css/gfm.css');
+require('highlight.js/styles/github.css');
+
 var RunModernizrTests = require("./modernizr"); // this side-effects a global
 var React = require("react");
 var ReactDOM = require("react-dom");
 var sdk = require("matrix-react-sdk");
-sdk.loadSkin(require('../skins/vector/skindex'));
-sdk.loadModule(require('../modules/VectorConferenceHandler'));
+sdk.loadSkin(require('../component-index'));
+var VectorConferenceHandler = require('../VectorConferenceHandler');
+var configJson = require("../../config.json");
 
 var qs = require("querystring");
 
 var lastLocationHashSet = null;
 
+var CallHandler = require("matrix-react-sdk/lib/CallHandler");
+CallHandler.setConferenceHandler(VectorConferenceHandler);
+
 function checkBrowserFeatures(featureList) {
     if (!window.Modernizr) {
         console.error("Cannot check features - Modernizr global is missing.");
@@ -136,16 +147,20 @@ window.onload = function() {
 
 function loadApp() {
     if (validBrowser) {
-        var MatrixChat = sdk.getComponent('pages.MatrixChat');
+        var MatrixChat = sdk.getComponent('structures.MatrixChat');
         window.matrixChat = ReactDOM.render(
-            <MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />,
+            <MatrixChat
+                onNewScreen={onNewScreen}
+                registrationUrl={makeRegistrationUrl()}
+                ConferenceHandler={VectorConferenceHandler}
+                config={configJson} />,
             document.getElementById('matrixchat')
         );
     }
     else {
         console.error("Browser is missing required features.");
         // take to a different landing page to AWOOOOOGA at the user
-        var CompatibilityPage = require("../skins/vector/views/pages/CompatibilityPage");
+        var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
         window.matrixChat = ReactDOM.render(
             <CompatibilityPage onAccept={function() {
                 validBrowser = true;
diff --git a/vector/icons/browserconfig.xml b/vector/icons/browserconfig.xml
deleted file mode 100644
index a75bbad326..0000000000
--- a/vector/icons/browserconfig.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
-  <msapplication>
-    <tile>
-      <square70x70logo src="/icons/mstile-70x70.png"/>
-      <square150x150logo src="/icons/mstile-150x150.png"/>
-      <square310x310logo src="/icons/mstile-310x310.png"/>
-      <wide310x150logo src="/icons/mstile-310x150.png"/>
-      <TileColor>#da532c</TileColor>
-    </tile>
-  </msapplication>
-</browserconfig>
diff --git a/vector/index.html b/vector/index.html
index d86466797d..273cb4394f 100644
--- a/vector/index.html
+++ b/vector/index.html
@@ -3,27 +3,27 @@
   <head>
     <meta charset="utf-8">
     <title>Vector</title>
-    <link href='fonts/MyriadPro.css' rel='stylesheet' type='text/css'>
-    <link rel="apple-touch-icon" sizes="57x57" href="icons/apple-touch-icon-57x57.png">
-    <link rel="apple-touch-icon" sizes="60x60" href="icons/apple-touch-icon-60x60.png">
-    <link rel="apple-touch-icon" sizes="72x72" href="icons/apple-touch-icon-72x72.png">
-    <link rel="apple-touch-icon" sizes="76x76" href="icons/apple-touch-icon-76x76.png">
-    <link rel="apple-touch-icon" sizes="114x114" href="icons/apple-touch-icon-114x114.png">
-    <link rel="apple-touch-icon" sizes="120x120" href="icons/apple-touch-icon-120x120.png">
-    <link rel="apple-touch-icon" sizes="144x144" href="icons/apple-touch-icon-144x144.png">
-    <link rel="apple-touch-icon" sizes="152x152" href="icons/apple-touch-icon-152x152.png">
-    <link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon-180x180.png">
-    <link rel="icon" type="image/png" href="icons/favicon-32x32.png" sizes="32x32">
-    <link rel="icon" type="image/png" href="icons/android-chrome-192x192.png" sizes="192x192">
-    <link rel="icon" type="image/png" href="icons/favicon-96x96.png" sizes="96x96">
-    <link rel="icon" type="image/png" href="icons/favicon-16x16.png" sizes="16x16">
-    <link rel="manifest" href="icons/manifest.json">
-    <link rel="shortcut icon" href="icons/favicon.ico">
+    <link href='fonts/OpenSans.css' rel='stylesheet' type='text/css'>
+    <link rel="apple-touch-icon" sizes="57x57" href="vector-icons/apple-touch-icon-57x57.png">
+    <link rel="apple-touch-icon" sizes="60x60" href="vector-icons/apple-touch-icon-60x60.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="vector-icons/apple-touch-icon-72x72.png">
+    <link rel="apple-touch-icon" sizes="76x76" href="vector-icons/apple-touch-icon-76x76.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="vector-icons/apple-touch-icon-114x114.png">
+    <link rel="apple-touch-icon" sizes="120x120" href="vector-icons/apple-touch-icon-120x120.png">
+    <link rel="apple-touch-icon" sizes="144x144" href="vector-icons/apple-touch-icon-144x144.png">
+    <link rel="apple-touch-icon" sizes="152x152" href="vector-icons/apple-touch-icon-152x152.png">
+    <link rel="apple-touch-icon" sizes="180x180" href="vector-icons/apple-touch-icon-180x180.png">
+    <link rel="icon" type="image/png" href="vector-icons/favicon-32x32.png" sizes="32x32">
+    <link rel="icon" type="image/png" href="vector-icons/android-chrome-192x192.png" sizes="192x192">
+    <link rel="icon" type="image/png" href="vector-icons/favicon-96x96.png" sizes="96x96">
+    <link rel="icon" type="image/png" href="vector-icons/favicon-16x16.png" sizes="16x16">
+    <link rel="manifest" href="vector-icons/manifest.json">
+    <link rel="shortcut icon" href="vector-icons/favicon.ico">
     <meta name="apple-mobile-web-app-title" content="Vector">
     <meta name="application-name" content="Vector">
     <meta name="msapplication-TileColor" content="#da532c">
-    <meta name="msapplication-TileImage" content="icons/mstile-144x144.png">
-    <meta name="msapplication-config" content="icons/browserconfig.xml">
+    <meta name="msapplication-TileImage" content="vector-icons/mstile-144x144.png">
+    <meta name="msapplication-config" content="vector-icons/browserconfig.xml">
     <meta name="theme-color" content="#ffffff">
   </head>
   <body style="height: 100%;">
@@ -42,5 +42,6 @@
     <section id="matrixchat" style="height: 100%;"></section>
     <script src="bundle.js"></script>
     <link rel="stylesheet" href="bundle.css">
+    <img src="img/warning.svg" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
   </body>
 </html>
diff --git a/vector/icons/android-chrome-144x144.png b/vector/vector-icons/android-chrome-144x144.png
similarity index 100%
rename from vector/icons/android-chrome-144x144.png
rename to vector/vector-icons/android-chrome-144x144.png
diff --git a/vector/icons/android-chrome-192x192.png b/vector/vector-icons/android-chrome-192x192.png
similarity index 100%
rename from vector/icons/android-chrome-192x192.png
rename to vector/vector-icons/android-chrome-192x192.png
diff --git a/vector/icons/android-chrome-36x36.png b/vector/vector-icons/android-chrome-36x36.png
similarity index 100%
rename from vector/icons/android-chrome-36x36.png
rename to vector/vector-icons/android-chrome-36x36.png
diff --git a/vector/icons/android-chrome-48x48.png b/vector/vector-icons/android-chrome-48x48.png
similarity index 100%
rename from vector/icons/android-chrome-48x48.png
rename to vector/vector-icons/android-chrome-48x48.png
diff --git a/vector/icons/android-chrome-72x72.png b/vector/vector-icons/android-chrome-72x72.png
similarity index 100%
rename from vector/icons/android-chrome-72x72.png
rename to vector/vector-icons/android-chrome-72x72.png
diff --git a/vector/icons/android-chrome-96x96.png b/vector/vector-icons/android-chrome-96x96.png
similarity index 100%
rename from vector/icons/android-chrome-96x96.png
rename to vector/vector-icons/android-chrome-96x96.png
diff --git a/vector/icons/apple-touch-icon-114x114.png b/vector/vector-icons/apple-touch-icon-114x114.png
similarity index 100%
rename from vector/icons/apple-touch-icon-114x114.png
rename to vector/vector-icons/apple-touch-icon-114x114.png
diff --git a/vector/icons/apple-touch-icon-120x120.png b/vector/vector-icons/apple-touch-icon-120x120.png
similarity index 100%
rename from vector/icons/apple-touch-icon-120x120.png
rename to vector/vector-icons/apple-touch-icon-120x120.png
diff --git a/vector/icons/apple-touch-icon-144x144.png b/vector/vector-icons/apple-touch-icon-144x144.png
similarity index 100%
rename from vector/icons/apple-touch-icon-144x144.png
rename to vector/vector-icons/apple-touch-icon-144x144.png
diff --git a/vector/icons/apple-touch-icon-152x152.png b/vector/vector-icons/apple-touch-icon-152x152.png
similarity index 100%
rename from vector/icons/apple-touch-icon-152x152.png
rename to vector/vector-icons/apple-touch-icon-152x152.png
diff --git a/vector/icons/apple-touch-icon-180x180.png b/vector/vector-icons/apple-touch-icon-180x180.png
similarity index 100%
rename from vector/icons/apple-touch-icon-180x180.png
rename to vector/vector-icons/apple-touch-icon-180x180.png
diff --git a/vector/icons/apple-touch-icon-57x57.png b/vector/vector-icons/apple-touch-icon-57x57.png
similarity index 100%
rename from vector/icons/apple-touch-icon-57x57.png
rename to vector/vector-icons/apple-touch-icon-57x57.png
diff --git a/vector/icons/apple-touch-icon-60x60.png b/vector/vector-icons/apple-touch-icon-60x60.png
similarity index 100%
rename from vector/icons/apple-touch-icon-60x60.png
rename to vector/vector-icons/apple-touch-icon-60x60.png
diff --git a/vector/icons/apple-touch-icon-72x72.png b/vector/vector-icons/apple-touch-icon-72x72.png
similarity index 100%
rename from vector/icons/apple-touch-icon-72x72.png
rename to vector/vector-icons/apple-touch-icon-72x72.png
diff --git a/vector/icons/apple-touch-icon-76x76.png b/vector/vector-icons/apple-touch-icon-76x76.png
similarity index 100%
rename from vector/icons/apple-touch-icon-76x76.png
rename to vector/vector-icons/apple-touch-icon-76x76.png
diff --git a/vector/icons/apple-touch-icon-precomposed.png b/vector/vector-icons/apple-touch-icon-precomposed.png
similarity index 100%
rename from vector/icons/apple-touch-icon-precomposed.png
rename to vector/vector-icons/apple-touch-icon-precomposed.png
diff --git a/vector/icons/apple-touch-icon.png b/vector/vector-icons/apple-touch-icon.png
similarity index 100%
rename from vector/icons/apple-touch-icon.png
rename to vector/vector-icons/apple-touch-icon.png
diff --git a/vector/vector-icons/browserconfig.xml b/vector/vector-icons/browserconfig.xml
new file mode 100644
index 0000000000..012a1dad28
--- /dev/null
+++ b/vector/vector-icons/browserconfig.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<browserconfig>
+  <msapplication>
+    <tile>
+      <square70x70logo src="/vector-icons/mstile-70x70.png"/>
+      <square150x150logo src="/vector-icons/mstile-150x150.png"/>
+      <square310x310logo src="/vector-icons/mstile-310x310.png"/>
+      <wide310x150logo src="/vector-icons/mstile-310x150.png"/>
+      <TileColor>#da532c</TileColor>
+    </tile>
+  </msapplication>
+</browserconfig>
diff --git a/vector/icons/favicon-16x16.png b/vector/vector-icons/favicon-16x16.png
similarity index 100%
rename from vector/icons/favicon-16x16.png
rename to vector/vector-icons/favicon-16x16.png
diff --git a/vector/icons/favicon-32x32.png b/vector/vector-icons/favicon-32x32.png
similarity index 100%
rename from vector/icons/favicon-32x32.png
rename to vector/vector-icons/favicon-32x32.png
diff --git a/vector/icons/favicon-96x96.png b/vector/vector-icons/favicon-96x96.png
similarity index 100%
rename from vector/icons/favicon-96x96.png
rename to vector/vector-icons/favicon-96x96.png
diff --git a/vector/icons/favicon.ico b/vector/vector-icons/favicon.ico
similarity index 100%
rename from vector/icons/favicon.ico
rename to vector/vector-icons/favicon.ico
diff --git a/vector/icons/manifest.json b/vector/vector-icons/manifest.json
similarity index 100%
rename from vector/icons/manifest.json
rename to vector/vector-icons/manifest.json
diff --git a/vector/icons/mstile-144x144.png b/vector/vector-icons/mstile-144x144.png
similarity index 100%
rename from vector/icons/mstile-144x144.png
rename to vector/vector-icons/mstile-144x144.png
diff --git a/vector/icons/mstile-150x150.png b/vector/vector-icons/mstile-150x150.png
similarity index 100%
rename from vector/icons/mstile-150x150.png
rename to vector/vector-icons/mstile-150x150.png
diff --git a/vector/icons/mstile-310x150.png b/vector/vector-icons/mstile-310x150.png
similarity index 100%
rename from vector/icons/mstile-310x150.png
rename to vector/vector-icons/mstile-310x150.png
diff --git a/vector/icons/mstile-310x310.png b/vector/vector-icons/mstile-310x310.png
similarity index 100%
rename from vector/icons/mstile-310x310.png
rename to vector/vector-icons/mstile-310x310.png
diff --git a/vector/icons/mstile-70x70.png b/vector/vector-icons/mstile-70x70.png
similarity index 100%
rename from vector/icons/mstile-70x70.png
rename to vector/vector-icons/mstile-70x70.png
diff --git a/webpack.config.js b/webpack.config.js
index 929e57d71b..c19438a0df 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,5 +1,8 @@
 var path = require('path');
 var webpack = require('webpack');
+var ExtractTextPlugin = require("extract-text-webpack-plugin");
+
+var olm_path = path.resolve('./node_modules/olm');
 
 module.exports = {
     module: {
@@ -9,6 +12,8 @@ module.exports = {
         loaders: [
             { test: /\.json$/, loader: "json" },
             { test: /\.js$/, loader: "babel", include: path.resolve('./src') },
+            // css-raw-loader loads CSS but doesn't try to treat url()s as require()s
+            { test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") },
         ]
     },
     output: {
@@ -29,15 +34,40 @@ module.exports = {
             // alias any requires to the react module to the one in our path, otherwise
             // we tend to get the react source included twice when using npm link.
             react: path.resolve('./node_modules/react'),
+
+            // matrix-js-sdk will use olm if it is available,
+            // but does not explicitly depend on it. Pull it
+            // in from node_modules if it's there.
+            olm: olm_path,
         },
     },
     plugins: [
-        new webpack.IgnorePlugin(/^olm/),
         new webpack.DefinePlugin({
             'process.env': {
                 NODE_ENV: JSON.stringify(process.env.NODE_ENV)
             }
-        })
+        }),
+
+        new ExtractTextPlugin("bundle.css", {
+            allChunks: true
+        }),
+
+        // olm.js includes "require 'fs'", which is never
+        // executed in the browser. Ignore it.
+        new webpack.IgnorePlugin(/^fs$/, /node_modules\/olm$/)
     ],
     devtool: 'source-map'
 };
+
+// ignore olm.js if it's not installed.
+(function() {
+    var fs = require('fs');
+    try {
+        fs.lstatSync(olm_path);
+        console.log("Olm is installed; including it in webpack bundle");
+    } catch (e) {
+        module.exports.plugins.push(
+            new webpack.IgnorePlugin(/^olm$/)
+        );
+    }
+}) ();