diff --git a/lib/static_resources.js b/lib/static_resources.js
index b994c3842d6b8abe2f939c53caf349150c4e521c..7a6863eab6dc14be472780fe7f44b08d8859fd97 100644
--- a/lib/static_resources.js
+++ b/lib/static_resources.js
@@ -16,60 +16,60 @@ var i18n = require('./i18n'),
 
 // Common to browserid.js dialog.js
 var common_js = [
-  '/lib/jquery-1.7.1.min.js',
-  '/lib/winchan.js',
-  '/lib/underscore.js',
-  '/lib/bidbundle.js',
-  '/lib/ejs.js',
-  '/lib/micrajax.js',
-  '/lib/urlparse.js',
-  '/shared/javascript-extensions.js',
+  '/common/js/lib/jquery-1.7.1.min.js',
+  '/common/js/lib/winchan.js',
+  '/common/js/lib/underscore.js',
+  '/common/js/lib/bidbundle.js',
+  '/common/js/lib/ejs.js',
+  '/common/js/lib/micrajax.js',
+  '/common/js/lib/urlparse.js',
+  '/common/js/javascript-extensions.js',
   '/i18n/:locale/client.json',
-  '/shared/gettext.js',
-  '/shared/browserid.js',
-  '/lib/hub.js',
-  '/lib/dom-jquery.js',
-  '/lib/module.js',
-  '/lib/jschannel.js',
-  '/shared/templates.js',
-  '/shared/renderer.js',
-  '/shared/class.js',
-  '/shared/mediator.js',
-  '/shared/tooltip.js',
-  '/shared/validation.js',
-  '/shared/helpers.js',
-  '/shared/screens.js',
-  '/shared/browser-support.js',
-  '/shared/enable_cookies_url.js',
-  '/shared/wait-messages.js',
-  '/shared/error-messages.js',
-  '/shared/error-display.js',
-  '/shared/storage.js',
-  '/shared/xhr_transport.js',
-  '/shared/xhr.js',
-  '/shared/network.js',
-  '/shared/provisioning.js',
-  '/shared/user.js',
-  '/shared/modules/page_module.js',
-  '/shared/modules/xhr_delay.js',
-  '/shared/modules/xhr_disable_form.js',
-  '/shared/modules/cookie_check.js',
-  '/shared/modules/development.js'
+  '/common/js/gettext.js',
+  '/common/js/browserid.js',
+  '/common/js/lib/hub.js',
+  '/common/js/lib/dom-jquery.js',
+  '/common/js/lib/module.js',
+  '/common/js/lib/jschannel.js',
+  '/common/js/templates.js',
+  '/common/js/renderer.js',
+  '/common/js/class.js',
+  '/common/js/mediator.js',
+  '/common/js/tooltip.js',
+  '/common/js/validation.js',
+  '/common/js/helpers.js',
+  '/common/js/screens.js',
+  '/common/js/browser-support.js',
+  '/common/js/enable_cookies_url.js',
+  '/common/js/wait-messages.js',
+  '/common/js/error-messages.js',
+  '/common/js/error-display.js',
+  '/common/js/storage.js',
+  '/common/js/xhr_transport.js',
+  '/common/js/xhr.js',
+  '/common/js/network.js',
+  '/common/js/provisioning.js',
+  '/common/js/user.js',
+  '/common/js/modules/page_module.js',
+  '/common/js/modules/xhr_delay.js',
+  '/common/js/modules/xhr_disable_form.js',
+  '/common/js/modules/cookie_check.js',
+  '/common/js/modules/development.js'
 ];
 
 var browserid_min_js = '/production/:locale/browserid.js';
 var browserid_js = und.flatten([
   common_js,
   [
-    '/pages/page_helpers.js',
-    '/pages/index.js',
-    '/pages/start.js',
-    '/pages/verify_secondary_address.js',
-    '/pages/forgot.js',
-    '/pages/manage_account.js',
-    '/pages/signin.js',
-    '/pages/signup.js',
-    '/pages/about.js'
+    '/pages/js/page_helpers.js',
+    '/pages/js/index.js',
+    '/pages/js/start.js',
+    '/pages/js/verify_secondary_address.js',
+    '/pages/js/forgot.js',
+    '/pages/js/manage_account.js',
+    '/pages/js/signin.js',
+    '/pages/js/signup.js',
+    '/pages/js/about.js'
   ]
 ]);
 
@@ -77,76 +77,76 @@ var dialog_min_js = '/production/:locale/dialog.js';
 var dialog_js = und.flatten([
   common_js,
   [
-    '/shared/command.js',
-    '/shared/history.js',
-    '/shared/state_machine.js',
+    '/common/js/command.js',
+    '/common/js/history.js',
+    '/common/js/state_machine.js',
 
-    '/shared/models/models.js',
-    '/shared/models/interaction_data.js',
+    '/common/js/models/models.js',
+    '/common/js/models/interaction_data.js',
 
-    '/shared/modules/interaction_data.js',
+    '/common/js/modules/interaction_data.js',
 
-    '/dialog/resources/internal_api.js',
-    '/dialog/resources/helpers.js',
-    '/dialog/resources/state.js',
-    '/dialog/resources/screen_size_hacks.js',
+    '/dialog/js/misc/internal_api.js',
+    '/dialog/js/misc/helpers.js',
+    '/dialog/js/misc/state.js',
+    '/dialog/js/misc/screen_size_hacks.js',
 
-    '/dialog/controllers/actions.js',
-    '/dialog/controllers/dialog.js',
-    '/dialog/controllers/authenticate.js',
-    '/dialog/controllers/check_registration.js',
-    '/dialog/controllers/pick_email.js',
-    '/dialog/controllers/add_email.js',
-    '/dialog/controllers/required_email.js',
-    '/dialog/controllers/verify_primary_user.js',
-    '/dialog/controllers/provision_primary_user.js',
-    '/dialog/controllers/primary_user_provisioned.js',
-    '/dialog/controllers/generate_assertion.js',
-    '/dialog/controllers/is_this_your_computer.js',
-    '/dialog/controllers/set_password.js',
-    '/dialog/controllers/rp_info.js',
-    '/dialog/start.js'
+    '/dialog/js/modules/actions.js',
+    '/dialog/js/modules/dialog.js',
+    '/dialog/js/modules/authenticate.js',
+    '/dialog/js/modules/check_registration.js',
+    '/dialog/js/modules/pick_email.js',
+    '/dialog/js/modules/add_email.js',
+    '/dialog/js/modules/required_email.js',
+    '/dialog/js/modules/verify_primary_user.js',
+    '/dialog/js/modules/provision_primary_user.js',
+    '/dialog/js/modules/primary_user_provisioned.js',
+    '/dialog/js/modules/generate_assertion.js',
+    '/dialog/js/modules/is_this_your_computer.js',
+    '/dialog/js/modules/set_password.js',
+    '/dialog/js/modules/rp_info.js',
+    '/dialog/js/start.js'
   ]]);
 
 exports.resources = {
   '/production/dialog.css': [
-    '/fonts/fonts_common.css',
-    '/fonts/fonts_dialog.css',
-    '/css/common.css',
-    '/dialog/css/popup.css',
+    '/common/fonts/fonts_common.css',
+    '/common/fonts/fonts_dialog.css',
+    '/common/css/style.css',
+    '/dialog/css/style.css',
     '/dialog/css/m.css'
   ],
   '/production/browserid.css': [
-    '/fonts/fonts_common.css',
-    '/fonts/fonts_mainsite.css',
-    '/css/common.css',
-    '/css/style.css',
-    '/css/m.css'
+    '/common/fonts/fonts_common.css',
+    '/common/fonts/fonts_pages.css',
+    '/common/css/style.css',
+    '/pages/css/style.css',
+    '/pages/css/m.css'
   ],
   '/production/ie8_main.css': [
-    '/css/ie8_common.css',
-    '/css/ie8_main.css'
+    '/common/css/ie8.css',
+    '/pages/css/ie8.css'
   ],
   '/production/ie8_dialog.css': [
-    '/css/ie8_common.css',
+    '/common/css/ie8.css',
     '/dialog/css/ie8.css'
   ],
   '/production/communication_iframe.js': [
-    '/lib/jschannel.js',
-    '/lib/winchan.js',
-    '/lib/underscore.js',
-    '/lib/bidbundle.js',
-    '/lib/hub.js',
-    '/lib/micrajax.js',
-    '/shared/javascript-extensions.js',
-    '/shared/browserid.js',
-    '/shared/mediator.js',
-    '/shared/helpers.js',
-    '/shared/storage.js',
-    '/shared/xhr_transport.js',
-    '/shared/xhr.js',
-    '/shared/network.js',
-    '/shared/user.js',
+    '/common/js/lib/jschannel.js',
+    '/common/js/lib/winchan.js',
+    '/common/js/lib/underscore.js',
+    '/common/js/lib/bidbundle.js',
+    '/common/js/lib/hub.js',
+    '/common/js/lib/micrajax.js',
+    '/common/js/javascript-extensions.js',
+    '/common/js/browserid.js',
+    '/common/js/mediator.js',
+    '/common/js/helpers.js',
+    '/common/js/storage.js',
+    '/common/js/xhr_transport.js',
+    '/common/js/xhr.js',
+    '/common/js/network.js',
+    '/common/js/user.js',
     '/communication_iframe/start.js'
   ],
   '/production/include.js': [
diff --git a/resources/static/css/ie8_common.css b/resources/static/common/css/ie8.css
similarity index 93%
rename from resources/static/css/ie8_common.css
rename to resources/static/common/css/ie8.css
index 99542f340d34480b5971bded5ad332c48bcfe6f0..c8ed84860842d538cb1764ae57d3d2d1c60109ac 100644
--- a/resources/static/css/ie8_common.css
+++ b/resources/static/common/css/ie8.css
@@ -11,7 +11,7 @@
 .submit button:focus,
 .submit .button:hover,
 .submit .button:focus {
-    background-image: url("/i/button-arrow.png");
+    background-image: url("/common/i/button-arrow.png");
     background-position: center right;
     background-repeat: no-repeat;
 }
diff --git a/resources/static/css/common.css b/resources/static/common/css/style.css
similarity index 83%
rename from resources/static/css/common.css
rename to resources/static/common/css/style.css
index 2cb6dbf41f259670449f1332b68b9ec3a750b6a0..c29a95bffb2b1b82c387529fbe41b5b656a71e2f 100644
--- a/resources/static/css/common.css
+++ b/resources/static/common/css/style.css
@@ -209,13 +209,13 @@ button::-moz-focus-inner, .button::-moz-focus-inner {
 .submit button {
     padding: 6px 45px 7px 10px;
     background-color: #4eb5e5;
-    background-image: url("/i/button-arrow.png");
-    background-image: url("/i/button-arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf));
-    background-image: url("/i/button-arrow.png"), -webkit-linear-gradient(top, #4eb5e5, #3196cf);
-    background-image: url("/i/button-arrow.png"),    -moz-linear-gradient(top, #4eb5e5, #3196cf);
-    background-image: url("/i/button-arrow.png"),     -ms-linear-gradient(top, #4eb5e5, #3196cf);
-    background-image: url("/i/button-arrow.png"),      -o-linear-gradient(top, #4eb5e5, #3196cf);
-    background-image: url("/i/button-arrow.png"),         linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-arrow.png");
+    background-image: url("/common/i/button-arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf));
+    background-image: url("/common/i/button-arrow.png"), -webkit-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-arrow.png"),    -moz-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-arrow.png"),     -ms-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-arrow.png"),      -o-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-arrow.png"),         linear-gradient(top, #4eb5e5, #3196cf);
     background-repeat: no-repeat, no-repeat;
     background-position: center right, center;
 }
@@ -226,13 +226,13 @@ button::-moz-focus-inner, .button::-moz-focus-inner {
 .submit .button:hover,
 .submit .button:focus {
     background-color: #43a6e2;
-    background-image: url("/i/button-arrow.png");
-    background-image: url("/i/button-arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#43a6e2), to(#277ac1));
-    background-image: url("/i/button-arrow.png"), -webkit-linear-gradient(top, #43a6e2, #277ac1);
-    background-image: url("/i/button-arrow.png"),    -moz-linear-gradient(top, #43a6e2, #277ac1);
-    background-image: url("/i/button-arrow.png"),     -ms-linear-gradient(top, #43a6e2, #277ac1);
-    background-image: url("/i/button-arrow.png"),      -o-linear-gradient(top, #43a6e2, #277ac1);
-    background-image: url("/i/button-arrow.png"),         linear-gradient(top, #43a6e2, #277ac1);
+    background-image: url("/common/i/button-arrow.png");
+    background-image: url("/common/i/button-arrow.png"), -webkit-gradient(linear, left top, left bottom, from(#43a6e2), to(#277ac1));
+    background-image: url("/common/i/button-arrow.png"), -webkit-linear-gradient(top, #43a6e2, #277ac1);
+    background-image: url("/common/i/button-arrow.png"),    -moz-linear-gradient(top, #43a6e2, #277ac1);
+    background-image: url("/common/i/button-arrow.png"),     -ms-linear-gradient(top, #43a6e2, #277ac1);
+    background-image: url("/common/i/button-arrow.png"),      -o-linear-gradient(top, #43a6e2, #277ac1);
+    background-image: url("/common/i/button-arrow.png"),         linear-gradient(top, #43a6e2, #277ac1);
 }
 
 button[disabled], .submit_disabled button, .submit_disabled .button,
@@ -369,12 +369,12 @@ footer .help {
 
 #wait, #delay, #error {
   background-color: #dadee1;
-  background-image: url("/i/grain.png"), -webkit-gradient(linear, left top, left bottom, from(#dadee1), to(#c7ccd0));
-  background-image: url("/i/grain.png"), -webkit-linear-gradient(top, #dadee1, #c7ccd0);
-  background-image: url("/i/grain.png"),    -moz-linear-gradient(top, #dadee1, #c7ccd0);
-  background-image: url("/i/grain.png"),     -ms-linear-gradient(top, #dadee1, #c7ccd0);
-  background-image: url("/i/grain.png"),      -o-linear-gradient(top, #dadee1, #c7ccd0);
-  background-image: url("/i/grain.png"),         linear-gradient(top, #dadee1, #c7ccd0);
+  background-image: url("/common/i/grain.png"), -webkit-gradient(linear, left top, left bottom, from(#dadee1), to(#c7ccd0));
+  background-image: url("/common/i/grain.png"), -webkit-linear-gradient(top, #dadee1, #c7ccd0);
+  background-image: url("/common/i/grain.png"),    -moz-linear-gradient(top, #dadee1, #c7ccd0);
+  background-image: url("/common/i/grain.png"),     -ms-linear-gradient(top, #dadee1, #c7ccd0);
+  background-image: url("/common/i/grain.png"),      -o-linear-gradient(top, #dadee1, #c7ccd0);
+  background-image: url("/common/i/grain.png"),         linear-gradient(top, #dadee1, #c7ccd0);
 }
 
 #wait, #delay {
diff --git a/resources/static/fonts/LICENSE.txt b/resources/static/common/fonts/LICENSE.txt
similarity index 100%
rename from resources/static/fonts/LICENSE.txt
rename to resources/static/common/fonts/LICENSE.txt
diff --git a/resources/static/fonts/OpenSans-Bold.eot b/resources/static/common/fonts/OpenSans-Bold.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-Bold.eot
rename to resources/static/common/fonts/OpenSans-Bold.eot
diff --git a/resources/static/fonts/OpenSans-Bold.ttf b/resources/static/common/fonts/OpenSans-Bold.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-Bold.ttf
rename to resources/static/common/fonts/OpenSans-Bold.ttf
diff --git a/resources/static/fonts/OpenSans-Bold.woff b/resources/static/common/fonts/OpenSans-Bold.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-Bold.woff
rename to resources/static/common/fonts/OpenSans-Bold.woff
diff --git a/resources/static/fonts/OpenSans-BoldItalic.eot b/resources/static/common/fonts/OpenSans-BoldItalic.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-BoldItalic.eot
rename to resources/static/common/fonts/OpenSans-BoldItalic.eot
diff --git a/resources/static/fonts/OpenSans-BoldItalic.ttf b/resources/static/common/fonts/OpenSans-BoldItalic.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-BoldItalic.ttf
rename to resources/static/common/fonts/OpenSans-BoldItalic.ttf
diff --git a/resources/static/fonts/OpenSans-BoldItalic.woff b/resources/static/common/fonts/OpenSans-BoldItalic.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-BoldItalic.woff
rename to resources/static/common/fonts/OpenSans-BoldItalic.woff
diff --git a/resources/static/fonts/OpenSans-Italic.eot b/resources/static/common/fonts/OpenSans-Italic.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-Italic.eot
rename to resources/static/common/fonts/OpenSans-Italic.eot
diff --git a/resources/static/fonts/OpenSans-Italic.ttf b/resources/static/common/fonts/OpenSans-Italic.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-Italic.ttf
rename to resources/static/common/fonts/OpenSans-Italic.ttf
diff --git a/resources/static/fonts/OpenSans-Italic.woff b/resources/static/common/fonts/OpenSans-Italic.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-Italic.woff
rename to resources/static/common/fonts/OpenSans-Italic.woff
diff --git a/resources/static/fonts/OpenSans-Light.eot b/resources/static/common/fonts/OpenSans-Light.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-Light.eot
rename to resources/static/common/fonts/OpenSans-Light.eot
diff --git a/resources/static/fonts/OpenSans-Light.ttf b/resources/static/common/fonts/OpenSans-Light.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-Light.ttf
rename to resources/static/common/fonts/OpenSans-Light.ttf
diff --git a/resources/static/fonts/OpenSans-Light.woff b/resources/static/common/fonts/OpenSans-Light.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-Light.woff
rename to resources/static/common/fonts/OpenSans-Light.woff
diff --git a/resources/static/fonts/OpenSans-LightItalic.eot b/resources/static/common/fonts/OpenSans-LightItalic.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-LightItalic.eot
rename to resources/static/common/fonts/OpenSans-LightItalic.eot
diff --git a/resources/static/fonts/OpenSans-LightItalic.ttf b/resources/static/common/fonts/OpenSans-LightItalic.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-LightItalic.ttf
rename to resources/static/common/fonts/OpenSans-LightItalic.ttf
diff --git a/resources/static/fonts/OpenSans-LightItalic.woff b/resources/static/common/fonts/OpenSans-LightItalic.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-LightItalic.woff
rename to resources/static/common/fonts/OpenSans-LightItalic.woff
diff --git a/resources/static/fonts/OpenSans-Regular.eot b/resources/static/common/fonts/OpenSans-Regular.eot
similarity index 100%
rename from resources/static/fonts/OpenSans-Regular.eot
rename to resources/static/common/fonts/OpenSans-Regular.eot
diff --git a/resources/static/fonts/OpenSans-Regular.ttf b/resources/static/common/fonts/OpenSans-Regular.ttf
similarity index 100%
rename from resources/static/fonts/OpenSans-Regular.ttf
rename to resources/static/common/fonts/OpenSans-Regular.ttf
diff --git a/resources/static/fonts/OpenSans-Regular.woff b/resources/static/common/fonts/OpenSans-Regular.woff
similarity index 100%
rename from resources/static/fonts/OpenSans-Regular.woff
rename to resources/static/common/fonts/OpenSans-Regular.woff
diff --git a/resources/static/common/fonts/fonts_common.css b/resources/static/common/fonts/fonts_common.css
new file mode 100644
index 0000000000000000000000000000000000000000..b00de80c43026259809ad4440d73568ec5cddf7f
--- /dev/null
+++ b/resources/static/common/fonts/fonts_common.css
@@ -0,0 +1,33 @@
+@font-face {
+  font-family: 'Open Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: url('/common/fonts/OpenSans-Regular.eot');
+  src: local('Open Sans'),
+       local('OpenSans'),
+       url('/common/fonts/OpenSans-Regular.eot') format('embedded-opentype'),
+       url('/common/fonts/OpenSans-Regular.woff') format('woff'),
+       url('/common/fonts/OpenSans-Regular.ttf') format('truetype');
+}
+@font-face {
+  font-family: 'Open Sans';
+  font-style: normal;
+  font-weight: 300;
+  src: url('/common/fonts/OpenSans-Light.eot');
+  src: local('Open Sans Light'),
+       local('OpenSans-Light'),
+       url('/common/fonts/OpenSans-Light.eot') format('embedded-opentype'),
+       url('/common/fonts/OpenSans-Light.woff') format('woff'),
+       url('/common/fonts/OpenSans-Light.ttf') format('truetype');
+}
+@font-face {
+  font-family: 'Open Sans';
+  font-style: normal;
+  font-weight: 700;
+  src: url('/common/fonts/OpenSans-Bold.eot');
+  src: local('Open Sans Bold'),
+       local('OpenSans-Bold'),
+       url('/common/fonts/OpenSans-Bold.eot') format('embedded-opentype'),
+       url('/common/fonts/OpenSans-Bold.woff') format('woff'),
+       url('/common/fonts/OpenSans-Bold.ttf') format('truetype');
+}
diff --git a/resources/static/fonts/fonts_dialog.css b/resources/static/common/fonts/fonts_dialog.css
similarity index 100%
rename from resources/static/fonts/fonts_dialog.css
rename to resources/static/common/fonts/fonts_dialog.css
diff --git a/resources/static/common/fonts/fonts_pages.css b/resources/static/common/fonts/fonts_pages.css
new file mode 100644
index 0000000000000000000000000000000000000000..e2629c17da3d691d54497910e9b8a96f2707aba8
--- /dev/null
+++ b/resources/static/common/fonts/fonts_pages.css
@@ -0,0 +1,12 @@
+@font-face {
+  font-family: 'Open Sans';
+  font-style: italic;
+  font-weight: 400;
+  src: url('/common/fonts/OpenSans-Italic.eot');
+  src: local('Open Sans Italic'),
+       local('OpenSans-Italic'),
+       url('/common/fonts/OpenSans-Italic.eot') format('embedded-opentype'),
+       url('/common/fonts/OpenSans-Italic.woff') format('woff'),
+       url('/common/fonts/OpenSans-Italic.ttf') format('truetype');
+}
+
diff --git a/resources/static/i/button-arrow.png b/resources/static/common/i/button-arrow.png
similarity index 100%
rename from resources/static/i/button-arrow.png
rename to resources/static/common/i/button-arrow.png
diff --git a/resources/static/i/grain.png b/resources/static/common/i/grain.png
similarity index 100%
rename from resources/static/i/grain.png
rename to resources/static/common/i/grain.png
diff --git a/resources/static/shared/browser-support.js b/resources/static/common/js/browser-support.js
similarity index 100%
rename from resources/static/shared/browser-support.js
rename to resources/static/common/js/browser-support.js
diff --git a/resources/static/shared/browserid.js b/resources/static/common/js/browserid.js
similarity index 100%
rename from resources/static/shared/browserid.js
rename to resources/static/common/js/browserid.js
diff --git a/resources/static/shared/class.js b/resources/static/common/js/class.js
similarity index 100%
rename from resources/static/shared/class.js
rename to resources/static/common/js/class.js
diff --git a/resources/static/shared/command.js b/resources/static/common/js/command.js
similarity index 100%
rename from resources/static/shared/command.js
rename to resources/static/common/js/command.js
diff --git a/resources/static/shared/enable_cookies_url.js b/resources/static/common/js/enable_cookies_url.js
similarity index 100%
rename from resources/static/shared/enable_cookies_url.js
rename to resources/static/common/js/enable_cookies_url.js
diff --git a/resources/static/shared/error-display.js b/resources/static/common/js/error-display.js
similarity index 100%
rename from resources/static/shared/error-display.js
rename to resources/static/common/js/error-display.js
diff --git a/resources/static/shared/error-messages.js b/resources/static/common/js/error-messages.js
similarity index 100%
rename from resources/static/shared/error-messages.js
rename to resources/static/common/js/error-messages.js
diff --git a/resources/static/shared/gettext.js b/resources/static/common/js/gettext.js
similarity index 100%
rename from resources/static/shared/gettext.js
rename to resources/static/common/js/gettext.js
diff --git a/resources/static/shared/helpers.js b/resources/static/common/js/helpers.js
similarity index 100%
rename from resources/static/shared/helpers.js
rename to resources/static/common/js/helpers.js
diff --git a/resources/static/shared/history.js b/resources/static/common/js/history.js
similarity index 100%
rename from resources/static/shared/history.js
rename to resources/static/common/js/history.js
diff --git a/resources/static/shared/javascript-extensions.js b/resources/static/common/js/javascript-extensions.js
similarity index 100%
rename from resources/static/shared/javascript-extensions.js
rename to resources/static/common/js/javascript-extensions.js
diff --git a/resources/static/lib/base64.js b/resources/static/common/js/lib/base64.js
similarity index 100%
rename from resources/static/lib/base64.js
rename to resources/static/common/js/lib/base64.js
diff --git a/resources/static/common/js/lib/bidbundle.js b/resources/static/common/js/lib/bidbundle.js
new file mode 120000
index 0000000000000000000000000000000000000000..00c7194bcdfbd8c359d6471c712c07c53bf442b0
--- /dev/null
+++ b/resources/static/common/js/lib/bidbundle.js
@@ -0,0 +1 @@
+../../../../../node_modules/jwcrypto/bidbundle.js
\ No newline at end of file
diff --git a/resources/static/lib/dom-jquery.js b/resources/static/common/js/lib/dom-jquery.js
similarity index 100%
rename from resources/static/lib/dom-jquery.js
rename to resources/static/common/js/lib/dom-jquery.js
diff --git a/resources/static/lib/ejs.js b/resources/static/common/js/lib/ejs.js
similarity index 100%
rename from resources/static/lib/ejs.js
rename to resources/static/common/js/lib/ejs.js
diff --git a/resources/static/lib/highlight.js b/resources/static/common/js/lib/highlight.js
similarity index 100%
rename from resources/static/lib/highlight.js
rename to resources/static/common/js/lib/highlight.js
diff --git a/resources/static/lib/html5shim.js b/resources/static/common/js/lib/html5shim.js
similarity index 100%
rename from resources/static/lib/html5shim.js
rename to resources/static/common/js/lib/html5shim.js
diff --git a/resources/static/lib/hub.js b/resources/static/common/js/lib/hub.js
similarity index 100%
rename from resources/static/lib/hub.js
rename to resources/static/common/js/lib/hub.js
diff --git a/resources/static/lib/jquery-1.7.1.min.js b/resources/static/common/js/lib/jquery-1.7.1.min.js
similarity index 100%
rename from resources/static/lib/jquery-1.7.1.min.js
rename to resources/static/common/js/lib/jquery-1.7.1.min.js
diff --git a/resources/static/lib/jschannel.js b/resources/static/common/js/lib/jschannel.js
similarity index 100%
rename from resources/static/lib/jschannel.js
rename to resources/static/common/js/lib/jschannel.js
diff --git a/resources/static/lib/json2.js b/resources/static/common/js/lib/json2.js
similarity index 100%
rename from resources/static/lib/json2.js
rename to resources/static/common/js/lib/json2.js
diff --git a/resources/static/lib/micrajax.js b/resources/static/common/js/lib/micrajax.js
similarity index 100%
rename from resources/static/lib/micrajax.js
rename to resources/static/common/js/lib/micrajax.js
diff --git a/resources/static/lib/module.js b/resources/static/common/js/lib/module.js
similarity index 100%
rename from resources/static/lib/module.js
rename to resources/static/common/js/lib/module.js
diff --git a/resources/static/lib/underscore.js b/resources/static/common/js/lib/underscore.js
similarity index 100%
rename from resources/static/lib/underscore.js
rename to resources/static/common/js/lib/underscore.js
diff --git a/resources/static/lib/urlparse.js b/resources/static/common/js/lib/urlparse.js
similarity index 100%
rename from resources/static/lib/urlparse.js
rename to resources/static/common/js/lib/urlparse.js
diff --git a/resources/static/lib/winchan.js b/resources/static/common/js/lib/winchan.js
similarity index 100%
rename from resources/static/lib/winchan.js
rename to resources/static/common/js/lib/winchan.js
diff --git a/resources/static/shared/mediator.js b/resources/static/common/js/mediator.js
similarity index 100%
rename from resources/static/shared/mediator.js
rename to resources/static/common/js/mediator.js
diff --git a/resources/static/shared/models/interaction_data.js b/resources/static/common/js/models/interaction_data.js
similarity index 93%
rename from resources/static/shared/models/interaction_data.js
rename to resources/static/common/js/models/interaction_data.js
index 5b91bb6ee129697b23e2c9741cf2b99882256825..f2ac0418da7d6443ff1e04d43b5d00826b03dd7f 100644
--- a/resources/static/shared/models/interaction_data.js
+++ b/resources/static/common/js/models/interaction_data.js
@@ -10,7 +10,21 @@ BrowserID.Models.InteractionData = (function() {
       storage = bid.getStorage(),
       network = bid.Network,
       complete = bid.Helpers.complete,
-      whitelistFilter = bid.Helpers.whitelistFilter;
+      whitelistFilter = bid.Helpers.whitelistFilter,
+      KPI_WHITELIST = [
+        'event_stream',
+        'lang',
+        'screen_size',
+        'sample_rate',
+        'timestamp',
+        'number_emails',
+        'sites_signed_in',
+        'sites_visited',
+        'orphaned',
+        'new_account',
+        'email_type'
+      ];
+
 
   function getInteractionData() {
     var interactionData;
@@ -97,13 +111,7 @@ BrowserID.Models.InteractionData = (function() {
       // could be used to fingerprint users.
       var filtered = [];
       _.each(data, function(obj) {
-        filtered.push(whitelistFilter(obj, [
-          'event_stream',
-          'lang',
-          'screen_size',
-          'sample_rate',
-          'timestamp'
-        ]));
+        filtered.push(whitelistFilter(obj, KPI_WHITELIST));
       });
 
       network.sendInteractionData(filtered, function() {
diff --git a/resources/static/shared/models/models.js b/resources/static/common/js/models/models.js
similarity index 100%
rename from resources/static/shared/models/models.js
rename to resources/static/common/js/models/models.js
diff --git a/resources/static/shared/modules/cookie_check.js b/resources/static/common/js/modules/cookie_check.js
similarity index 100%
rename from resources/static/shared/modules/cookie_check.js
rename to resources/static/common/js/modules/cookie_check.js
diff --git a/resources/static/shared/modules/development.js b/resources/static/common/js/modules/development.js
similarity index 100%
rename from resources/static/shared/modules/development.js
rename to resources/static/common/js/modules/development.js
diff --git a/resources/static/shared/modules/interaction_data.js b/resources/static/common/js/modules/interaction_data.js
similarity index 85%
rename from resources/static/shared/modules/interaction_data.js
rename to resources/static/common/js/modules/interaction_data.js
index e07e04de42e27fea588e31ad5ddd805040ba694b..8f36514dac7ddc5f6f13dccd4d4e6a593f105ef5 100644
--- a/resources/static/shared/modules/interaction_data.js
+++ b/resources/static/common/js/modules/interaction_data.js
@@ -44,6 +44,29 @@ BrowserID.Modules.InteractionData = (function() {
    *   directly from the mediator.  Function returns a value.  If no value is
    *   returned, field will not be saved to KPI data set.
    */
+
+  /**
+   * Explanation of KPIs:
+   *
+   * screen.* - the user sees a new screen (generally speaking, though there
+   *   may be a couple of exceptions).
+   * window.redirect_to_primary - the user has to authenticate with their
+   *   IdP so they are being redirected away.
+   * window.unload - the last thing in every event stream.
+   * generate_assertion - the order was given to generate an assertion.
+   * assertion_generated - the assertion generation is complete -
+   *   these two together are useful to measure how long crypto is taking
+   *   on various devices.
+   * user.user_staged - a new user verification email is sent
+   * user.user_confirmed - the user has confirmed and the dialog is closing.
+   *   These two together give us the info needed to see how long it takes
+   *   users to confirm their address - iff they keep their dialog open.
+   * user.email_staged/user.email_confirmed is similar to
+   *   user.user_staged/confirmed except it is when the user adds a secondary
+   *   email to their account.
+   * user.logout - that is the user has clicked "this is not me."
+   */
+
   var MediatorToKPINameTable = {
     service: function(msg, data) { return "screen." + data.name; },
     cancel_state: "screen.cancel",
@@ -51,12 +74,11 @@ BrowserID.Modules.InteractionData = (function() {
     window_unload: "window.unload",
     generate_assertion: null,
     assertion_generated: null,
-    emails_displayed: function(msg, data) { return "user.email_count:" + data.count; },
     user_staged: "user.user_staged",
     user_confirmed: "user.user_confirmed",
     email_staged: "user.email_staged",
     email_confirmed: "user.email_confrimed",
-    notme: "user.logout",
+    notme: "user.logout"
   };
 
   function getKPIName(msg, data) {
@@ -102,9 +124,9 @@ BrowserID.Modules.InteractionData = (function() {
 
     // server_time is sent in milliseconds. The promise to users and data
     // safety is the timestamp would be at a 10 minute resolution.  Round to the
-    // nearest 10 minute mark.
+    // previous 10 minute mark.
     var TEN_MINS_IN_MS = 10 * 60 * 1000,
-        roundedServerTime = Math.round(result.server_time / TEN_MINS_IN_MS) * TEN_MINS_IN_MS;
+        roundedServerTime = Math.floor(result.server_time / TEN_MINS_IN_MS) * TEN_MINS_IN_MS;
 
     var currentData = {
       event_stream: self.initialEventStream,
@@ -132,6 +154,12 @@ BrowserID.Modules.InteractionData = (function() {
     self.samplesBeingStored = true;
   }
 
+  function onKPIData(msg, result) {
+    var currentData = this.getCurrent();
+    _.extend(currentData, result);
+    model.setCurrent(currentData);
+  }
+
   // At every load, after session_context returns, try to publish the previous
   // data.  We have to wait until session_context completes so that we have
   // a csrf token to send.
@@ -236,11 +264,12 @@ BrowserID.Modules.InteractionData = (function() {
         // whenever session_context is hit, let's hear about it so we can
         // extract the information that's important to us (like, whether we
         // should be running or not)
-        self.contextInfoHandle = this.subscribe('context_info', onSessionContext);
+        self.subscribe('context_info', onSessionContext);
       }
 
       // on all events, update event_stream
-      this.subscribeAll(addEvent);
+      self.subscribeAll(addEvent);
+      self.subscribe('kpi_data', onKPIData);
     },
 
     addEvent: addEvent,
diff --git a/resources/static/shared/modules/page_module.js b/resources/static/common/js/modules/page_module.js
similarity index 100%
rename from resources/static/shared/modules/page_module.js
rename to resources/static/common/js/modules/page_module.js
diff --git a/resources/static/shared/modules/xhr_delay.js b/resources/static/common/js/modules/xhr_delay.js
similarity index 100%
rename from resources/static/shared/modules/xhr_delay.js
rename to resources/static/common/js/modules/xhr_delay.js
diff --git a/resources/static/shared/modules/xhr_disable_form.js b/resources/static/common/js/modules/xhr_disable_form.js
similarity index 100%
rename from resources/static/shared/modules/xhr_disable_form.js
rename to resources/static/common/js/modules/xhr_disable_form.js
diff --git a/resources/static/shared/network.js b/resources/static/common/js/network.js
similarity index 100%
rename from resources/static/shared/network.js
rename to resources/static/common/js/network.js
diff --git a/resources/static/shared/provisioning.js b/resources/static/common/js/provisioning.js
similarity index 100%
rename from resources/static/shared/provisioning.js
rename to resources/static/common/js/provisioning.js
diff --git a/resources/static/shared/renderer.js b/resources/static/common/js/renderer.js
similarity index 100%
rename from resources/static/shared/renderer.js
rename to resources/static/common/js/renderer.js
diff --git a/resources/static/shared/screens.js b/resources/static/common/js/screens.js
similarity index 100%
rename from resources/static/shared/screens.js
rename to resources/static/common/js/screens.js
diff --git a/resources/static/shared/state_machine.js b/resources/static/common/js/state_machine.js
similarity index 100%
rename from resources/static/shared/state_machine.js
rename to resources/static/common/js/state_machine.js
diff --git a/resources/static/shared/storage.js b/resources/static/common/js/storage.js
similarity index 95%
rename from resources/static/shared/storage.js
rename to resources/static/common/js/storage.js
index b195e760191606371425e2f2f218eb2a6ad0dcc6..e83f61f010f8acbc7a2ff94ae0a6d2ceb283feaa 100644
--- a/resources/static/shared/storage.js
+++ b/resources/static/common/js/storage.js
@@ -84,6 +84,10 @@ BrowserID.Storage = (function() {
     return {};
   }
 
+  function getEmailCount() {
+    return _.size(getEmails());
+  }
+
   function getEmail(email) {
     var ids = getEmails();
 
@@ -213,10 +217,19 @@ BrowserID.Storage = (function() {
 
     if (siteInfo) {
       delete siteInfo[key];
+
+      // If no more info for site, get rid of it.
+      if (!_.size(siteInfo)) delete allSiteInfo[site];
+
       storage.siteInfo = JSON.stringify(allSiteInfo);
     }
   }
 
+  function siteCount(callback) {
+    var allSiteInfo = JSON.parse(storage.siteInfo || "{}");
+    return _.size(allSiteInfo);
+  }
+
   function generic2KeySet(namespace, key, value) {
     var allInfo = JSON.parse(storage[namespace] || "{}");
     allInfo[key] = value;
@@ -246,6 +259,11 @@ BrowserID.Storage = (function() {
     return allInfo[origin];
   }
 
+  function loggedInCount() {
+    var allInfo = JSON.parse(storage.loggedIn || "{}");
+    return _.size(allInfo);
+  }
+
   function watchLoggedIn(origin, callback) {
     var lastState = getLoggedIn(origin);
 
@@ -442,6 +460,14 @@ BrowserID.Storage = (function() {
      * @method getEmails
      */
     getEmails: getEmails,
+
+    /**
+     * Get the number of stored emails
+     * @method getEmailCount
+     * @return {number}
+     */
+    getEmailCount: getEmailCount,
+
     /**
      * Get one email address and its key pair, if found.  Returns undefined if
      * not found.
@@ -484,7 +510,14 @@ BrowserID.Storage = (function() {
        * @param {string} site - site to remove info for
        * @param {string} key - key to remove
        */
-      remove: siteRemove
+      remove: siteRemove,
+
+      /**
+       * Get the number of sites that have info
+       * @method site.count
+       * @return {number}
+       */
+      count: siteCount,
     },
 
     manage_page: {
@@ -565,6 +598,13 @@ BrowserID.Storage = (function() {
      */
     getLoggedIn: getLoggedIn,
 
+    /**
+     * Get the number of sites the user is logged in to.
+     * @method loggedInCount
+     * @return {number}
+     */
+    loggedInCount: loggedInCount,
+
     /** watch for changes in the logged in state of a page
      * @param {string} origin - the site to watch the status of
      * @param {function} callback - a callback to invoke when state changes
diff --git a/resources/static/shared/templates.js b/resources/static/common/js/templates.js
similarity index 100%
rename from resources/static/shared/templates.js
rename to resources/static/common/js/templates.js
diff --git a/resources/static/shared/tooltip.js b/resources/static/common/js/tooltip.js
similarity index 100%
rename from resources/static/shared/tooltip.js
rename to resources/static/common/js/tooltip.js
diff --git a/resources/static/shared/user.js b/resources/static/common/js/user.js
similarity index 97%
rename from resources/static/shared/user.js
rename to resources/static/common/js/user.js
index ab493ae9f4870da8bc26472ff8a08f69b53b4095..f39d3935d9bdae5f9bcdb3cf966f1212769c2318 100644
--- a/resources/static/shared/user.js
+++ b/resources/static/common/js/user.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser:true, forin: true, laxbreak: true */
 /*global _: true, BrowserID: true, console: true */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -318,7 +318,7 @@ BrowserID.User = (function() {
       var email = info.email;
       User.provisionPrimaryUser(email, info, function(status, provInfo) {
         if (status === "primary.verified") {
-          network.authenticateWithAssertion(email, provInfo.assertion, function(status) {
+          User.authenticateWithAssertion(email, provInfo.assertion, function(status) {
             if (status) {
               onComplete("primary.verified");
             }
@@ -659,7 +659,7 @@ BrowserID.User = (function() {
             if (!emails_to_add || !emails_to_add.length) {
               onComplete();
               return;
-            }
+           }
 
             var email = emails_to_add.shift();
 
@@ -744,6 +744,31 @@ BrowserID.User = (function() {
       }, onFailure);
     },
 
+    /**
+     * Authenticate the user with the given email and assertion.  This will sync
+     * the user's addresses.
+     * @method authenticateWithAssertion
+     * @param {string} email
+     * @param {string} assertion
+     * @param {function} [onComplete] - Called on completion with status. true
+     * if user is authenticated, false otw.
+     * @param {function} [onFailure] - Called on error.
+     */
+    authenticateWithAssertion: function(email, assertion, onComplete, onFailure) {
+      network.authenticateWithAssertion(email, assertion, function(authenticated) {
+        setAuthenticationStatus(authenticated);
+
+        if (authenticated) {
+          User.syncEmails(function() {
+            complete(onComplete, authenticated);
+          }, onFailure);
+        } else {
+          complete(onComplete, authenticated);
+        }
+      }, onFailure);
+
+    },
+
     /**
      * Check whether the email is already registered.
      * @method emailRegistered
@@ -1194,6 +1219,7 @@ BrowserID.User = (function() {
 
       return shouldAsk;
     }
+
   };
 
   // Set origin to default to the current domain.  Other contexts that use user.js,
diff --git a/resources/static/shared/validation.js b/resources/static/common/js/validation.js
similarity index 100%
rename from resources/static/shared/validation.js
rename to resources/static/common/js/validation.js
diff --git a/resources/static/shared/wait-messages.js b/resources/static/common/js/wait-messages.js
similarity index 100%
rename from resources/static/shared/wait-messages.js
rename to resources/static/common/js/wait-messages.js
diff --git a/resources/static/shared/xhr.js b/resources/static/common/js/xhr.js
similarity index 100%
rename from resources/static/shared/xhr.js
rename to resources/static/common/js/xhr.js
diff --git a/resources/static/shared/xhr_transport.js b/resources/static/common/js/xhr_transport.js
similarity index 100%
rename from resources/static/shared/xhr_transport.js
rename to resources/static/common/js/xhr_transport.js
diff --git a/resources/static/css/sil.ttf b/resources/static/css/sil.ttf
deleted file mode 100644
index 903e32be5e3dbc418dea268b28bc3432bde14a10..0000000000000000000000000000000000000000
Binary files a/resources/static/css/sil.ttf and /dev/null differ
diff --git a/resources/static/css/ts.ttf b/resources/static/css/ts.ttf
deleted file mode 100644
index c5842d5fefd18738bcc6ec4384975bdae4a80b3b..0000000000000000000000000000000000000000
Binary files a/resources/static/css/ts.ttf and /dev/null differ
diff --git a/resources/static/dialog/css/popup.css b/resources/static/dialog/css/style.css
similarity index 91%
rename from resources/static/dialog/css/popup.css
rename to resources/static/dialog/css/style.css
index 39d97a1f508042e5650c79ca03fa7d77b6f0dff5..5edef21780867c23f7855a6b5981cb36fc3e1cd7 100644
--- a/resources/static/dialog/css/popup.css
+++ b/resources/static/dialog/css/style.css
@@ -5,13 +5,13 @@
 body {
   color: #383838;
   background-color: #dee3e6;
-  background-image: url('/i/grain.png');
-  background-image: url("/i/grain.png"), -webkit-gradient(linear, left top, left bottom, from(rgba(113, 126, 137, 0)), to(rgba(113, 126, 137, 0.2)));
-  background-image: url('/i/grain.png'), -webkit-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
-  background-image: url('/i/grain.png'),    -moz-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
-  background-image: url('/i/grain.png'),     -ms-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
-  background-image: url('/i/grain.png'),      -o-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
-  background-image: url('/i/grain.png'),         linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
+  background-image: url('/common/i/grain.png');
+  background-image: url("/common/i/grain.png"), -webkit-gradient(linear, left top, left bottom, from(rgba(113, 126, 137, 0)), to(rgba(113, 126, 137, 0.2)));
+  background-image: url('/common/i/grain.png'), -webkit-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
+  background-image: url('/common/i/grain.png'),    -moz-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
+  background-image: url('/common/i/grain.png'),     -ms-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
+  background-image: url('/common/i/grain.png'),      -o-linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
+  background-image: url('/common/i/grain.png'),         linear-gradient(top, rgba(113, 126, 137, 0), rgba(113, 126, 137, 0.2));
 }
 
 #signIn {
@@ -68,7 +68,7 @@ header {
 .home {
   width: 161px;
   height: 40px;
-  background: url("/i/persona-logo-transparent.png") 0 0 no-repeat;
+  background: url("/dialog/i/persona-logo-transparent.png") 0 0 no-repeat;
   text-indent: -9999px;
   display: inline-block;
   *display: block;
@@ -259,7 +259,7 @@ section > .contents {
     bottom: 0;
     left: 0;
     right: 0;
-    background-image: url('/i/arrow_grey.png');
+    background-image: url('/dialog/i/arrow_grey.png');
     background-repeat: no-repeat;
     background-position: center;
     background-color: transparent;
diff --git a/resources/static/i/arrow_grey.png b/resources/static/dialog/i/arrow_grey.png
similarity index 100%
rename from resources/static/i/arrow_grey.png
rename to resources/static/dialog/i/arrow_grey.png
diff --git a/resources/static/i/firefox_logo.png b/resources/static/dialog/i/firefox_logo.png
similarity index 100%
rename from resources/static/i/firefox_logo.png
rename to resources/static/dialog/i/firefox_logo.png
diff --git a/resources/static/i/persona-logo-transparent.png b/resources/static/dialog/i/persona-logo-transparent.png
similarity index 100%
rename from resources/static/i/persona-logo-transparent.png
rename to resources/static/dialog/i/persona-logo-transparent.png
diff --git a/resources/static/dialog/resources/helpers.js b/resources/static/dialog/js/misc/helpers.js
similarity index 98%
rename from resources/static/dialog/resources/helpers.js
rename to resources/static/dialog/js/misc/helpers.js
index 64225604a1fd9b4ca32f7177702ade8afbfbae52..af595a5338e5dba843f45572329d1c648496daf7 100644
--- a/resources/static/dialog/resources/helpers.js
+++ b/resources/static/dialog/js/misc/helpers.js
@@ -40,9 +40,11 @@
   }
 
   function getAssertion(email, callback) {
-    var self=this;
-    var wait = bid.Screens.wait;
+    var self=this,
+        wait = bid.Screens.wait;
+
     wait.show("wait", bid.Wait.generateKey);
+
     user.getAssertion(email, user.getOrigin(), function(assert) {
       assert = assert || null;
       wait.hide();
diff --git a/resources/static/dialog/resources/internal_api.js b/resources/static/dialog/js/misc/internal_api.js
similarity index 100%
rename from resources/static/dialog/resources/internal_api.js
rename to resources/static/dialog/js/misc/internal_api.js
diff --git a/resources/static/dialog/resources/screen_size_hacks.js b/resources/static/dialog/js/misc/screen_size_hacks.js
similarity index 100%
rename from resources/static/dialog/resources/screen_size_hacks.js
rename to resources/static/dialog/js/misc/screen_size_hacks.js
diff --git a/resources/static/dialog/resources/state.js b/resources/static/dialog/js/misc/state.js
similarity index 92%
rename from resources/static/dialog/resources/state.js
rename to resources/static/dialog/js/misc/state.js
index c56eee0ddf6d95326c190dde76913f89c403073d..032bd85f683c1f9a9698fcecf87af9a9ad2c5af5 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/js/misc/state.js
@@ -62,12 +62,15 @@ BrowserID.State = (function() {
     });
 
     handleState("window_unload", function() {
-      if (!self.success) {
-        storage.setStagedOnBehalfOf("");
-        // do not call doCancel here, let winchan's cancel
-        // handling do the work. This gives us consistent semantics
-        // across browsers on the RP side of the WinChan.
-      }
+      // Round up final KPI stats as the user is leaving the dialog.  This
+      // ensures the final state is sent to the KPI stats.  Any new logins are
+      // counted, any new sites are counted, any new emails are included, etc.
+      mediator.publish("kpi_data", {
+        number_emails: storage.getEmailCount() || 0,
+        sites_signed_in: storage.loggedInCount() || 0,
+        sites_visited: storage.site.count() || 0,
+        orphaned: !self.success
+     });
     });
 
     handleState("authentication_checked", function(msg, info) {
@@ -97,6 +100,10 @@ BrowserID.State = (function() {
     handleState("new_user", function(msg, info) {
       self.newUserEmail = info.email;
 
+      // Add new_account to the KPIs *before* the staging occurs allows us to
+      // know when we are losing users due to the email verification.
+      mediator.publish("kpi_data", { new_account: true });
+
       // cancel is disabled if the user is doing the initial password set
       // for a requiredEmail.
       info.cancelable = !requiredEmail;
@@ -146,9 +153,15 @@ BrowserID.State = (function() {
         redirectToState("primary_user_ready", info);
       }
       else {
-        // We don't want to put the provisioning step on the stack, instead when
-        // a user cancels this step, they should go back to the step before the
-        // provisioning.
+        user.isEmailRegistered(email, function(known) {
+          if (!known) {
+            mediator.publish("kpi_data", { new_account: true });
+          }
+        });
+
+        // We don't want to put the provisioning step on the stack,
+        // instead when a user cancels this step, they should go
+        // back to the step before the provisioning.
         startAction(false, "doProvisionPrimaryUser", info);
       }
     });
@@ -221,6 +234,8 @@ BrowserID.State = (function() {
       }
 
       if (idInfo) {
+        mediator.publish("kpi_data", { email_type: idInfo.type });
+
         if (idInfo.type === "primary") {
           if (idInfo.cert) {
             // Email is a primary and the cert is available - the user can log
@@ -323,8 +338,8 @@ BrowserID.State = (function() {
     handleState("assertion_generated", function(msg, info) {
       self.success = true;
       if (info.assertion !== null) {
-        // XXX TODO - move the setLoggedIn to the getAssertion perhaps?
         storage.setLoggedIn(user.getOrigin(), self.email);
+
         startAction("doAssertionGenerated", { assertion: info.assertion, email: self.email });
       }
       else {
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/js/modules/actions.js
similarity index 100%
rename from resources/static/dialog/controllers/actions.js
rename to resources/static/dialog/js/modules/actions.js
diff --git a/resources/static/dialog/controllers/add_email.js b/resources/static/dialog/js/modules/add_email.js
similarity index 100%
rename from resources/static/dialog/controllers/add_email.js
rename to resources/static/dialog/js/modules/add_email.js
diff --git a/resources/static/dialog/controllers/authenticate.js b/resources/static/dialog/js/modules/authenticate.js
similarity index 100%
rename from resources/static/dialog/controllers/authenticate.js
rename to resources/static/dialog/js/modules/authenticate.js
diff --git a/resources/static/dialog/controllers/check_registration.js b/resources/static/dialog/js/modules/check_registration.js
similarity index 100%
rename from resources/static/dialog/controllers/check_registration.js
rename to resources/static/dialog/js/modules/check_registration.js
diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/js/modules/dialog.js
similarity index 100%
rename from resources/static/dialog/controllers/dialog.js
rename to resources/static/dialog/js/modules/dialog.js
diff --git a/resources/static/dialog/controllers/generate_assertion.js b/resources/static/dialog/js/modules/generate_assertion.js
similarity index 100%
rename from resources/static/dialog/controllers/generate_assertion.js
rename to resources/static/dialog/js/modules/generate_assertion.js
diff --git a/resources/static/dialog/controllers/is_this_your_computer.js b/resources/static/dialog/js/modules/is_this_your_computer.js
similarity index 100%
rename from resources/static/dialog/controllers/is_this_your_computer.js
rename to resources/static/dialog/js/modules/is_this_your_computer.js
diff --git a/resources/static/dialog/controllers/pick_email.js b/resources/static/dialog/js/modules/pick_email.js
similarity index 98%
rename from resources/static/dialog/controllers/pick_email.js
rename to resources/static/dialog/js/modules/pick_email.js
index 4382645c75ce2777cfb4008e5e50d40cb2e5bebf..b666c95e90d2d95f26bb9ca71f65695f1f581466 100644
--- a/resources/static/dialog/controllers/pick_email.js
+++ b/resources/static/dialog/js/modules/pick_email.js
@@ -91,8 +91,6 @@ BrowserID.Modules.PickEmail = (function() {
 
       var identities = getSortedIdentities();
 
-      self.publish("emails_displayed", { count: identities.length });
-
       self.renderDialog("pick_email", {
         identities: identities,
         siteemail: storage.site.get(origin, "email"),
diff --git a/resources/static/dialog/controllers/primary_user_provisioned.js b/resources/static/dialog/js/modules/primary_user_provisioned.js
similarity index 96%
rename from resources/static/dialog/controllers/primary_user_provisioned.js
rename to resources/static/dialog/js/modules/primary_user_provisioned.js
index e5722a0a58da144f5d30a328c66d314bae9bad9e..892448a5071df9acd2d0e21f5f5cb317d397a43b 100644
--- a/resources/static/dialog/controllers/primary_user_provisioned.js
+++ b/resources/static/dialog/js/modules/primary_user_provisioned.js
@@ -42,7 +42,7 @@ BrowserID.Modules.PrimaryUserProvisioned = (function() {
         }, self.getErrorDialog(errors.addEmailWithAssertion, complete));
       }
       else {
-        network.authenticateWithAssertion(email, assertion, function(status) {
+        user.authenticateWithAssertion(email, assertion, function(status) {
           if(status) {
             setTimeout(function() {
               self.publish("primary_user_ready", options);
diff --git a/resources/static/dialog/controllers/provision_primary_user.js b/resources/static/dialog/js/modules/provision_primary_user.js
similarity index 100%
rename from resources/static/dialog/controllers/provision_primary_user.js
rename to resources/static/dialog/js/modules/provision_primary_user.js
diff --git a/resources/static/dialog/controllers/required_email.js b/resources/static/dialog/js/modules/required_email.js
similarity index 100%
rename from resources/static/dialog/controllers/required_email.js
rename to resources/static/dialog/js/modules/required_email.js
diff --git a/resources/static/dialog/controllers/rp_info.js b/resources/static/dialog/js/modules/rp_info.js
similarity index 100%
rename from resources/static/dialog/controllers/rp_info.js
rename to resources/static/dialog/js/modules/rp_info.js
diff --git a/resources/static/dialog/controllers/set_password.js b/resources/static/dialog/js/modules/set_password.js
similarity index 100%
rename from resources/static/dialog/controllers/set_password.js
rename to resources/static/dialog/js/modules/set_password.js
diff --git a/resources/static/dialog/controllers/verify_primary_user.js b/resources/static/dialog/js/modules/verify_primary_user.js
similarity index 100%
rename from resources/static/dialog/controllers/verify_primary_user.js
rename to resources/static/dialog/js/modules/verify_primary_user.js
diff --git a/resources/static/dialog/start.js b/resources/static/dialog/js/start.js
similarity index 100%
rename from resources/static/dialog/start.js
rename to resources/static/dialog/js/start.js
diff --git a/resources/static/dialog/mozilla.png b/resources/static/dialog/mozilla.png
deleted file mode 100644
index af714602027dc8e7745cf9a4c1218f00546196f4..0000000000000000000000000000000000000000
Binary files a/resources/static/dialog/mozilla.png and /dev/null differ
diff --git a/resources/static/fonts/fonts_common.css b/resources/static/fonts/fonts_common.css
deleted file mode 100644
index a88cb32976d9ea1307b8720b38bfc978dcf9b84a..0000000000000000000000000000000000000000
--- a/resources/static/fonts/fonts_common.css
+++ /dev/null
@@ -1,33 +0,0 @@
-@font-face {
-  font-family: 'Open Sans';
-  font-style: normal;
-  font-weight: 400;
-  src: url('/fonts/OpenSans-Regular.eot');
-  src: local('Open Sans'),
-       local('OpenSans'),
-       url('/fonts/OpenSans-Regular.eot') format('embedded-opentype'),
-       url('/fonts/OpenSans-Regular.woff') format('woff'),
-       url('/fonts/OpenSans-Regular.ttf') format('truetype');
-}
-@font-face {
-  font-family: 'Open Sans';
-  font-style: normal;
-  font-weight: 300;
-  src: url('/fonts/OpenSans-Light.eot');
-  src: local('Open Sans Light'),
-       local('OpenSans-Light'),
-       url('/fonts/OpenSans-Light.eot') format('embedded-opentype'),
-       url('/fonts/OpenSans-Light.woff') format('woff'),
-       url('/fonts/OpenSans-Light.ttf') format('truetype');
-}
-@font-face {
-  font-family: 'Open Sans';
-  font-style: normal;
-  font-weight: 700;
-  src: url('/fonts/OpenSans-Bold.eot');
-  src: local('Open Sans Bold'),
-       local('OpenSans-Bold'),
-       url('/fonts/OpenSans-Bold.eot') format('embedded-opentype'),
-       url('/fonts/OpenSans-Bold.woff') format('woff'),
-       url('/fonts/OpenSans-Bold.ttf') format('truetype');
-}
diff --git a/resources/static/fonts/fonts_mainsite.css b/resources/static/fonts/fonts_mainsite.css
deleted file mode 100644
index a425735dae71bff42278801ad5b11bbe624f4b75..0000000000000000000000000000000000000000
--- a/resources/static/fonts/fonts_mainsite.css
+++ /dev/null
@@ -1,12 +0,0 @@
-@font-face {
-  font-family: 'Open Sans';
-  font-style: italic;
-  font-weight: 400;
-  src: url('/fonts/OpenSans-Italic.eot');
-  src: local('Open Sans Italic'),
-       local('OpenSans-Italic'),
-       url('/fonts/OpenSans-Italic.eot') format('embedded-opentype'),
-       url('/fonts/OpenSans-Italic.woff') format('woff'),
-       url('/fonts/OpenSans-Italic.ttf') format('truetype');
-}
-
diff --git a/resources/static/i/count.png b/resources/static/i/count.png
deleted file mode 100644
index b6bccc3dc40388432b3ede95ce1204d50a5f78a1..0000000000000000000000000000000000000000
Binary files a/resources/static/i/count.png and /dev/null differ
diff --git a/resources/static/i/faux-tabzilla.png b/resources/static/i/faux-tabzilla.png
deleted file mode 100644
index 7619610c383b3e27505efc3f24ab0b5b93d9af1b..0000000000000000000000000000000000000000
Binary files a/resources/static/i/faux-tabzilla.png and /dev/null differ
diff --git a/resources/static/i/hint.png b/resources/static/i/hint.png
deleted file mode 100644
index 3ff8bf5314769e16ad6e44cbfa0f0839c321ab47..0000000000000000000000000000000000000000
Binary files a/resources/static/i/hint.png and /dev/null differ
diff --git a/resources/static/i/tutorial_1.png b/resources/static/i/tutorial_1.png
deleted file mode 100644
index eebf4b458198996f902493f577d120e03be877fe..0000000000000000000000000000000000000000
Binary files a/resources/static/i/tutorial_1.png and /dev/null differ
diff --git a/resources/static/i/tutorial_2.png b/resources/static/i/tutorial_2.png
deleted file mode 100644
index 11a7bf247fd96bb90e157bd2184a1e7e7174131d..0000000000000000000000000000000000000000
Binary files a/resources/static/i/tutorial_2.png and /dev/null differ
diff --git a/resources/static/i/tutorial_3.png b/resources/static/i/tutorial_3.png
deleted file mode 100644
index 3c6c11efd3f56aeed6bc74b303d9ab4715ea6971..0000000000000000000000000000000000000000
Binary files a/resources/static/i/tutorial_3.png and /dev/null differ
diff --git a/resources/static/lib/bidbundle.js b/resources/static/lib/bidbundle.js
deleted file mode 120000
index c2bfd9cad507d6df9fe365ec014dd1b6d0850a1d..0000000000000000000000000000000000000000
--- a/resources/static/lib/bidbundle.js
+++ /dev/null
@@ -1 +0,0 @@
-../../../node_modules/jwcrypto/bidbundle.js
\ No newline at end of file
diff --git a/resources/static/css/ie8_main.css b/resources/static/pages/css/ie8.css
similarity index 85%
rename from resources/static/css/ie8_main.css
rename to resources/static/pages/css/ie8.css
index 7a5122ddb60301ee9ccf3c9dd6e75c795794433c..27e301090480afe7c8886d313422ec8cd6ba378d 100644
--- a/resources/static/css/ie8_main.css
+++ b/resources/static/pages/css/ie8.css
@@ -7,7 +7,7 @@
  */
 
 body {
-  background-image: url("/i/marketplace-header.png");
+  background-image: url("/pages/i/marketplace-header.png");
   background-position: center top;
   background-repeat: repeat-x;
 }
diff --git a/resources/static/css/m.css b/resources/static/pages/css/m.css
similarity index 100%
rename from resources/static/css/m.css
rename to resources/static/pages/css/m.css
diff --git a/resources/static/css/style.css b/resources/static/pages/css/style.css
similarity index 98%
rename from resources/static/css/style.css
rename to resources/static/pages/css/style.css
index 6ac4fb9fd4c9c7f5c8eac819133263d8ed2883eb..96034587943cb6247e9a382cd8149d6b020691e8 100644
--- a/resources/static/css/style.css
+++ b/resources/static/pages/css/style.css
@@ -17,7 +17,7 @@ noscript {
 
 body {
   background-color: #6a7b86;
-  background-image: url("/i/marketplace-header.png"), url("/i/grain.png");
+  background-image: url("/pages/i/marketplace-header.png"), url("/common/i/grain.png");
   background-position: center top, center top;
   background-repeat: repeat-x, repeat;
   color: #fff;
@@ -396,7 +396,7 @@ button.delete:active {
   position: absolute;
   z-index: 1;
   left: 0;
-  background-image: url('/i/badge.png');
+  background-image: url('/pages/i/badge.png');
   background-position: 0px center;
   background-repeat: no-repeat;
 
@@ -525,7 +525,7 @@ header li {
 .home {
   width: 205px;
   height: 50px;
-  background: url("/i/persona-logo-wordmark.png") 0 0 no-repeat;
+  background: url("/pages/i/persona-logo-wordmark.png") 0 0 no-repeat;
   text-indent: -9999px;
   display: inline-block;
 }
diff --git a/resources/static/i/badge.png b/resources/static/pages/i/badge.png
similarity index 100%
rename from resources/static/i/badge.png
rename to resources/static/pages/i/badge.png
diff --git a/resources/static/i/developers-link.png b/resources/static/pages/i/developers-link.png
similarity index 100%
rename from resources/static/i/developers-link.png
rename to resources/static/pages/i/developers-link.png
diff --git a/resources/static/i/flexible-graphic.png b/resources/static/pages/i/flexible-graphic.png
similarity index 100%
rename from resources/static/i/flexible-graphic.png
rename to resources/static/pages/i/flexible-graphic.png
diff --git a/resources/static/i/marketplace-header.png b/resources/static/pages/i/marketplace-header.png
similarity index 100%
rename from resources/static/i/marketplace-header.png
rename to resources/static/pages/i/marketplace-header.png
diff --git a/resources/static/i/one-password-graphic.png b/resources/static/pages/i/one-password-graphic.png
similarity index 100%
rename from resources/static/i/one-password-graphic.png
rename to resources/static/pages/i/one-password-graphic.png
diff --git a/resources/static/i/persona-logo-wordmark.png b/resources/static/pages/i/persona-logo-wordmark.png
similarity index 100%
rename from resources/static/i/persona-logo-wordmark.png
rename to resources/static/pages/i/persona-logo-wordmark.png
diff --git a/resources/static/i/slit.png b/resources/static/pages/i/slit.png
similarity index 100%
rename from resources/static/i/slit.png
rename to resources/static/pages/i/slit.png
diff --git a/resources/static/pages/about.js b/resources/static/pages/js/about.js
similarity index 100%
rename from resources/static/pages/about.js
rename to resources/static/pages/js/about.js
diff --git a/resources/static/pages/forgot.js b/resources/static/pages/js/forgot.js
similarity index 100%
rename from resources/static/pages/forgot.js
rename to resources/static/pages/js/forgot.js
diff --git a/resources/static/pages/index.js b/resources/static/pages/js/index.js
similarity index 100%
rename from resources/static/pages/index.js
rename to resources/static/pages/js/index.js
diff --git a/resources/static/pages/manage_account.js b/resources/static/pages/js/manage_account.js
similarity index 100%
rename from resources/static/pages/manage_account.js
rename to resources/static/pages/js/manage_account.js
diff --git a/resources/static/pages/page_helpers.js b/resources/static/pages/js/page_helpers.js
similarity index 100%
rename from resources/static/pages/page_helpers.js
rename to resources/static/pages/js/page_helpers.js
diff --git a/resources/static/pages/signin.js b/resources/static/pages/js/signin.js
similarity index 100%
rename from resources/static/pages/signin.js
rename to resources/static/pages/js/signin.js
diff --git a/resources/static/pages/signup.js b/resources/static/pages/js/signup.js
similarity index 100%
rename from resources/static/pages/signup.js
rename to resources/static/pages/js/signup.js
diff --git a/resources/static/pages/start.js b/resources/static/pages/js/start.js
similarity index 97%
rename from resources/static/pages/start.js
rename to resources/static/pages/js/start.js
index 62f92b774bed66ebba614798c1d1303912b51670..fdcdcf2f644cdb65cf8b6e335d2192f728bbe3bb 100644
--- a/resources/static/pages/start.js
+++ b/resources/static/pages/js/start.js
@@ -155,6 +155,10 @@ $(function() {
       var module = bid.about.create();
       module.start({});
     }
+    else if (path === "/tos" || path === "/privacy") {
+      // do nothing.  This prevents "unknown path" from being displayed to the
+      // user.
+    }
     else {
       // Instead of throwing a hard error here, adding a message to the console
       // to let developers know something is up.
diff --git a/resources/static/pages/verify_secondary_address.js b/resources/static/pages/js/verify_secondary_address.js
similarity index 100%
rename from resources/static/pages/verify_secondary_address.js
rename to resources/static/pages/js/verify_secondary_address.js
diff --git a/resources/static/test/cases/shared/browser-support.js b/resources/static/test/cases/common/js/browser-support.js
similarity index 100%
rename from resources/static/test/cases/shared/browser-support.js
rename to resources/static/test/cases/common/js/browser-support.js
diff --git a/resources/static/test/cases/shared/class.js b/resources/static/test/cases/common/js/class.js
similarity index 100%
rename from resources/static/test/cases/shared/class.js
rename to resources/static/test/cases/common/js/class.js
diff --git a/resources/static/test/cases/shared/command.js b/resources/static/test/cases/common/js/command.js
similarity index 100%
rename from resources/static/test/cases/shared/command.js
rename to resources/static/test/cases/common/js/command.js
diff --git a/resources/static/test/cases/shared/enable_cookies_url.js b/resources/static/test/cases/common/js/enable_cookies_url.js
similarity index 100%
rename from resources/static/test/cases/shared/enable_cookies_url.js
rename to resources/static/test/cases/common/js/enable_cookies_url.js
diff --git a/resources/static/test/cases/shared/error-display.js b/resources/static/test/cases/common/js/error-display.js
similarity index 100%
rename from resources/static/test/cases/shared/error-display.js
rename to resources/static/test/cases/common/js/error-display.js
diff --git a/resources/static/test/cases/shared/helpers.js b/resources/static/test/cases/common/js/helpers.js
similarity index 100%
rename from resources/static/test/cases/shared/helpers.js
rename to resources/static/test/cases/common/js/helpers.js
diff --git a/resources/static/test/cases/shared/history.js b/resources/static/test/cases/common/js/history.js
similarity index 100%
rename from resources/static/test/cases/shared/history.js
rename to resources/static/test/cases/common/js/history.js
diff --git a/resources/static/test/cases/shared/models/interaction_data.js b/resources/static/test/cases/common/js/models/interaction_data.js
similarity index 89%
rename from resources/static/test/cases/shared/models/interaction_data.js
rename to resources/static/test/cases/common/js/models/interaction_data.js
index 740a3d676656c5ffea15ff5de962652c442a747d..8f229c141f738196db302ba9e43c11edf46f30bf 100644
--- a/resources/static/test/cases/shared/models/interaction_data.js
+++ b/resources/static/test/cases/common/js/models/interaction_data.js
@@ -95,7 +95,12 @@
           timestamp: now,
           local_timestamp: now,
           lang: "bar",
-          secret: "Attack at dawn!!!"
+          number_emails: 1,
+          sites_signed_in: 2,
+          sites_visited: 3,
+          orphaned: false,
+          new_account: true,
+          email_type: "assertion"
         });
         model.stageCurrent();
 
@@ -112,11 +117,16 @@
             event_stream: [],
             sample_rate: 1,
             timestamp: now,
-            lang: "bar"
+            lang: "bar",
+            number_emails: 1,
+            sites_signed_in: 2,
+            sites_visited: 3,
+            orphaned: false,
+            new_account: true,
+            email_type: "assertion"
           });
 
-          equal(typeof mostRecentSessionData.local_timestamp, "undefined", "non-whitelisted valued stripped");
-          equal(typeof mostRecentSessionData.secret, "undefined", "non-whitelisted valued stripped");
+          testHelpers.testUndefined(mostRecentSessionData.local_timestamp, "non-whitelisted valued stripped");
           start();
         });
       });
diff --git a/resources/static/test/cases/shared/modules/cookie_check.js b/resources/static/test/cases/common/js/modules/cookie_check.js
similarity index 100%
rename from resources/static/test/cases/shared/modules/cookie_check.js
rename to resources/static/test/cases/common/js/modules/cookie_check.js
diff --git a/resources/static/test/cases/shared/modules/interaction_data.js b/resources/static/test/cases/common/js/modules/interaction_data.js
similarity index 96%
rename from resources/static/test/cases/shared/modules/interaction_data.js
rename to resources/static/test/cases/common/js/modules/interaction_data.js
index 35940a19c9bd04e043467b3a9ea3d1df6a5c8e2b..434631b9b747fe58b7cd8b979325edef22ee1112 100644
--- a/resources/static/test/cases/shared/modules/interaction_data.js
+++ b/resources/static/test/cases/common/js/modules/interaction_data.js
@@ -246,4 +246,15 @@
     });
   });
 
+  asyncTest("kpi_data message adds fields to current kpi_data", function() {
+    createController();
+    network.withContext(function() {
+      mediator.publish("kpi_data", { number_emails: 1 });
+      testHelpers.testObjectValuesEqual(controller.getCurrent(), {
+        number_emails: 1
+      });
+      start();
+    });
+  });
+
 }());
diff --git a/resources/static/test/cases/shared/modules/page_module.js b/resources/static/test/cases/common/js/modules/page_module.js
similarity index 100%
rename from resources/static/test/cases/shared/modules/page_module.js
rename to resources/static/test/cases/common/js/modules/page_module.js
diff --git a/resources/static/test/cases/shared/modules/xhr_delay.js b/resources/static/test/cases/common/js/modules/xhr_delay.js
similarity index 100%
rename from resources/static/test/cases/shared/modules/xhr_delay.js
rename to resources/static/test/cases/common/js/modules/xhr_delay.js
diff --git a/resources/static/test/cases/shared/modules/xhr_disable_form.js b/resources/static/test/cases/common/js/modules/xhr_disable_form.js
similarity index 100%
rename from resources/static/test/cases/shared/modules/xhr_disable_form.js
rename to resources/static/test/cases/common/js/modules/xhr_disable_form.js
diff --git a/resources/static/test/cases/shared/network.js b/resources/static/test/cases/common/js/network.js
similarity index 100%
rename from resources/static/test/cases/shared/network.js
rename to resources/static/test/cases/common/js/network.js
diff --git a/resources/static/test/cases/shared/renderer.js b/resources/static/test/cases/common/js/renderer.js
similarity index 100%
rename from resources/static/test/cases/shared/renderer.js
rename to resources/static/test/cases/common/js/renderer.js
diff --git a/resources/static/test/cases/shared/screens.js b/resources/static/test/cases/common/js/screens.js
similarity index 100%
rename from resources/static/test/cases/shared/screens.js
rename to resources/static/test/cases/common/js/screens.js
diff --git a/resources/static/test/cases/shared/state_machine.js b/resources/static/test/cases/common/js/state_machine.js
similarity index 100%
rename from resources/static/test/cases/shared/state_machine.js
rename to resources/static/test/cases/common/js/state_machine.js
diff --git a/resources/static/test/cases/shared/storage.js b/resources/static/test/cases/common/js/storage.js
similarity index 86%
rename from resources/static/test/cases/shared/storage.js
rename to resources/static/test/cases/common/js/storage.js
index f7238bdedf1f9379b0a2f100e2e6b665b7ea69fb..2b7fd3d0789b6c3d171d816a3a6500c0efd7c50a 100644
--- a/resources/static/test/cases/shared/storage.js
+++ b/resources/static/test/cases/common/js/storage.js
@@ -4,7 +4,8 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 (function() {
-  var storage = BrowserID.Storage;
+  var storage = BrowserID.Storage,
+      TEST_ORIGIN = "http://test.domain";
 
   module("shared/storage", {
     setup: function() {
@@ -16,11 +17,12 @@
     }
   });
 
-  test("getEmails with no emails", function() {
+  test("getEmails, getEmailCount with no emails", function() {
     var emails = storage.getEmails();
 
     equal("object", typeof emails, "no emails returns empty object");
     equal(_.size(emails), 0, "object should be empty");
+    equal(storage.getEmailCount(), 0, "no emails");
   });
 
   test("addEmail, getEmails, getEmail", function() {
@@ -28,6 +30,7 @@
 
     var emails = storage.getEmails();
     equal(_.size(emails), 1, "object should have one item");
+    equal(storage.getEmailCount(), 1, "a single email has been added");
     ok("testuser@testuser.com" in emails, "added email address is there");
 
     var id = storage.getEmail("testuser@testuser.com");
@@ -97,17 +100,21 @@
     equal(error.toString(), "unknown email address", "Invalidating an unknown email address");
   });
 
-  test("site.set/site.get/site.remove, happy case", function() {
+  test("site.set/site.get/site.remove/site.count, happy case", function() {
     storage.site.set("www.testsite.com", "autoauth", true);
     equal(storage.site.get("www.testsite.com", "autoauth"), true, "set/get works correctly");
+    equal(storage.site.count(), 1, "correct count");
 
     storage.site.remove("www.testsite.com", "autoauth");
     equal(typeof storage.site.get("www.testsite.com", "autoauth"), "undefined", "after remove, get returns undefined");
+
+    equal(storage.site.count(), 0, "last field for site removed, count decremented correctly");
   });
 
   test("clear clears site info", function() {
     storage.site.set("www.testsite.com", "autoauth", true);
     storage.clear();
+    equal(storage.site.count(), 0, "no more sites after clear");
     equal(typeof storage.site.get("www.testsite.com", "autoauth"), "undefined", "after clear, get returns undefined");
   });
 
@@ -167,5 +174,14 @@
     equal(typeof storage.signInEmail.get(), "undefined", "after remove, signInEmail is empty");
   });
 
+  test("setLoggedIn, getLoggedIn, loggedInCount", function() {
+    var email = "testuser@testuser.com";
+    storage.setLoggedIn(TEST_ORIGIN, email);
+    equal(storage.getLoggedIn(TEST_ORIGIN), email, "correct email");
+
+    storage.setLoggedIn("http://another.domain", email);
+    equal(storage.loggedInCount(), 2, "correct logged in count");
+  });
+
 }());
 
diff --git a/resources/static/test/cases/shared/tooltip.js b/resources/static/test/cases/common/js/tooltip.js
similarity index 100%
rename from resources/static/test/cases/shared/tooltip.js
rename to resources/static/test/cases/common/js/tooltip.js
diff --git a/resources/static/test/cases/shared/user.js b/resources/static/test/cases/common/js/user.js
similarity index 97%
rename from resources/static/test/cases/shared/user.js
rename to resources/static/test/cases/common/js/user.js
index a252cd357f1f73bc34d6c3bfed4755514813f2db..26c47010104631cc42cdd9ac2c77caff04b6c9e5 100644
--- a/resources/static/test/cases/shared/user.js
+++ b/resources/static/test/cases/common/js/user.js
@@ -10,6 +10,7 @@ var jwcrypto = require("./lib/jwcrypto");
       lib = bid.User,
       storage = bid.Storage,
       network = bid.Network,
+      mediator = bid.Mediator,
       xhr = bid.Mocks.xhr,
       testHelpers = bid.TestHelpers,
       testOrigin = testHelpers.testOrigin,
@@ -523,6 +524,26 @@ var jwcrypto = require("./lib/jwcrypto");
     failureCheck(lib.authenticate, TEST_EMAIL, "testuser");
   });
 
+  asyncTest("authenticateWithAssertion with valid assertion", function() {
+    lib.authenticateWithAssertion(TEST_EMAIL, "test_assertion", function(authenticated) {
+      equal(true, authenticated, "we are authenticated!");
+      var emails = lib.getStoredEmailKeypairs();
+      equal(_.size(emails) > 0, true, "emails have been synced to server");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("authenticateWithAssertion with invalid assertion", function() {
+    xhr.useResult("invalid");
+    lib.authenticateWithAssertion(TEST_EMAIL, "test_assertion", function onComplete(authenticated) {
+      equal(false, authenticated, "invalid authentication.");
+      start();
+    }, testHelpers.unexpectedXHRFailure);
+  });
+
+  asyncTest("authenticateWithAssertion with XHR failure", function() {
+    failureCheck(lib.authenticateWithAssertion, TEST_EMAIL, "testuser");
+  });
 
   asyncTest("checkAuthentication with valid authentication", function() {
     storage.addSecondaryEmail(TEST_EMAIL);
@@ -864,6 +885,7 @@ var jwcrypto = require("./lib/jwcrypto");
       var identities = lib.getStoredEmailKeypairs();
       ok(TEST_EMAIL in identities, "Our new email is added");
       equal(_.size(identities), 1, "there is one identity");
+
       start();
     }, testHelpers.unexpectedXHRFailure);
   });
@@ -955,7 +977,7 @@ var jwcrypto = require("./lib/jwcrypto");
       testHelpers.unexpectedXHRFailure);
   });
 
-  asyncTest("getAssertion with known primary email, expired cert, user authenticated with IdP - expect null assertion", function() {
+  asyncTest("getAssertion with known primary email, expired cert, user not authenticated with IdP - expect null assertion", function() {
     xhr.useResult("primary");
     provisioning.setStatus(provisioning.NOT_AUTHENTICATED);
     storage.addEmail("unregistered@testuser.com", { type: "primary" });
diff --git a/resources/static/test/cases/shared/validation.js b/resources/static/test/cases/common/js/validation.js
similarity index 100%
rename from resources/static/test/cases/shared/validation.js
rename to resources/static/test/cases/common/js/validation.js
diff --git a/resources/static/test/cases/shared/xhr.js b/resources/static/test/cases/common/js/xhr.js
similarity index 100%
rename from resources/static/test/cases/shared/xhr.js
rename to resources/static/test/cases/common/js/xhr.js
diff --git a/resources/static/test/cases/dialog.js b/resources/static/test/cases/dialog.js
deleted file mode 100644
index 802dad601609ba877b034c2cbc4de3960a77e831..0000000000000000000000000000000000000000
--- a/resources/static/test/cases/dialog.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-module("dialog");
-
-test("dialog testing works", function(){
-	ok(true,"an assert is run");
-});
diff --git a/resources/static/test/cases/resources/helpers.js b/resources/static/test/cases/dialog/js/misc/helpers.js
similarity index 100%
rename from resources/static/test/cases/resources/helpers.js
rename to resources/static/test/cases/dialog/js/misc/helpers.js
diff --git a/resources/static/test/cases/resources/internal_api.js b/resources/static/test/cases/dialog/js/misc/internal_api.js
similarity index 100%
rename from resources/static/test/cases/resources/internal_api.js
rename to resources/static/test/cases/dialog/js/misc/internal_api.js
diff --git a/resources/static/test/cases/resources/state.js b/resources/static/test/cases/dialog/js/misc/state.js
similarity index 96%
rename from resources/static/test/cases/resources/state.js
rename to resources/static/test/cases/dialog/js/misc/state.js
index 1addb9623d02241590dc7906f8c0fdbf3a30f4be..8d6850d399428fb18c5348332367ffc39a9db33d 100644
--- a/resources/static/test/cases/resources/state.js
+++ b/resources/static/test/cases/dialog/js/misc/state.js
@@ -179,9 +179,13 @@
     mediator.publish("primary_user", { email: TEST_EMAIL });
   });
 
-  test("primary_user with unprovisioned primary user - call doProvisionPrimaryUser", function() {
-    mediator.publish("primary_user", { email: TEST_EMAIL });
-    ok(actions.called.doProvisionPrimaryUser, "doPrimaryUserProvisioned called");
+  asyncTest("primary_user with unprovisioned, unregistered primary user - call doProvisionPrimaryUser", function() {
+    mediator.subscribe("kpi_data", function(msg, data) {
+      equal(data.new_account, true, "new_account kpi added for new primary user");
+      ok(actions.called.doProvisionPrimaryUser, "doPrimaryUserProvisioned called");
+      start();
+    });
+    mediator.publish("primary_user", { email: "unregistered@testuser.com" });
   });
 
   test("primary_user_provisioned - call doEmailChosen", function() {
@@ -517,6 +521,17 @@
     });
   });
 
+  asyncTest("window_unload - set the final KPIs", function() {
+    mediator.subscribe("kpi_data", function(msg, data) {
+      testHelpers.testKeysInObject(data, [
+        'number_emails', 'sites_signed_in', 'sites_visited', 'orphaned'
+      ]);
+      start();
+    });
+
+    mediator.publish("window_unload");
+  });
+
 
 
 }());
diff --git a/resources/static/test/cases/controllers/actions.js b/resources/static/test/cases/dialog/js/modules/actions.js
similarity index 100%
rename from resources/static/test/cases/controllers/actions.js
rename to resources/static/test/cases/dialog/js/modules/actions.js
diff --git a/resources/static/test/cases/controllers/add_email.js b/resources/static/test/cases/dialog/js/modules/add_email.js
similarity index 100%
rename from resources/static/test/cases/controllers/add_email.js
rename to resources/static/test/cases/dialog/js/modules/add_email.js
diff --git a/resources/static/test/cases/controllers/authenticate.js b/resources/static/test/cases/dialog/js/modules/authenticate.js
similarity index 100%
rename from resources/static/test/cases/controllers/authenticate.js
rename to resources/static/test/cases/dialog/js/modules/authenticate.js
diff --git a/resources/static/test/cases/controllers/check_registration.js b/resources/static/test/cases/dialog/js/modules/check_registration.js
similarity index 100%
rename from resources/static/test/cases/controllers/check_registration.js
rename to resources/static/test/cases/dialog/js/modules/check_registration.js
diff --git a/resources/static/test/cases/controllers/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js
similarity index 100%
rename from resources/static/test/cases/controllers/dialog.js
rename to resources/static/test/cases/dialog/js/modules/dialog.js
diff --git a/resources/static/test/cases/controllers/forgot_password.js b/resources/static/test/cases/dialog/js/modules/forgot_password.js
similarity index 100%
rename from resources/static/test/cases/controllers/forgot_password.js
rename to resources/static/test/cases/dialog/js/modules/forgot_password.js
diff --git a/resources/static/test/cases/controllers/generate_assertion.js b/resources/static/test/cases/dialog/js/modules/generate_assertion.js
similarity index 100%
rename from resources/static/test/cases/controllers/generate_assertion.js
rename to resources/static/test/cases/dialog/js/modules/generate_assertion.js
diff --git a/resources/static/test/cases/controllers/is_this_your_computer.js b/resources/static/test/cases/dialog/js/modules/is_this_your_computer.js
similarity index 100%
rename from resources/static/test/cases/controllers/is_this_your_computer.js
rename to resources/static/test/cases/dialog/js/modules/is_this_your_computer.js
diff --git a/resources/static/test/cases/controllers/pick_email.js b/resources/static/test/cases/dialog/js/modules/pick_email.js
similarity index 94%
rename from resources/static/test/cases/controllers/pick_email.js
rename to resources/static/test/cases/dialog/js/modules/pick_email.js
index a4419af1df70e98e88503b7fd9d61b0529b4c87b..555d2f4631c63ae0c9949339feab0e389c180e2c 100644
--- a/resources/static/test/cases/controllers/pick_email.js
+++ b/resources/static/test/cases/dialog/js/modules/pick_email.js
@@ -37,16 +37,11 @@
     controller.start({});
   }
 
-  asyncTest("multiple emails - print emails in alphabetical order, emails_displayed triggered", function() {
+  test("multiple emails - print emails in alphabetical order", function() {
     storage.addEmail("third@testuser.com", {});
     storage.addEmail("second@testuser.com", {});
     storage.addEmail("first@testuser.com", {});
 
-    register("emails_displayed", function(msg, data) {
-      equal(data.count, 3, "emails_displayed triggered with correct email count");
-      start();
-    });
-
     createController();
 
     var inputs = $(".inputs input[type=radio]");
diff --git a/resources/static/test/cases/controllers/primary_user_provisioned.js b/resources/static/test/cases/dialog/js/modules/primary_user_provisioned.js
similarity index 100%
rename from resources/static/test/cases/controllers/primary_user_provisioned.js
rename to resources/static/test/cases/dialog/js/modules/primary_user_provisioned.js
diff --git a/resources/static/test/cases/controllers/provision_primary_user.js b/resources/static/test/cases/dialog/js/modules/provision_primary_user.js
similarity index 100%
rename from resources/static/test/cases/controllers/provision_primary_user.js
rename to resources/static/test/cases/dialog/js/modules/provision_primary_user.js
diff --git a/resources/static/test/cases/controllers/required_email.js b/resources/static/test/cases/dialog/js/modules/required_email.js
similarity index 100%
rename from resources/static/test/cases/controllers/required_email.js
rename to resources/static/test/cases/dialog/js/modules/required_email.js
diff --git a/resources/static/test/cases/controllers/rp_info.js b/resources/static/test/cases/dialog/js/modules/rp_info.js
similarity index 100%
rename from resources/static/test/cases/controllers/rp_info.js
rename to resources/static/test/cases/dialog/js/modules/rp_info.js
diff --git a/resources/static/test/cases/controllers/set_password.js b/resources/static/test/cases/dialog/js/modules/set_password.js
similarity index 100%
rename from resources/static/test/cases/controllers/set_password.js
rename to resources/static/test/cases/dialog/js/modules/set_password.js
diff --git a/resources/static/test/cases/controllers/verify_primary_user.js b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
similarity index 100%
rename from resources/static/test/cases/controllers/verify_primary_user.js
rename to resources/static/test/cases/dialog/js/modules/verify_primary_user.js
diff --git a/resources/static/test/cases/pages/about.js b/resources/static/test/cases/pages/js/about.js
similarity index 100%
rename from resources/static/test/cases/pages/about.js
rename to resources/static/test/cases/pages/js/about.js
diff --git a/resources/static/test/cases/pages/browserid.js b/resources/static/test/cases/pages/js/browserid.js
similarity index 100%
rename from resources/static/test/cases/pages/browserid.js
rename to resources/static/test/cases/pages/js/browserid.js
diff --git a/resources/static/test/cases/pages/forgot.js b/resources/static/test/cases/pages/js/forgot.js
similarity index 100%
rename from resources/static/test/cases/pages/forgot.js
rename to resources/static/test/cases/pages/js/forgot.js
diff --git a/resources/static/test/cases/pages/manage_account.js b/resources/static/test/cases/pages/js/manage_account.js
similarity index 100%
rename from resources/static/test/cases/pages/manage_account.js
rename to resources/static/test/cases/pages/js/manage_account.js
diff --git a/resources/static/test/cases/pages/page_helpers.js b/resources/static/test/cases/pages/js/page_helpers.js
similarity index 100%
rename from resources/static/test/cases/pages/page_helpers.js
rename to resources/static/test/cases/pages/js/page_helpers.js
diff --git a/resources/static/test/cases/pages/signin.js b/resources/static/test/cases/pages/js/signin.js
similarity index 100%
rename from resources/static/test/cases/pages/signin.js
rename to resources/static/test/cases/pages/js/signin.js
diff --git a/resources/static/test/cases/pages/signup.js b/resources/static/test/cases/pages/js/signup.js
similarity index 100%
rename from resources/static/test/cases/pages/signup.js
rename to resources/static/test/cases/pages/js/signup.js
diff --git a/resources/static/test/cases/pages/verify_secondary_address.js b/resources/static/test/cases/pages/js/verify_secondary_address.js
similarity index 100%
rename from resources/static/test/cases/pages/verify_secondary_address.js
rename to resources/static/test/cases/pages/js/verify_secondary_address.js
diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js
index ed90066d39a1fdf4438117acbea6bcf7ce7f2823..119bd73f64a8f23eec6b6655e63781e11459b751 100644
--- a/resources/static/test/testHelpers/helpers.js
+++ b/resources/static/test/testHelpers/helpers.js
@@ -204,10 +204,8 @@ BrowserID.TestHelpers = (function() {
     },
 
     testKeysInObject: function(objToTest, expected, msg) {
-      if (!objToTest) {
-        ok(false, "Missing object to test against");
-        return;
-      }
+      if (!objToTest) ok(false, "missing objToTest");
+      if (!expected) ok(false, "missing objToTest");
 
       for(var i=0, key; key=expected[i]; ++i) {
         ok(key in objToTest, msg || ("object contains " + key));
@@ -215,6 +213,9 @@ BrowserID.TestHelpers = (function() {
     },
 
     testObjectValuesEqual: function(objToTest, expected, msg) {
+      if (!objToTest) ok(false, "missing objToTest");
+      if (!expected) ok(false, "missing objToTest");
+
       for(var key in expected) {
         deepEqual(objToTest[key], expected[key], key + " set to: " + expected[key] + (msg ? " - " + msg : ""));
       }
diff --git a/resources/views/about.ejs b/resources/views/about.ejs
index dfdeba036ae1e32c4470c9530e9b96b6a31d2792..b16b28ac52a5a143b96774b0c7622c6b9e29b890 100644
--- a/resources/views/about.ejs
+++ b/resources/views/about.ejs
@@ -13,13 +13,13 @@
                 </div>
 
                 <div class="graphic">
-                    <img src="i/one-password-graphic.png" alt="One password to rule them all.">
+                    <img src="/pages/i/one-password-graphic.png" alt="One password to rule them all.">
                 </div>
             </article>
 
             <article class="blurb flexible">
                 <div class="graphic first">
-                    <img src="i/flexible-graphic.png" alt="Use multiple email addresses">
+                    <img src="/pages/i/flexible-graphic.png" alt="Use multiple email addresses">
                 </div>
 
                 <div class="info">
@@ -42,7 +42,7 @@
             </article>
         </section>
 
-        <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="i/developers-link.png" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a>
+        <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="/pages/i/developers-link.png" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a>
     </div><!-- #dashboard -->
 </div>
 
diff --git a/resources/views/dialog_layout.ejs b/resources/views/dialog_layout.ejs
index 4b12722611e3681ead1acbc0eb7e202eff51bad5..954d9bfa5cff4c3e009f8f032aecad4e300f52d7 100644
--- a/resources/views/dialog_layout.ejs
+++ b/resources/views/dialog_layout.ejs
@@ -10,7 +10,7 @@
   <meta name="format-detection" content="email=no" />
 
   <!--[if lt IE 9]>
-    <script src="/lib/html5shim.js"></script>
+    <script src="/common/js/lib/html5shim.js"></script>
   <![endif]-->
   <%- cachify_css('/production/dialog.css') %>
   <!--[if lt IE 9]>
diff --git a/resources/views/index.ejs b/resources/views/index.ejs
index 8c526f38aa67b44854c3daa342bf594e03081c81..7eb8e2c35b8f6c9e22322a1062e428ce90671ffc 100644
--- a/resources/views/index.ejs
+++ b/resources/views/index.ejs
@@ -5,7 +5,7 @@
   <div id="hAlign" class="display_nonauth">
       <div id="vAlign">
           <div id="signUp">
-              <div id="card"><img src="<%- cachify('/i/slit.png') %>"></div>
+              <div id="card"><img src="<%- cachify('/pages/i/slit.png') %>"></div>
 
               <h1 class="white headline-main">Connect with Mozilla Persona, the safest &amp; easiest way to sign in.</h1>
               <p class="tour white">
diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs
index d01aa64f819fb1fa741e229cd64231fe48afc183..bfaa83682d1ecbd45b97896476d22b672b498aef 100644
--- a/resources/views/layout.ejs
+++ b/resources/views/layout.ejs
@@ -8,7 +8,7 @@
   <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width" />
   <meta name="format-detection" content="email=no" />
   <!--[if lt IE 9]>
-    <script src="/lib/html5shim.js"></script>
+    <script src="/common/js/lib/html5shim.js"></script>
   <![endif]-->
   <%- cachify_css('/production/browserid.css') %>
   <!--[if lt IE 9]>
diff --git a/resources/views/test.ejs b/resources/views/test.ejs
index da1332fe11b80a0fe7d2547c457495081ea4b377..546fb8a9ceb76c3c53f1ca64432426f89bcf9be3 100644
--- a/resources/views/test.ejs
+++ b/resources/views/test.ejs
@@ -9,7 +9,7 @@
 		<link rel="stylesheet" type="text/css" href="qunit/qunit.css" />
 		<title>Persona QUnit Test</title>
   <!--[if lt IE 9]>
-    <script src="/lib/html5shim.js"></script>
+    <script src="/common/js/lib/html5shim.js"></script>
   <![endif]-->
 	</head>
 	<body>
@@ -66,20 +66,21 @@
     </script>
     <script src="qunit/qunit.js"></script>
     <script src="/include.js"></script>
-    <script src="/lib/jquery-1.7.1.min.js"></script>
-    <script src="/lib/underscore.js"></script>
-    <script src="/lib/ejs.js"></script>
+    <script src="/common/js/lib/jquery-1.7.1.min.js"></script>
+    <script src="/common/js/lib/underscore.js"></script>
+    <script src="/common/js/lib/ejs.js"></script>
     <script src="/i18n/en_US/client.json"></script>
-    <script src="/shared/javascript-extensions.js"></script>
-    <script src="/shared/gettext.js"></script>
-    <script src="/lib/bidbundle.js"></script>
+    <script src="/common/js/javascript-extensions.js"></script>
+    <script src="/common/js/gettext.js"></script>
+    <script src="/common/js/lib/bidbundle.js"></script>
     <script src="http://testmob.org/include.js"></script>
-    <script src="/shared/browserid.js"></script>
-    <script src="/lib/dom-jquery.js"></script>
-    <script src="/lib/hub.js"></script>
-    <script src="/lib/module.js"></script>
-    <script src="/lib/jschannel.js"></script>
-    <script src="/lib/urlparse.js"></script>
+    <script src="/common/js/browserid.js"></script>
+    <script src="/common/js/lib/dom-jquery.js"></script>
+    <script src="/common/js/lib/hub.js"></script>
+    <script src="/common/js/lib/module.js"></script>
+    <script src="/common/js/lib/jschannel.js"></script>
+    <script src="/common/js/lib/urlparse.js"></script>
+
     <script src="mocks/mocks.js"></script>
     <script src="mocks/xhr.js"></script>
     <script src="mocks/templates.js"></script>
@@ -88,119 +89,119 @@
     <script src="mocks/winchan.js"></script>
     <script src="mocks/cachify.js"></script>
 
-    <script src="/shared/renderer.js"></script>
-    <script src="/shared/class.js"></script>
-    <script src="/shared/mediator.js"></script>
-    <script src="/shared/tooltip.js"></script>
-    <script src="/shared/validation.js"></script>
-    <script src="/shared/helpers.js"></script>
-    <script src="/shared/screens.js"></script>
-    <script src="/shared/browser-support.js"></script>
-    <script src="/shared/enable_cookies_url.js"></script>
-    <script src="/shared/wait-messages.js"></script>
-    <script src="/shared/error-messages.js"></script>
-    <script src="/shared/error-display.js"></script>
-    <script src="/shared/storage.js"></script>
-    <script src="/shared/xhr.js"></script>
-    <script src="/shared/network.js"></script>
-    <script src="/shared/provisioning.js"></script>
-    <script src="/shared/user.js"></script>
-    <script src="/shared/command.js"></script>
-    <script src="/shared/history.js"></script>
-    <script src="/shared/state_machine.js"></script>
-
-    <script src="/shared/models/models.js"></script>
-    <script src="/shared/models/interaction_data.js"></script>
-
-    <script src="/shared/modules/page_module.js"></script>
-    <script src="/shared/modules/xhr_delay.js"></script>
-    <script src="/shared/modules/xhr_disable_form.js"></script>
-    <script src="/shared/modules/cookie_check.js"></script>
-    <script src="/shared/modules/interaction_data.js"></script>
-
-    <script src="/dialog/resources/internal_api.js"></script>
-    <script src="/dialog/resources/helpers.js"></script>
-    <script src="/dialog/resources/state.js"></script>
-
-    <script src="/dialog/controllers/actions.js"></script>
-    <script src="/dialog/controllers/pick_email.js"></script>
-    <script src="/dialog/controllers/add_email.js"></script>
-    <script src="/dialog/controllers/dialog.js"></script>
-    <script src="/dialog/controllers/check_registration.js"></script>
-    <script src="/dialog/controllers/authenticate.js"></script>
-    <script src="/dialog/controllers/required_email.js"></script>
-    <script src="/dialog/controllers/verify_primary_user.js"></script>
-    <script src="/dialog/controllers/generate_assertion.js"></script>
-    <script src="/dialog/controllers/provision_primary_user.js"></script>
-    <script src="/dialog/controllers/primary_user_provisioned.js"></script>
-    <script src="/dialog/controllers/is_this_your_computer.js"></script>
-    <script src="/dialog/controllers/set_password.js"></script>
-    <script src="/dialog/controllers/rp_info.js"></script>
-
-    <script src="/pages/page_helpers.js"></script>
-    <script src="/pages/verify_secondary_address.js"></script>
-    <script src="/pages/forgot.js"></script>
-    <script src="/pages/manage_account.js"></script>
-    <script src="/pages/signin.js"></script>
-    <script src="/pages/signup.js"></script>
-    <script src="/pages/about.js"></script>
+    <script src="/common/js/renderer.js"></script>
+    <script src="/common/js/class.js"></script>
+    <script src="/common/js/mediator.js"></script>
+    <script src="/common/js/tooltip.js"></script>
+    <script src="/common/js/validation.js"></script>
+    <script src="/common/js/helpers.js"></script>
+    <script src="/common/js/screens.js"></script>
+    <script src="/common/js/browser-support.js"></script>
+    <script src="/common/js/enable_cookies_url.js"></script>
+    <script src="/common/js/wait-messages.js"></script>
+    <script src="/common/js/error-messages.js"></script>
+    <script src="/common/js/error-display.js"></script>
+    <script src="/common/js/storage.js"></script>
+    <script src="/common/js/xhr.js"></script>
+    <script src="/common/js/network.js"></script>
+    <script src="/common/js/provisioning.js"></script>
+    <script src="/common/js/user.js"></script>
+    <script src="/common/js/command.js"></script>
+    <script src="/common/js/history.js"></script>
+    <script src="/common/js/state_machine.js"></script>
+
+    <script src="/common/js/models/models.js"></script>
+    <script src="/common/js/models/interaction_data.js"></script>
+
+    <script src="/common/js/modules/page_module.js"></script>
+    <script src="/common/js/modules/xhr_delay.js"></script>
+    <script src="/common/js/modules/xhr_disable_form.js"></script>
+    <script src="/common/js/modules/cookie_check.js"></script>
+    <script src="/common/js/modules/interaction_data.js"></script>
+
+    <script src="/dialog/js/misc/internal_api.js"></script>
+    <script src="/dialog/js/misc/helpers.js"></script>
+    <script src="/dialog/js/misc/state.js"></script>
+
+    <script src="/dialog/js/modules/actions.js"></script>
+    <script src="/dialog/js/modules/pick_email.js"></script>
+    <script src="/dialog/js/modules/add_email.js"></script>
+    <script src="/dialog/js/modules/dialog.js"></script>
+    <script src="/dialog/js/modules/check_registration.js"></script>
+    <script src="/dialog/js/modules/authenticate.js"></script>
+    <script src="/dialog/js/modules/required_email.js"></script>
+    <script src="/dialog/js/modules/verify_primary_user.js"></script>
+    <script src="/dialog/js/modules/generate_assertion.js"></script>
+    <script src="/dialog/js/modules/provision_primary_user.js"></script>
+    <script src="/dialog/js/modules/primary_user_provisioned.js"></script>
+    <script src="/dialog/js/modules/is_this_your_computer.js"></script>
+    <script src="/dialog/js/modules/set_password.js"></script>
+    <script src="/dialog/js/modules/rp_info.js"></script>
+
+    <script src="/pages/js/page_helpers.js"></script>
+    <script src="/pages/js/verify_secondary_address.js"></script>
+    <script src="/pages/js/forgot.js"></script>
+    <script src="/pages/js/manage_account.js"></script>
+    <script src="/pages/js/signin.js"></script>
+    <script src="/pages/js/signup.js"></script>
+    <script src="/pages/js/about.js"></script>
 
     <script src="testHelpers/helpers.js"></script>
 
     <script src="cases/include.js"></script>
 
-    <script src="cases/shared/helpers.js"></script>
-    <script src="cases/shared/renderer.js"></script>
-    <script src="cases/shared/screens.js"></script>
-    <script src="cases/shared/tooltip.js"></script>
-    <script src="cases/shared/error-display.js"></script>
-    <script src="cases/shared/browser-support.js"></script>
-    <script src="cases/shared/enable_cookies_url.js"></script>
-    <script src="cases/shared/validation.js"></script>
-    <script src="cases/shared/storage.js"></script>
-    <script src="cases/shared/xhr.js"></script>
-    <script src="cases/shared/network.js"></script>
-    <script src="cases/shared/user.js"></script>
-    <script src="cases/shared/command.js"></script>
-    <script src="cases/shared/history.js"></script>
-    <script src="cases/shared/state_machine.js"></script>
-
-    <script src="cases/shared/models/interaction_data.js"></script>
-
-    <script src="cases/shared/modules/page_module.js"></script>
-    <script src="cases/shared/modules/xhr_delay.js"></script>
-    <script src="cases/shared/modules/xhr_disable_form.js"></script>
-    <script src="cases/shared/modules/cookie_check.js"></script>
-    <script src="cases/shared/modules/interaction_data.js"></script>
-
-    <script src="cases/pages/browserid.js"></script>
-    <script src="cases/pages/page_helpers.js"></script>
-    <script src="cases/pages/verify_secondary_address.js"></script>
-    <script src="cases/pages/forgot.js"></script>
-    <script src="cases/pages/signin.js"></script>
-    <script src="cases/pages/signup.js"></script>
-    <script src="cases/pages/manage_account.js"></script>
-    <script src="cases/pages/about.js"></script>
-
-    <script src="cases/resources/internal_api.js"></script>
-    <script src="cases/resources/helpers.js"></script>
-    <script src="cases/resources/state.js"></script>
-
-    <script src="cases/controllers/actions.js"></script>
-    <script src="cases/controllers/pick_email.js"></script>
-    <script src="cases/controllers/add_email.js"></script>
-    <script src="cases/controllers/check_registration.js"></script>
-    <script src="cases/controllers/authenticate.js"></script>
-    <script src="cases/controllers/required_email.js"></script>
-    <script src="cases/controllers/verify_primary_user.js"></script>
-    <script src="cases/controllers/generate_assertion.js"></script>
-    <script src="cases/controllers/provision_primary_user.js"></script>
-    <script src="cases/controllers/primary_user_provisioned.js"></script>
-    <script src="cases/controllers/is_this_your_computer.js"></script>
-    <script src="cases/controllers/set_password.js"></script>
-    <script src="cases/controllers/rp_info.js"></script>
+    <script src="cases/common/js/helpers.js"></script>
+    <script src="cases/common/js/renderer.js"></script>
+    <script src="cases/common/js/screens.js"></script>
+    <script src="cases/common/js/tooltip.js"></script>
+    <script src="cases/common/js/error-display.js"></script>
+    <script src="cases/common/js/browser-support.js"></script>
+    <script src="cases/common/js/enable_cookies_url.js"></script>
+    <script src="cases/common/js/validation.js"></script>
+    <script src="cases/common/js/storage.js"></script>
+    <script src="cases/common/js/xhr.js"></script>
+    <script src="cases/common/js/network.js"></script>
+    <script src="cases/common/js/user.js"></script>
+    <script src="cases/common/js/command.js"></script>
+    <script src="cases/common/js/history.js"></script>
+    <script src="cases/common/js/state_machine.js"></script>
+
+    <script src="cases/common/js/models/interaction_data.js"></script>
+
+    <script src="cases/common/js/modules/page_module.js"></script>
+    <script src="cases/common/js/modules/xhr_delay.js"></script>
+    <script src="cases/common/js/modules/xhr_disable_form.js"></script>
+    <script src="cases/common/js/modules/cookie_check.js"></script>
+    <script src="cases/common/js/modules/interaction_data.js"></script>
+
+    <script src="cases/pages/js/browserid.js"></script>
+    <script src="cases/pages/js/page_helpers.js"></script>
+    <script src="cases/pages/js/verify_secondary_address.js"></script>
+    <script src="cases/pages/js/forgot.js"></script>
+    <script src="cases/pages/js/signin.js"></script>
+    <script src="cases/pages/js/signup.js"></script>
+    <script src="cases/pages/js/manage_account.js"></script>
+    <script src="cases/pages/js/about.js"></script>
+
+    <script src="cases/dialog/js/misc/internal_api.js"></script>
+    <script src="cases/dialog/js/misc/helpers.js"></script>
+    <script src="cases/dialog/js/misc/state.js"></script>
+
+    <script src="cases/dialog/js/modules/actions.js"></script>
+    <script src="cases/dialog/js/modules/pick_email.js"></script>
+    <script src="cases/dialog/js/modules/add_email.js"></script>
+    <script src="cases/dialog/js/modules/check_registration.js"></script>
+    <script src="cases/dialog/js/modules/authenticate.js"></script>
+    <script src="cases/dialog/js/modules/required_email.js"></script>
+    <script src="cases/dialog/js/modules/verify_primary_user.js"></script>
+    <script src="cases/dialog/js/modules/generate_assertion.js"></script>
+    <script src="cases/dialog/js/modules/provision_primary_user.js"></script>
+    <script src="cases/dialog/js/modules/primary_user_provisioned.js"></script>
+    <script src="cases/dialog/js/modules/is_this_your_computer.js"></script>
+    <script src="cases/dialog/js/modules/set_password.js"></script>
+    <script src="cases/dialog/js/modules/rp_info.js"></script>
 
     <!-- must go last or all other tests will fail. -->
-    <script src="cases/controllers/dialog.js"></script>
+    <script src="cases/dialog/js/modules/dialog.js"></script>
 	</body>
 </html>