diff --git a/.gitignore b/.gitignore
index 5c48f0e0b97b6d82bf93880858cd51d2db92a22b..8181691a4075c67b187fd3f4c874886167e3b7ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@
 /npm-debug.log
 /resources/static/build
 /resources/static/production
-.DS_Store
\ No newline at end of file
+.DS_Store
+/locale
diff --git a/bin/browserid b/bin/browserid
index ac1e5f25cb2afbb524e07f6d39a48f3459ce30e2..f4bdbb7d39caf4c77da4c230e06199686c786c79 100755
--- a/bin/browserid
+++ b/bin/browserid
@@ -67,7 +67,7 @@ app.use(i18n.abide({
 
 var statsd_config = config.get('statsd');
 if (statsd_config && statsd_config.enabled) {
-  logger_statsd = require("connect-logger-statsd");
+  var logger_statsd = require("connect-logger-statsd");
   app.use(logger_statsd({
     host: statsd_config.hostname || "localhost",
     port: statsd_config.port || 8125,
@@ -132,7 +132,7 @@ db.open(config.get('database'), function (error) {
   // shut down express gracefully on SIGINT
   shutdown.handleTerminationSignals(app, function(readyForShutdownCB) {
     require('../lib/bcrypt.js').shutdown();
-    db.close(readyForShutdownCB)
+    db.close(readyForShutdownCB);
   });
 
   var bindTo = config.get('bind_to');
diff --git a/config/aws.json b/config/aws.json
index 95eb55fdfae619519adb14a3a8040cd717dcc507..275f067d040c676972be521b66a561e482fe99c5 100644
--- a/config/aws.json
+++ b/config/aws.json
@@ -12,6 +12,7 @@
   "supported_languages": [
     "en-US", "it-CH", "db-LB"
   ],
+  "debug_lang": "it-CH",
   "locale_directory": "/home/app/code/locale",
   "var_path": "/home/app/var",
 
diff --git a/example/primary/provision.html b/example/primary/provision.html
index c8b7cd583c4c38008d9df3181fa2ee3539bbce92..01394cb48057b0a3ac2cdb3863b538686876e991 100644
--- a/example/primary/provision.html
+++ b/example/primary/provision.html
@@ -9,9 +9,6 @@
 <script type="text/javascript" src="/jquery.js"></script>
 <script type="text/javascript">
 
-  // an alias
-  var fail = navigator.id.raiseProvisioningFailure;
-
   // begin provisioning!  This both gives us indicated to browserid that we're
   // a well formed provisioning page and gives us the parameters of the provisioning
   navigator.id.beginProvisioning(function(email, cert_duration) {
@@ -22,7 +19,7 @@
     $.get('/api/whoami')
       .success(function(who) {
         if (user != who) {
-          return fail('user is not authenticated as target user');
+          return navigator.id.raiseProvisioningFailure('user is not authenticated as target user');
         }
 
         // Awesome!  The user is authenticated as who we want to provision.  let's
@@ -44,13 +41,13 @@
               navigator.id.registerCertificate(r.cert);
             },
             error: function(r) {
-              fail("couldn't certify key");
+              navigator.id.raiseProvisioningFailure("couldn't certify key");
             }
           });
         });
       })
       .error(function() {
-        fail('user is not authenticated');
+        navigator.id.raiseProvisioningFailure('user is not authenticated');
       });
   });
 </script>
diff --git a/example/primary/sign_in.html b/example/primary/sign_in.html
index 51a0eaccae58c47b4ffa31f88220cb0a9b2aec60..838080d2fb96fb83dfe87695538437e7269e1d4d 100644
--- a/example/primary/sign_in.html
+++ b/example/primary/sign_in.html
@@ -33,37 +33,30 @@ button { line-height: 20px; }
 </div>
 
 <script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="https://login.persona.org/authentication_api.js"></script>
 <script type="text/javascript">
 
-function getParameterByName(name)
-{
-  name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
-  var regexS = "[\\?&]" + name + "=([^&#]*)";
-  var regex = new RegExp(regexS);
-  var results = regex.exec(window.location.href);
-  if(results == null)
-    return "";
-  else
-    return decodeURIComponent(results[1].replace(/\+/g, " "));
-}
+var who = null;
 
 $(document).ready(function() {
   try {
-    var who = getParameterByName("email").replace(/@.*$/, "");
-    $('#who').text(who);
+    navigator.id.beginAuthentication(function(email) {
+      who = /^([^@]+)@/.exec(email)[1];
+      $('#who').text(who);
+    });
   } catch(e) {
     alert("uh oh: " + e);
   }
   $("button").click(function(e) {
     $.get('/api/login', { user: who })
       .success(function(r) {
-        window.location = getParameterByName('return_to');
+        navigator.id.completeAuthentication();
       });
   });
 
   $("#cancel").click(function(e) {
     e.preventDefault();
-    window.location = getParameterByName('return_to');
+    navigator.id.raiseAuthenticationFailure('cancel');
   });
 });
 </script>
diff --git a/example/rp/index.html b/example/rp/index.html
index 19ddc0533b343d33ef00de82318cc0f11ddf8655..50f5808a8da6400b14728ef5d541909b156e29a3 100644
--- a/example/rp/index.html
+++ b/example/rp/index.html
@@ -77,9 +77,6 @@ pre {
       <input type="checkbox" id="returnTo">
       <label for="returnTo">Supply returnTo</label><br />
     </li>
-    </li><li>
-      <input type="text" id="requiredEmail" width="80">
-      <label for="requiredEmail">Require a specific email</label><br />
     </li>
   </ul>
     <button class="assertion">Get an assertion</button>
@@ -181,15 +178,6 @@ navigator.id.watch({
 
 $(document).ready(function() {
   $(".specify button.assertion").click(function() {
-    var requiredEmail = $.trim($('#requiredEmail').val());
-    if (!requiredEmail.length) requiredEmail = undefined;
-    if (requiredEmail && requiredEmail.indexOf('@') === -1) {
-      alert('Invalid Email in "Require a specific email" field. Adding @example.com');
-      $('#requiredEmail').val(requiredEmail + '@example.com');
-      $('#requiredEmail').focus();
-      return;
-    }
-
     $(".specify button.assertion").attr('disabled', 'true');
 
     navigator.id.request({
@@ -198,7 +186,6 @@ $(document).ready(function() {
       siteName: $('#siteName').attr('checked') ? "Persona Test Relying Party" : undefined,
       siteLogo: $('#siteLogo').attr('checked') ? "/i/logo.png" : undefined,
       returnTo: $('#returnTo').attr('checked') ? "/postVerificationReturn.html" : undefined,
-      requiredEmail: requiredEmail,
       oncancel: function() {
         loggit("oncancel");
         $(".specify button.assertion").removeAttr('disabled');
diff --git a/lib/static/fake_verification.js b/lib/browserid/fake_verification.js
similarity index 100%
rename from lib/static/fake_verification.js
rename to lib/browserid/fake_verification.js
diff --git a/lib/configuration.js b/lib/configuration.js
index 8283f20f4da1f049a3a4a18e6c534074e43e2ac0..ef4d97f42e2609563a792783337bbc04f2697663 100644
--- a/lib/configuration.js
+++ b/lib/configuration.js
@@ -108,10 +108,6 @@ var conf = module.exports = convict({
       format: 'string?',
       env: 'DATABASE_NAME'
     },
-    password: {
-      format: 'string?',
-      env: 'MYSQL_PASSWORD'
-    },
     max_query_time_ms: {
       format: 'integer = 5000',
       doc: "The maximum amount of time we'll allow a query to run before considering the database to be sick",
diff --git a/lib/email.js b/lib/email.js
index e4bfd894a4d8b6dca2439066655e015725fa021b..f87e03e404bee549e7431cc0372d3b43af8f4cd3 100644
--- a/lib/email.js
+++ b/lib/email.js
@@ -8,7 +8,8 @@ fs = require('fs'),
 path = require('path'),
 ejs = require('ejs'),
 config = require('./configuration.js'),
-logger = require('./logging.js').logger;
+logger = require('./logging.js').logger,
+url = require('url');
 
 /* if smtp parameters are configured, use them */
 try { var smtp_params = config.get('smtp'); } catch(e) {};
@@ -27,8 +28,35 @@ if (smtp_params && smtp_params.host) {
   }
 }
 
-const template = ejs.compile(
-    fs.readFileSync(path.join(__dirname, "static", "prove_template.ejs")).toString());
+const TEMPLATE_PATH = path.join(__dirname, "static", "email_templates");
+
+// the underbar decorator to allow getext to extract strings 
+function _(str) { return str; }
+
+// a map of all the different emails we send
+const templates = {
+  "new": {
+    landing: 'verify_email_address',
+    subject: _("Confirm email address for Persona"), 
+    template: fs.readFileSync(path.join(TEMPLATE_PATH, 'new.ejs')),
+  },
+  "reset": {
+    landing: 'verify_email_address',
+    subject: _("Reset Persona password"), 
+    template: fs.readFileSync(path.join(TEMPLATE_PATH, 'reset.ejs')),
+  },
+  "add": {
+    landing: 'add_email_address',
+    subject: _("Confirm email address for Persona"), 
+    template: fs.readFileSync(path.join(TEMPLATE_PATH, 'add.ejs')),
+  }
+};
+
+// now turn file contents into compiled templates
+Object.keys(templates).forEach(function(type) {
+  templates[type].template = ejs.compile(templates[type].template.toString());
+});
+    
 
 var interceptor = undefined;
 
@@ -48,24 +76,36 @@ exports.setInterceptor = function(callback) {
 };
 
 //TODO send in localeContext
-function doSend(landing_page, email, site, secret, langContext) {
-  var url = config.get('public_url') + "/" + landing_page + "?token=" + encodeURIComponent(secret),
-      _ = langContext.gettext,
+function doSend(email_type, email, site, secret, langContext) {
+  if (!templates[email_type]) throw "unknown email type: " + email_type;
+
+  // remove scheme from site to make it more human
+  site = url.parse(site).hostname;
+
+  var email_params = templates[email_type];
+
+  var public_url = config.get('public_url') + "/" + email_params.landing + "?token=" + encodeURIComponent(secret),
+      GETTEXT = langContext.gettext,
       format = langContext.format;
 
   if (interceptor) {
     interceptor(email, site, secret);
   } else if (config.get('email_to_console')) {
     // log verification email to console separated by whitespace.
-    console.log("\nVERIFICATION URL:\n" + url + "\n");
+    console.log("\nVERIFICATION URL:\n" + public_url + "\n");
   } else {
-    // TODO(aok) extract_po.sh isn't seeing this string if it's below in object literal
-    var subject = format(_("Complete Login to %s using Persona"), [site]);
     emailer.send_mail({
-      sender: "Persona@persona.org",
+      // XXX: Ideally this would be a live email address and a response to these email
+      // addresses would go into a ticketing system (lloyd/skinny)
+      sender: "Persona <no-reply@persona.org>",
       to: email,
-      subject: subject,
-      body: template({ link: url, site: site, gettext: _, format: format })
+      subject: GETTEXT(email_params.subject),
+      body: email_params.template({
+        link: public_url,
+        site: site,
+        gettext: GETTEXT,
+        format: format
+      })
     }, function(err, success){
       if (!success) {
         logger.error("error sending email to: " + email + " - " + err);
@@ -75,9 +115,13 @@ function doSend(landing_page, email, site, secret, langContext) {
 };
 
 exports.sendNewUserEmail = function(email, site, secret, langContext) {
-  doSend('verify_email_address', email, site, secret, langContext);
+  doSend('new', email, site, secret, langContext);
 };
 
 exports.sendAddAddressEmail = function(email, site, secret, langContext) {
-  doSend('add_email_address', email, site, secret, langContext);
+  doSend('add', email, site, secret, langContext);
+};
+
+exports.sendForgotPasswordEmail = function(email, site, secret, langContext) {
+  doSend('reset', email, site, secret, langContext);
 };
diff --git a/lib/static/email_templates/add.ejs b/lib/static/email_templates/add.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..fb928499b4f11710331a8ae83d7d8b4de035e59a
--- /dev/null
+++ b/lib/static/email_templates/add.ejs
@@ -0,0 +1,9 @@
+<%= gettext('Thank you for using Persona.') %>
+
+<%= format(gettext('Click to confirm this email address and automatically sign in to %s'), [site]) %>
+<%= link %>
+
+<%= format(gettext('Note: If you are NOT trying to sign into this website, please ignore this email.'), [site]) %>
+
+<%= gettext('Thank you,') %>
+<%= gettext('The Persona team') %>
diff --git a/lib/static/email_templates/new.ejs b/lib/static/email_templates/new.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..fb928499b4f11710331a8ae83d7d8b4de035e59a
--- /dev/null
+++ b/lib/static/email_templates/new.ejs
@@ -0,0 +1,9 @@
+<%= gettext('Thank you for using Persona.') %>
+
+<%= format(gettext('Click to confirm this email address and automatically sign in to %s'), [site]) %>
+<%= link %>
+
+<%= format(gettext('Note: If you are NOT trying to sign into this website, please ignore this email.'), [site]) %>
+
+<%= gettext('Thank you,') %>
+<%= gettext('The Persona team') %>
diff --git a/lib/static/email_templates/reset.ejs b/lib/static/email_templates/reset.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..0b1845507c71c0f4b8b980e680e09f4427cbe53e
--- /dev/null
+++ b/lib/static/email_templates/reset.ejs
@@ -0,0 +1,9 @@
+<%= gettext('Forgot your password for Persona?  It happens to the best of us.') %>
+
+<%= gettext('Click to reset your password:') %>
+<%= link %>
+
+<%= gettext('Note: If you did NOT ask to reset your password, please ignore this email.') %>
+
+<%= gettext('Thank you,') %>
+<%= gettext('The Persona team') %>
diff --git a/lib/static/prove_template.ejs b/lib/static/prove_template.ejs
deleted file mode 100644
index d20e219da9555171ef51302105a93acec5549179..0000000000000000000000000000000000000000
--- a/lib/static/prove_template.ejs
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= format(
-      gettext('Thanks for verifying your email address. This message is being sent to you to complete your sign-in to %s.'), [site]) %>
-
-<%= gettext('Finish registration by clicking this link:') %> <%= link %>
-
-<%= gettext('If you are NOT trying to sign into this site, just ignore this email.') %>
-
-<%= gettext('Thanks,') %>
-<%= gettext('Persona') %>
-<%= gettext('(A better way to sign in)') %>
diff --git a/lib/static_resources.js b/lib/static_resources.js
index b994c3842d6b8abe2f939c53caf349150c4e521c..7b14d260da15c063e0260419c81c2420b4c03eb8 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,79 @@ 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/html5shim.js': [
+    '/common/js/lib/html5shim.js'
+  ],
   '/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/package.json b/package.json
index c1bf949a8a05aba32cba092a7433f35c5c6f2a2b..24a3bdf1e613fdced528e5c488f7be92f699ae34 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
         "express": "2.5.0",
         "iconv": "1.1.3",
         "mustache": "0.3.1-dev",
-        "jwcrypto": "0.2.2",
+        "jwcrypto": "0.3.2",
         "mysql": "0.9.5",
         "node-gettext": "0.1.1",
         "node-statsd": "https://github.com/downloads/lloyd/node-statsd/0509f85.tgz",
diff --git a/resources/static/authentication_api.js b/resources/static/authentication_api.js
index 5bc44cd704cabf66c811e6933da34cc720c4bc3a..ca9b16c49990977dac2d828dacfdf2a9ea669571 100644
--- a/resources/static/authentication_api.js
+++ b/resources/static/authentication_api.js
@@ -23,7 +23,6 @@
       return decodeURIComponent(results[1].replace(/\+/g, " "));
   }
 
-
   if (!navigator.id.beginAuthentication || navigator.id._primaryAPIIsShimmed) {
     navigator.id.beginAuthentication = function(cb) {
       if (typeof cb !== 'function') {
@@ -34,11 +33,11 @@
     };
 
     navigator.id.completeAuthentication = function(cb) {
-      window.location = getParameterByName('return_to');
+      window.location = 'https://login.persona.org/sign_in#AUTH_RETURN';
     };
 
     navigator.id.raiseAuthenticationFailure = function(reason) {
-      window.location = getParameterByName('return_to');
+      window.location = 'https://login.persona.org/sign_in#AUTH_RETURN_CANCEL';
     };
 
     navigator.id._primaryAPIIsShimmed = true;
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 58%
rename from resources/static/css/common.css
rename to resources/static/common/css/style.css
index 2cb6dbf41f259670449f1332b68b9ec3a750b6a0..06c994071a64761355f61a4ea894beba38d277af 100644
--- a/resources/static/css/common.css
+++ b/resources/static/common/css/style.css
@@ -56,8 +56,13 @@ ul, li {
   list-style-type: none;
 }
 
-.mono {
-  font-family: 'Monaco', monospace;
+strong {
+  font-weight: 700;
+}
+
+.hidden {
+  visibility: hidden;
+  height: 0;
 }
 
 .tooltip {
@@ -134,6 +139,18 @@ input[type=password]:disabled {
     border-color: #b2b2b2;
 }
 
+label + input[type=text],
+label + input[type=password],
+label + input[type=email] {
+  margin-top: 8px;
+}
+
+label.hidden + input[type=text],
+label.hidden + input[type=password],
+label.hidden + input[type=email] {
+  margin-top: 0;
+}
+
 input[type=radio],
 input[type=checkbox] {
   cursor: pointer;
@@ -143,6 +160,7 @@ input[type=checkbox] {
 button,
 .button {
     font-size: 14px;
+    font-weight: bold;
     line-height: 14px;
     /* The difference between top and bottom padding is to make up for the tiny
      * offset that browsers use to display lowercase letters.
@@ -151,12 +169,12 @@ button,
     float: right;
     border: 0;
     color: #fff;
-    text-shadow: 0 1px 0 #888, 0 0 2px rgba(255,255,255,.2);
+    text-shadow: 0 1px rgba(0,0,0,0.5);
     cursor: pointer;
     white-space: nowrap;
 
     border-radius: 3px;
-    border-bottom: 1px solid #888;
+    box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2);
 
     background-color: #4eb5e5;
     background-image: -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf));
@@ -171,13 +189,15 @@ button:hover,
 button:focus,
 .button:hover,
 .button:focus {
-    background-color: #43a6e2;
-    background-image: -webkit-gradient(linear, left top, left bottom, from(#43a6e2), to(#277ac1));
-    background-image: -webkit-linear-gradient(top, #43a6e2, #277ac1);
-    background-image:    -moz-linear-gradient(top, #43a6e2, #277ac1);
-    background-image:      -ms-linear-gradient(top, #43a6e2, #277ac1);
-    background-image:       -o-linear-gradient(top, #43a6e2, #277ac1);
-    background-image:          linear-gradient(top, #43a6e2, #277ac1);
+    box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2), 0 2px 0 rgba(0, 0, 0, 0.1);
+
+    background-color: #4aafe5;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8));
+    background-image: -webkit-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image:    -moz-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image:      -ms-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image:       -o-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image:          linear-gradient(top, #4aafe5, #2c89c8);
 }
 
 button:focus,
@@ -196,8 +216,8 @@ button:active,
     background-image:       -o-linear-gradient(top, #184a73, #276084);
     background-image:          linear-gradient(top, #184a73, #276084);
     color: #97b6ca;
-    text-shadow: 0 1px #143348;
-    box-shadow: 0 0 5px #003763 inset;
+    text-shadow: 0 1px rgba(0,0,0,0.4);
+    box-shadow: inset 0 2px 1px rgba(0,0,0,0.3);
 }
 
 button::-moz-focus-inner, .button::-moz-focus-inner {
@@ -209,30 +229,40 @@ 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;
 }
 
-
 .submit button:hover,
 .submit button:focus,
 .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-color: #4aafe5;
+    background-image: url("/common/i/button-arrow-hover.png");
+    background-image: url("/common/i/button-arrow-hover.png"), -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8));
+    background-image: url("/common/i/button-arrow-hover.png"), -webkit-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image: url("/common/i/button-arrow-hover.png"),    -moz-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image: url("/common/i/button-arrow-hover.png"),     -ms-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image: url("/common/i/button-arrow-hover.png"),      -o-linear-gradient(top, #4aafe5, #2c89c8);
+    background-image: url("/common/i/button-arrow-hover.png"),         linear-gradient(top, #4aafe5, #2c89c8);
+}
+
+.submit button:active,
+.submit .button:active {
+    background-color: #184a73;
+    background-image: url("/common/i/button-arrow-active.png"), -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084));
+    background-image: url("/common/i/button-arrow-active.png"), -webkit-linear-gradient(top, #184a73, #276084);
+    background-image: url("/common/i/button-arrow-active.png"),    -moz-linear-gradient(top, #184a73, #276084);
+    background-image: url("/common/i/button-arrow-active.png"),      -ms-linear-gradient(top, #184a73, #276084);
+    background-image: url("/common/i/button-arrow-active.png"),       -o-linear-gradient(top, #184a73, #276084);
+    background-image: url("/common/i/button-arrow-active.png"),          linear-gradient(top, #184a73, #276084);
 }
 
 button[disabled], .submit_disabled button, .submit_disabled .button,
@@ -268,26 +298,57 @@ button.negative:hover,
 button.negative:focus,
 .button.negative:hover,
 .button.negative:focus {
-    background-color: #ad1804;
-    background-image: -webkit-gradient(linear, left top, left bottom, from(#ad1804), to(#911403));
-    background-image: -webkit-linear-gradient(top, #ad1804, #911403);
-    background-image:    -moz-linear-gradient(top, #ad1804, #911403);
-    background-image:     -ms-linear-gradient(top, #ad1804, #911403);
-    background-image:      -o-linear-gradient(top, #ad1804, #911403);
-    background-image:         linear-gradient(top, #ad1804, #911403);
+    background-color: #e3653f;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#e3653f), to(#c01c03));
+    background-image: -webkit-linear-gradient(top, #e3653f, #c01c03);
+    background-image:    -moz-linear-gradient(top, #e3653f, #c01c03);
+    background-image:     -ms-linear-gradient(top, #e3653f, #c01c03);
+    background-image:      -o-linear-gradient(top, #e3653f, #c01c03);
+    background-image:         linear-gradient(top, #e3653f, #c01c03);
 }
 
 button.negative:active,
 .button.negative:active {
     box-shadow: 0 0 5px #333 inset;
+    color: #cfa391;
+
+    background-color: #83311e;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#83311e), to(#670d01));
+    background-image: -webkit-linear-gradient(top, #83311e, #670d01);
+    background-image:    -moz-linear-gradient(top, #83311e, #670d01);
+    background-image:     -ms-linear-gradient(top, #83311e, #670d01);
+    background-image:      -o-linear-gradient(top, #83311e, #670d01);
+    background-image:         linear-gradient(top, #83311e, #670d01);
 }
 
-.submit #cancel {
-  float: right;
-  margin-right: 15px;
+button.loading, input[type="submit"].loading, .submit button.loading, .submit .button.loading{
+    background-color: #4eb5e5;
+    background-image: url("/common/i/button-loader.gif"), -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf));
+    background-image: url("/common/i/button-loader.gif"), -webkit-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-loader.gif"),    -moz-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-loader.gif"),      -ms-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-loader.gif"),       -o-linear-gradient(top, #4eb5e5, #3196cf);
+    background-image: url("/common/i/button-loader.gif"),          linear-gradient(top, #4eb5e5, #3196cf);
+    background-position: 95% center;
+}
+
+.tospp {
+  line-height: 14px;
+}
+
+.buttonrow {
   line-height: 28px;
 }
 
+.buttonrow > .right {
+  margin-right: 15px;
+}
+
+.buttonrow > .right.emphasize {
+  margin-right: 0;
+  margin-top: 8px;
+}
+
 a.secondary[disabled], .submit_disabled a.secondary, .submit_disabled a.secondary:focus, .submit_disabled a.secondary:active {
   color: #999;
 }
@@ -369,22 +430,29 @@ 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 {
-  text-align: center;
+#wait .contents, #error .contents, #delay .contents {
+    max-width: 430px;
+    margin: 0 auto;
 }
 
+#wait h2, #delay h2 {
+    font-size: 20px;
+}
 
-#wait h2, #error h2, #delay h2 {
+#error h2 {
     font-size: 22px;
-    margin-bottom: 10px;
+}
+
+#wait p, #error p, #delay p {
+    margin-top: 20px;
 }
 
 #error .emphasis {
@@ -392,7 +460,7 @@ footer .help {
     color: #aa1401;
 }
 
-#error_contents {
+#error .contents, #wait .contents {
     max-width: 430px;
     margin: 0 auto;
 }
@@ -415,6 +483,19 @@ footer .help {
   color: #333;
 }
 
+.submit {
+    margin-top: 10px;
+}
+
+.submit > p {
+  margin-top: 10px;
+}
+
+.tospp {
+  line-height: 1.2;
+}
+
+
 #showDevelopment {
   position: absolute;
   top: 0;
diff --git a/resources/static/fonts/LICENSE.txt b/resources/static/common/fonts/LICENSE.txt
similarity index 98%
rename from resources/static/fonts/LICENSE.txt
rename to resources/static/common/fonts/LICENSE.txt
index 0b365b09f4a75a94f6e9baa26b7283ce7c873d0d..2c227077890c8c6b4c4edd557015c1ba8f760d18 100644
--- a/resources/static/fonts/LICENSE.txt
+++ b/resources/static/common/fonts/LICENSE.txt
@@ -1,203 +1,203 @@
-Fonts obtained from Google's Web Font service at: http://www.google.com/webfonts
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
+Fonts obtained from Google's Web Font service at: http://www.google.com/webfonts
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
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/common/i/button-arrow-active.png b/resources/static/common/i/button-arrow-active.png
new file mode 100644
index 0000000000000000000000000000000000000000..6d8b4b1ebee6f2c6e86609b68187b1bc7e8a6aee
Binary files /dev/null and b/resources/static/common/i/button-arrow-active.png differ
diff --git a/resources/static/common/i/button-arrow-hover.png b/resources/static/common/i/button-arrow-hover.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f16a66797ce4ce0e21223d4d8a873c4064164ed
Binary files /dev/null and b/resources/static/common/i/button-arrow-hover.png differ
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/common/i/button-loader.gif b/resources/static/common/i/button-loader.gif
new file mode 100644
index 0000000000000000000000000000000000000000..48b61ac1b684c487b5e270bd75e6166ff1193f29
Binary files /dev/null and b/resources/static/common/i/button-loader.gif differ
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 99%
rename from resources/static/lib/dom-jquery.js
rename to resources/static/common/js/lib/dom-jquery.js
index 6438fec068304658384f58d1e86e2c0c1c309dcb..0faeb1ccc0db906754e3461716bbfd2b8c3bd1dc 100644
--- a/resources/static/lib/dom-jquery.js
+++ b/resources/static/common/js/lib/dom-jquery.js
@@ -313,7 +313,7 @@ BrowserID.DOM = ( function() {
         },
 
         /**
-         * Show an element/elements
+         * Show an element
          * @method show
          * @param {selector || element} elementToShow
          */
@@ -322,15 +322,13 @@ BrowserID.DOM = ( function() {
         },
 
         /**
-         * Hide an element/elements
+         * Hide an element
          * @method hide
          * @param {selector || element} elementToHide
          */
         hide: function( elementToHide ) {
           return jQuery( elementToHide ).hide();
         }
-
-
     };
 
     function isValBased( target ) {
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 90%
rename from resources/static/lib/micrajax.js
rename to resources/static/common/js/lib/micrajax.js
index f56dbcec347c3ffc1edbbdae17a4e438f9e23951..1bf9522b13ce4025c33f926d76f4fae938403cba 100644
--- a/resources/static/lib/micrajax.js
+++ b/resources/static/common/js/lib/micrajax.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser:true, forin: true, laxbreak: 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -17,12 +17,16 @@ window.Micrajax = (function() {
   function getXHRObject() {
     var xhrObject;
 
-    if (window.ActiveXObject) {
-      xhrObject = new ActiveXObject('Microsoft.XMLHTTP');
-    }
-    else if (window.XMLHttpRequest) {
+    // From  http://blogs.msdn.com/b/ie/archive/2011/08/31/browsing-without-plug-ins.aspx
+    // Best Practice: Use Native XHR, if available
+    if (window.XMLHttpRequest) {
+      // If IE7+, Gecko, WebKit: Use native object
       xhrObject = new XMLHttpRequest();
     }
+    else if (window.ActiveXObject) {
+      // ...if not, try the ActiveX control
+      xhrObject = new ActiveXObject('Microsoft.XM/LHTTP');
+    }
 
     return xhrObject;
   }
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 100%
rename from resources/static/shared/models/interaction_data.js
rename to resources/static/common/js/models/interaction_data.js
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 93%
rename from resources/static/shared/modules/cookie_check.js
rename to resources/static/common/js/modules/cookie_check.js
index a1f361dd5ded9fea8d531c1944e3842d78eb462d..c3057a503d79a9dd9076f04668e0f8414670fe9c 100644
--- a/resources/static/shared/modules/cookie_check.js
+++ b/resources/static/common/js/modules/cookie_check.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: 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
diff --git a/resources/static/shared/modules/development.js b/resources/static/common/js/modules/development.js
similarity index 97%
rename from resources/static/shared/modules/development.js
rename to resources/static/common/js/modules/development.js
index 5c676960ac8b580463109857f3a80691321d9bd9..4511c38511b86c78d41cdbe420b37b2091129081 100644
--- a/resources/static/shared/modules/development.js
+++ b/resources/static/common/js/modules/development.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: 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
diff --git a/resources/static/shared/modules/interaction_data.js b/resources/static/common/js/modules/interaction_data.js
similarity index 99%
rename from resources/static/shared/modules/interaction_data.js
rename to resources/static/common/js/modules/interaction_data.js
index ff0c977231e68796dd44c5e26069334250931ff5..8f36514dac7ddc5f6f13dccd4d4e6a593f105ef5 100644
--- a/resources/static/shared/modules/interaction_data.js
+++ b/resources/static/common/js/modules/interaction_data.js
@@ -124,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,
diff --git a/resources/static/shared/modules/page_module.js b/resources/static/common/js/modules/page_module.js
similarity index 98%
rename from resources/static/shared/modules/page_module.js
rename to resources/static/common/js/modules/page_module.js
index 36aa78c7c29a72aae98d2d6552b08100467c54b2..d89ae38a790e9fee8b95027b202a5b8e4edf68b8 100644
--- a/resources/static/shared/modules/page_module.js
+++ b/resources/static/common/js/modules/page_module.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: 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
@@ -119,6 +119,8 @@ BrowserID.Modules.PageModule = (function() {
       self.hideError();
       self.hideDelay();
 
+      dom.removeClass("body", "rptospp");
+
       screens.form.show(template, data);
       dom.focus("input:visible:not(:disabled):eq(0)");
       // XXX jQuery.  bleck.
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 98%
rename from resources/static/shared/network.js
rename to resources/static/common/js/network.js
index 752ce427df57ff8903fea6a742ccd7b795b97703..26397bc3ea7beb72971b3b55455628da763a44b4 100644
--- a/resources/static/shared/network.js
+++ b/resources/static/common/js/network.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: true, _: 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
@@ -6,7 +6,8 @@
 BrowserID.Network = (function() {
   "use strict";
 
-  var bid = BrowserID,
+  var jwcrypto = require("./lib/jwcrypto"),
+      bid = BrowserID,
       complete = bid.Helpers.complete,
       context,
       server_time,
@@ -45,10 +46,7 @@ BrowserID.Network = (function() {
     setUserID(result.userid);
 
     // seed the PRNG
-    // FIXME: properly abstract this out, probably by exposing a jwcrypto
-    // interface for randomness
-    // require("./libs/all").sjcl.random.addEntropy(result.random_seed);
-    // FIXME: this wasn't doing anything for real, so commenting this out for now
+    jwcrypto.addEntropy(result.random_seed);
   }
 
   function withContext(cb, onFailure) {
diff --git a/resources/static/shared/provisioning.js b/resources/static/common/js/provisioning.js
similarity index 98%
rename from resources/static/shared/provisioning.js
rename to resources/static/common/js/provisioning.js
index fa91170c2ea3941fa2c59b5b1b53ff748125f2d7..53859547130ee17a4033c1ae060e879ebbbbf704 100644
--- a/resources/static/shared/provisioning.js
+++ b/resources/static/common/js/provisioning.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: true, _: 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
diff --git a/resources/static/shared/renderer.js b/resources/static/common/js/renderer.js
similarity index 95%
rename from resources/static/shared/renderer.js
rename to resources/static/common/js/renderer.js
index 8cbfb216a5c541d1ac62782efc3564abea3df593..4f74c0c414b83e8af803fdc1be5f61bbdcfd174d 100644
--- a/resources/static/shared/renderer.js
+++ b/resources/static/common/js/renderer.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: true, _: 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
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 96%
rename from resources/static/shared/state_machine.js
rename to resources/static/common/js/state_machine.js
index c6dcafdcc6db6f2f1c4a8e58754d0f859b02bcca..12a4c4a1b32e4a81f5b49550f282608293b7039b 100644
--- a/resources/static/shared/state_machine.js
+++ b/resources/static/common/js/state_machine.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
diff --git a/resources/static/shared/storage.js b/resources/static/common/js/storage.js
similarity index 100%
rename from resources/static/shared/storage.js
rename to resources/static/common/js/storage.js
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 97%
rename from resources/static/shared/tooltip.js
rename to resources/static/common/js/tooltip.js
index 912595e49333cf629f26b5b9f412d28accaf3b47..e50d8bc89ffb416a694599b9b165bc9ca04dafa3 100644
--- a/resources/static/shared/tooltip.js
+++ b/resources/static/common/js/tooltip.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
diff --git a/resources/static/shared/user.js b/resources/static/common/js/user.js
similarity index 98%
rename from resources/static/shared/user.js
rename to resources/static/common/js/user.js
index f39d3935d9bdae5f9bcdb3cf966f1212769c2318..b1be1696d9d8cddb49f66ba00eaa51f88060a168 100644
--- a/resources/static/shared/user.js
+++ b/resources/static/common/js/user.js
@@ -160,6 +160,10 @@ BrowserID.User = (function() {
       throw "invalid email type (should be 'secondary' or 'primary'): " + type;
   }
 
+  function getIdPName(addressInfo) {
+    return addressInfo.email.replace(/.*@/, "");
+  }
+
   /**
    * Persist an address and key pair locally.
    * @method persistEmailKeypair
@@ -264,6 +268,14 @@ BrowserID.User = (function() {
       return origin;
     },
 
+    setOriginEmail: function(email) {
+      storage.site.set(origin, "email", email);
+    },
+
+    getOriginEmail: function() {
+      return storage.site.get(origin, "email");
+    },
+
     /**
      * Get the hostname for the set origin
      * @method getHostname
@@ -817,6 +829,7 @@ BrowserID.User = (function() {
           if(info.type === "primary") {
             User.isUserAuthenticatedToPrimary(email, info, function(authed) {
               info.authed = authed;
+              info.idpName = getIdPName(info);
               complete(info);
             }, onFailure);
           }
@@ -952,8 +965,14 @@ BrowserID.User = (function() {
      */
     syncEmailKeypair: function(email, onComplete, onFailure) {
       prepareDeps();
-      jwcrypto.generateKeypair({algorithm: "DS", keysize: bid.KEY_LENGTH}, function(err, keypair) {
-        certifyEmailKeypair(email, keypair, onComplete, onFailure);
+      // jwcrypto depends on a random seed being set to generate a keypair.
+      // The seed is set with a call to network.withContext.  Ensure the
+      // random seed is set before continuing or else the seed may not be set,
+      // the key never created, and the onComplete callback never called.
+      network.withContext(function() {
+        jwcrypto.generateKeypair({algorithm: "DS", keysize: bid.KEY_LENGTH}, function(err, keypair) {
+          certifyEmailKeypair(email, keypair, onComplete, onFailure);
+        });
       });
     },
 
@@ -1216,8 +1235,6 @@ BrowserID.User = (function() {
       } else {
         complete(onFailure, "user is not authenticated");
       }
-
-      return shouldAsk;
     }
 
   };
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 72%
rename from resources/static/shared/wait-messages.js
rename to resources/static/common/js/wait-messages.js
index 00a64aa65609bbe0036a9bda3033220cced2ca87..734239bb9e91e5f25836da3ba117bc32a966fa94 100644
--- a/resources/static/shared/wait-messages.js
+++ b/resources/static/common/js/wait-messages.js
@@ -7,7 +7,7 @@ BrowserID.Wait = (function(){
   var Wait = {
     authentication: {
       title: gettext("Finishing Sign In..."),
-      message:  gettext("In just a moment you'll be signed into BrowserID.")
+      message:  gettext("In just a moment you'll be signed into Persona.")
     },
 
     generateKey: {
@@ -16,8 +16,8 @@ BrowserID.Wait = (function(){
     },
 
     slowXHR: {
-      title:  gettext("We are sorry, this request is taking a LOOONG time."),
-      message:  gettext("This message will go away when the request completes (hopefully soon). If you wait too long, close this window and try again."),
+      title:  gettext("We are sorry, this is taking a longer than it should."),
+      message:  gettext("If this doesn't resolve itself within a few seconds, please close the window and try again."),
       id: "slowXHR"
     }
 
diff --git a/resources/static/shared/xhr.js b/resources/static/common/js/xhr.js
similarity index 98%
rename from resources/static/shared/xhr.js
rename to resources/static/common/js/xhr.js
index 2d86c6fde9fa421161c56f142bad9562482830d9..007d9c31dd5b058d578bbbb84e69de284d66ec9a 100644
--- a/resources/static/shared/xhr.js
+++ b/resources/static/common/js/xhr.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
diff --git a/resources/static/shared/xhr_transport.js b/resources/static/common/js/xhr_transport.js
similarity index 83%
rename from resources/static/shared/xhr_transport.js
rename to resources/static/common/js/xhr_transport.js
index 61e639646b40145ae723c2c06fbb0a05eaa21f2f..6366d032d44e93abee200f759a288482c796662d 100644
--- a/resources/static/shared/xhr_transport.js
+++ b/resources/static/common/js/xhr_transport.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
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/m.css b/resources/static/dialog/css/m.css
index ac8b2285240c5f6f43d8bce5d1c9547415d87b1c..40509beb05e94aa7dfa4a385281f9b620b4825f0 100644
--- a/resources/static/dialog/css/m.css
+++ b/resources/static/dialog/css/m.css
@@ -124,7 +124,7 @@
 
   #selectEmail > .inputs > li > label {
     margin: 0;
-    padding: 15px 0;
+    padding: 8px 0;
   }
 
   #signIn .submit > p {
@@ -185,10 +185,6 @@
   }
 
 
-  .forgot {
-    font-size: 16px;
-  }
-
   .inputs > li {
     margin-top: 12px;
   }
diff --git a/resources/static/dialog/css/popup.css b/resources/static/dialog/css/style.css
similarity index 84%
rename from resources/static/dialog/css/popup.css
rename to resources/static/dialog/css/style.css
index 39d97a1f508042e5650c79ca03fa7d77b6f0dff5..8e6a7eaf56071cd67f1856944fc699d7ba568c8c 100644
--- a/resources/static/dialog/css/popup.css
+++ b/resources/static/dialog/css/style.css
@@ -5,13 +5,14 @@
 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));
+  line-height: 18px;
 }
 
 #signIn {
@@ -65,10 +66,19 @@ header {
     background-image:         linear-gradient(top, rgba(0,0,0,.05), rgba(0,0,0,0));
 }
 
+h2 {
+    font-size: 20px;
+}
+
+h3 {
+    font-size: 18px;
+}
+
+
 .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;
@@ -135,6 +145,11 @@ section > .contents {
      * otherwise the buttons slide right with the arrow
      */
     width: 250px;
+    font-size: 13px;
+}
+
+.form_section p {
+    margin-top: 8px;
 }
 
 
@@ -241,7 +256,7 @@ section > .contents {
 
 #signIn .table {
     background-color: #eff0f2;
-    padding: 20px;
+    padding: 0 20px;
 }
 
 .arrowContainer {
@@ -259,7 +274,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;
@@ -284,15 +299,16 @@ section > .contents {
 #favicon img {
     display: block;
     margin: 0 auto;
-    max-height: 128px;
-    max-width: 128px;
+    max-height: 100px;
+    max-width: 100px;
 }
 
 #favicon h2, #favicon h3 {
     white-space: nowrap;
     text-overflow: ellipsis;
-    line-height: 1.3;  /* the 1.3em is to keep y, g, j, etc from having their bottoms chopped off */
-    overflow: hidden;
+    line-height: 1.3em;  /* the 1.3em is to keep y, g, j, etc from having their bottoms chopped off */
+    overflow: hidden;    /* Use the same overflow for everything, this allows us to show the ellipsis.  Trying to set overflow-x only causes a scroll bar to be shown in firefox.  */
+    margin: 10px 0 0 0;
 }
 
 #favicon h2 {
@@ -300,7 +316,7 @@ section > .contents {
 }
 
 #favicon h3 {
-    font-size: 19px;
+    font-size: 16px;
     margin-top: 0;
 }
 
@@ -340,16 +356,8 @@ div#required_email {
     padding-right: 1px;
 }
 
-.inputs {
-  line-height: 18px;
-}
-
 .inputs > li {
-  margin-top: 10px;
-}
-
-.inputs > li:first-child {
-  margin-top: 0;
+  margin-top: 8px;
 }
 
 .inputs > li > label {
@@ -362,7 +370,7 @@ div#required_email {
 }
 
 #selectEmail > .inputs > li > label {
-    padding: 6px 1px;
+    padding: 3px 1px;
     white-space: nowrap;
 }
 
@@ -379,19 +387,28 @@ div#required_email {
 }
 
 .submit {
-    margin-top: 10px;
+    margin-top: 8px;
     color: #333;
     font-size: 11px;
-    line-height: 14px;
 }
 
 .submit > p {
-  margin-top: 10px;
+  margin-top: 8px;
 }
 
 .tospp {
-    color: #787878;
-    clear: right;
+  font-size: 11px;
+  color: #787878;
+}
+
+#rptospp {
+  display: none;
+  margin: 10px auto;
+  max-width: 280px;
+}
+
+.rptospp #rptospp {
+  display: block;
 }
 
 a.emphasize {
@@ -430,6 +447,7 @@ a.emphasize:active {
 #back {
   color: #000;
   border-bottom: 1px dotted;
+  font-weight: normal;
 }
 
 .submit > button {
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 95%
rename from resources/static/dialog/resources/helpers.js
rename to resources/static/dialog/js/misc/helpers.js
index af595a5338e5dba843f45572329d1c648496daf7..ce0f58a472e1a65dfb689a64d3eaec5bf1a0f390 100644
--- a/resources/static/dialog/resources/helpers.js
+++ b/resources/static/dialog/js/misc/helpers.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
@@ -11,7 +11,8 @@
       complete = helpers.complete,
       user = bid.User,
       tooltip = bid.Tooltip,
-      errors = bid.Errors;
+      errors = bid.Errors,
+      dom = bid.DOM;
 
   function animateClose(callback) {
     var body = $("body"),
@@ -135,6 +136,10 @@
     }, self.getErrorDialog(errors.addEmail, callback));
   }
 
+  function showRPTosPP() {
+    dom.addClass("body", "rptospp");
+  }
+
   helpers.Dialog = helpers.Dialog || {};
 
   helpers.extend(helpers.Dialog, {
@@ -145,7 +150,8 @@
     addSecondaryEmail: addSecondaryEmail,
     resetPassword: resetPassword,
     cancelEvent: helpers.cancelEvent,
-    animateClose: animateClose
+    animateClose: animateClose,
+    showRPTosPP: showRPTosPP
   });
 
 }());
diff --git a/resources/static/dialog/resources/internal_api.js b/resources/static/dialog/js/misc/internal_api.js
similarity index 98%
rename from resources/static/dialog/resources/internal_api.js
rename to resources/static/dialog/js/misc/internal_api.js
index 3f0ceb8be5077f47826ece6a1b64ccb6a7e00feb..5a5e644c0dbd2cd6c1038c7ede4c8f937d25cf1f 100644
--- a/resources/static/dialog/resources/internal_api.js
+++ b/resources/static/dialog/js/misc/internal_api.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
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 74%
rename from resources/static/dialog/resources/state.js
rename to resources/static/dialog/js/misc/state.js
index 7f36178c5aef18b25d44053317291c585589bd16..65fdc9b0e691806dc8929219fa4acd4b6e9710dd 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/js/misc/state.js
@@ -1,9 +1,11 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 BrowserID.State = (function() {
+  "use strict";
+
   var bid = BrowserID,
       storage = bid.Storage,
       network = bid.Network,
@@ -19,6 +21,7 @@ BrowserID.State = (function() {
       primaryVerificationInfo;
 
   function startStateMachine() {
+    /*jshint validthis: true*/
     var self = this,
         handleState = function(msg, callback) {
           self.subscribe(msg, function(msg, info) {
@@ -42,8 +45,9 @@ BrowserID.State = (function() {
 
     handleState("start", function(msg, info) {
       self.hostname = info.hostname;
-      self.privacyURL = info.privacyURL;
-      self.tosURL = info.tosURL;
+      self.siteName = info.siteName || info.hostname;
+      self.siteTOSPP = !!(info.privacyPolicy && info.termsOfService);
+
       requiredEmail = info.requiredEmail;
 
       startAction(false, "doRPInfo", info);
@@ -62,13 +66,6 @@ BrowserID.State = (function() {
     });
 
     handleState("window_unload", function() {
-      if (!self.success) {
-        storage.setReturnTo("");
-        // 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.
@@ -87,8 +84,10 @@ BrowserID.State = (function() {
         self.email = requiredEmail;
         startAction("doAuthenticateWithRequiredEmail", {
           email: requiredEmail,
-          privacyURL: self.privacyURL,
-          tosURL: self.tosURL
+          // New users are handled by either the "new_user" flow or the
+          // "primary_user" flow. The Persona TOS/PP will be shown to users in
+          // these stages.
+          siteTOSPP: self.siteTOSPP && !user.getOriginEmail()
         });
       }
       else if (authenticated) {
@@ -99,8 +98,11 @@ BrowserID.State = (function() {
     });
 
     handleState("authenticate", function(msg, info) {
-      info.privacyURL = self.privacyURL;
-      info.tosURL = self.tosURL;
+      _.extend(info, {
+        siteName: self.siteName,
+        siteTOSPP: self.siteTOSPP
+      });
+
       startAction("doAuthenticate", info);
     });
 
@@ -111,9 +113,23 @@ BrowserID.State = (function() {
       // 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;
+      _.extend(info, {
+        // cancel is disabled if the user is doing the initial password set
+        // for a requiredEmail.
+        cancelable: !requiredEmail,
+
+        // New users in the requiredEmail flow are sent directly to the
+        // set_password screen.  If this happens, they have not yet seen the
+        // TOS/PP agreement.
+
+        // Always show the Persona TOS/PP to new requiredEmail users.
+        personaTOSPP: !!requiredEmail,
+
+        // The site TOS/PP must be shown to new requiredEmail users if there is
+        // a site TOS/PP
+        siteTOSPP: !!requiredEmail && self.siteTOSPP
+      });
+
       startAction(false, "doSetPassword", info);
     });
 
@@ -142,7 +158,12 @@ BrowserID.State = (function() {
 
     handleState("user_staged", function(msg, info) {
       self.stagedEmail = info.email;
-      info.required = !!requiredEmail;
+
+      _.extend(info, {
+        required: !!requiredEmail,
+        siteName: self.siteName
+      });
+
       startAction("doConfirmUser", info);
     });
 
@@ -183,12 +204,26 @@ BrowserID.State = (function() {
     });
 
     handleState("primary_user_unauthenticated", function(msg, info) {
-      info = helpers.extend(info, {
+      // a user who lands here is not authenticated with their identity
+      // provider.
+      _.extend(info, {
         add: !!addPrimaryUser,
         email: email,
         requiredEmail: !!requiredEmail,
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
+
+        // In the requiredEmail flow, a user who is not authenticated with
+        // their primary will be sent directly to the "you must verify
+        // with your IdP" screen.
+        //
+        // Show the siteTOSPP to all requiredEmail users who have never visited
+        // the site before.
+        siteTOSPP: requiredEmail && self.siteTOSPP && !user.getOriginEmail(),
+
+        // Show the persona TOS/PP only to requiredEmail users who are creating
+        // a new account.
+        personaTOSPP: requiredEmail && !addPrimaryUser,
+        siteName: self.siteName,
+        idpName: info.idpName || URLParse(info.auth_url).host
       });
 
       if (primaryVerificationInfo) {
@@ -225,8 +260,7 @@ BrowserID.State = (function() {
     handleState("pick_email", function() {
       startAction("doPickEmail", {
         origin: self.hostname,
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
+        siteTOSPP: self.siteTOSPP && !user.getOriginEmail()
       });
     });
 
@@ -267,8 +301,12 @@ BrowserID.State = (function() {
               startAction("doAuthenticateWithRequiredEmail", {
                 email: email,
                 secondary_auth: true,
-                privacyURL: self.privacyURL,
-                tosURL: self.tosURL
+
+                // This is a user is already authenticated to the assertion
+                // level who has chosen a secondary email address from the
+                // pick_email screen. They would have been shown the
+                // siteTOSPP there.
+                siteTOSPP: false
               });
             }
             else {
@@ -366,12 +404,26 @@ BrowserID.State = (function() {
       redirectToState("email_chosen", info);
     });
 
-    handleState("add_email", function(msg, info) {
-      info = helpers.extend(info, {
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
-      });
+    handleState("reset_password", function(msg, info) {
+      info = info || {};
+      // reset_password says the user has confirmed that they want to
+      // reset their password.  doResetPassword will attempt to invoke
+      // the create_user wsapi.  If the wsapi call is successful,
+      // the user will be shown the "go verify your account" message.
+
+      // We have to save the staged email address here for when the user
+      // verifies their account and user_confirmed is called.
+      self.stagedEmail = info.email;
+      startAction(false, "doResetPassword", info);
+    });
 
+    handleState("add_email", function(msg, info) {
+      // add_email indicates the user wishes to add an email to the account,
+      // the add_email screen must be displayed.  After the user enters the
+      // email address they wish to add, add_email will trigger
+      // either 1) primary_user or 2) email_staged. #1 occurs if the email
+      // address is a primary address, #2 occurs if the address is a secondary
+      // and the verification email has been sent.
       startAction("doAddEmail", info);
     });
 
@@ -379,9 +431,26 @@ BrowserID.State = (function() {
       user.passwordNeededToAddSecondaryEmail(function(passwordNeeded) {
         if(passwordNeeded) {
           self.addEmailEmail = info.email;
-          // cancel is disabled if the user is doing the initial password set
-          // for a requiredEmail.
-          info.cancelable = !requiredEmail;
+
+          _.extend(info, {
+            // cancel is disabled if the user is doing the initial password set
+            // for a requiredEmail.
+            cancelable: !requiredEmail,
+
+            // stage_email is called to add an email to an already existing
+            // account.  Since it is an already existing account, the
+            // personaTOSPP does not need to be shown.
+            personaTOSPP: false,
+
+            // requiredEmail users who are adding an email but must set their
+            // password will be redirected here without seeing any other
+            // screens. non-requiredEmail users will have already seen the site
+            // TOS/PP in the pick-email screen if it was necessary.  Since
+            // requiredEmail users may not have seen the screen before, show it
+            // here if there is no originEmail.
+            siteTOSPP: self.siteTOSPP && requiredEmail && !user.getOriginEmail()
+          });
+
           startAction(false, "doSetPassword", info);
         }
         else {
@@ -394,7 +463,10 @@ BrowserID.State = (function() {
 
     handleState("email_staged", function(msg, info) {
       self.stagedEmail = info.email;
-      info.required = !!requiredEmail;
+      _.extend(info, {
+        required: !!requiredEmail,
+        siteName: self.siteName
+      });
       startAction("doConfirmEmail", info);
     });
 
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/js/modules/actions.js
similarity index 96%
rename from resources/static/dialog/controllers/actions.js
rename to resources/static/dialog/js/modules/actions.js
index 3a95c89155d98f12c71b9f8eb11231f0a1f82bfe..aec8e96a38ca1abfa54e69e683aa2bd7f2134c99 100644
--- a/resources/static/dialog/controllers/actions.js
+++ b/resources/static/dialog/js/modules/actions.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -34,11 +34,11 @@ BrowserID.Modules.Actions = (function() {
 
   function startRegCheckService(options, verifier, message, password) {
     var controller = startService("check_registration", {
-      email: options.email,
-      required: options.required,
       verifier: verifier,
       verificationMessage: message,
-      password: password
+      password: password,
+      siteName: options.siteName,
+      email: options.email
     });
     controller.startCheck();
   }
@@ -57,10 +57,6 @@ BrowserID.Modules.Actions = (function() {
       if(data.ready) _.defer(data.ready);
     },
 
-    doRPInfo: function(info) {
-      startService("rp_info", info);
-    },
-
     doCancel: function() {
       if(onsuccess) onsuccess(null);
     },
@@ -156,6 +152,10 @@ BrowserID.Modules.Actions = (function() {
 
     doGenerateAssertion: function(info) {
       startService("generate_assertion", info);
+    },
+
+    doRPInfo: function(info) {
+      startService("rp_info", info);
     }
   });
 
diff --git a/resources/static/dialog/controllers/add_email.js b/resources/static/dialog/js/modules/add_email.js
similarity index 82%
rename from resources/static/dialog/controllers/add_email.js
rename to resources/static/dialog/js/modules/add_email.js
index da642e0d2ced87d3cdf769b0d2389e0b2ad46e9a..151baa6ca2393679ca8b612f292f5281198da430 100644
--- a/resources/static/dialog/controllers/add_email.js
+++ b/resources/static/dialog/js/modules/add_email.js
@@ -1,5 +1,5 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
-/*global _: true, BrowserID: true, PageController: true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
+/*global _: true, BrowserID: true, PageController: true, gettext: 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,6 +9,7 @@ BrowserID.Modules.AddEmail = (function() {
   var bid = BrowserID,
       dom = bid.DOM,
       helpers = bid.Helpers,
+      user = bid.User,
       dialogHelpers = helpers.Dialog,
       errors = bid.Errors,
       complete = helpers.complete,
@@ -54,12 +55,9 @@ BrowserID.Modules.AddEmail = (function() {
   var Module = bid.Modules.PageModule.extend({
     start: function(options) {
       var self=this,
-          templateData = helpers.extend({}, options, {
-            privacy_url: options.privacyURL || null,
-            tos_url: options.tosURL || null
-          });
+          originEmail = user.getOriginEmail();
 
-      self.renderDialog("add_email", templateData);
+      self.renderDialog("add_email", options);
       hideHint("addressInfo");
 
       self.click("#cancel", cancelAddEmail);
diff --git a/resources/static/dialog/controllers/authenticate.js b/resources/static/dialog/js/modules/authenticate.js
similarity index 81%
rename from resources/static/dialog/controllers/authenticate.js
rename to resources/static/dialog/js/modules/authenticate.js
index c316e55a5e5822825c2ad2bb69f1b32a6bf106fa..24ea77b11e8acf28ef98b6af66c62dd200061e56 100644
--- a/resources/static/dialog/controllers/authenticate.js
+++ b/resources/static/dialog/js/modules/authenticate.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID:true, PageController: 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
@@ -25,6 +25,7 @@ BrowserID.Modules.Authenticate = (function() {
   }
 
   function initialState(info) {
+    /*jshint validthis: true*/
     var self=this;
 
     self.submit = checkEmail;
@@ -33,11 +34,13 @@ BrowserID.Modules.Authenticate = (function() {
     }
     else {
       showHint("start");
+      enterEmailState.call(self);
       complete(info.ready);
     }
   }
 
   function checkEmail(info) {
+    /*jshint validthis: true*/
     var email = getEmail(),
         self = this;
 
@@ -69,6 +72,7 @@ BrowserID.Modules.Authenticate = (function() {
   }
 
   function createSecondaryUser(callback) {
+    /*jshint validthis: true*/
     var self=this,
         email = getEmail();
 
@@ -80,6 +84,7 @@ BrowserID.Modules.Authenticate = (function() {
   }
 
   function authenticate() {
+    /*jshint validthis: true*/
     var email = getEmail(),
         pass = helpers.getAndValidatePassword("#password"),
         self = this;
@@ -98,7 +103,7 @@ BrowserID.Modules.Authenticate = (function() {
   function showHint(showSelector, callback) {
     _.each(hints, function(className) {
       if(className != showSelector) {
-        $("." + className).not("." + showSelector).hide();
+        dom.hide("." + className + ":not(." + showSelector + ")");
       }
     });
 
@@ -112,14 +117,16 @@ BrowserID.Modules.Authenticate = (function() {
     });
   }
 
-  function enterEmailState(el) {
-    if (!$("#email").is(":disabled")) {
+  function enterEmailState() {
+    /*jshint validthis: true*/
+    if (!dom.is("#email", ":disabled")) {
       this.submit = checkEmail;
       showHint("start");
     }
   }
 
   function enterPasswordState(callback) {
+    /*jshint validthis: true*/
     var self=this;
 
     dom.setInner("#password", "");
@@ -129,10 +136,13 @@ BrowserID.Modules.Authenticate = (function() {
     showHint("returning", function() {
       dom.focus("#password");
     });
+
+
     complete(callback);
   }
 
   function forgotPassword() {
+    /*jshint validthis: true*/
     var email = getEmail();
     if (email) {
       var info = addressInfo || { email: email };
@@ -141,6 +151,7 @@ BrowserID.Modules.Authenticate = (function() {
   }
 
   function emailKeyUp() {
+    /*jshint validthis: true*/
     var newEmail = dom.getInner("#email");
     if (newEmail !== lastEmail) {
       lastEmail = newEmail;
@@ -157,13 +168,20 @@ BrowserID.Modules.Authenticate = (function() {
 
       var self=this;
       self.renderDialog("authenticate", {
-        sitename: user.getHostname(),
-        email: lastEmail,
-        privacy_url: options.privacyURL,
-        tos_url: options.tosURL
+        siteName: options.siteName,
+        email: lastEmail
       });
 
-      $(".returning,.start").hide();
+      dom.hide(".returning,.start");
+
+      // We have to show the TOS/PP agreements to *all* users here. Users who
+      // are already authenticated to their IdP but do not have a Persona
+      // account automatically have an account created with no further
+      // interaction.  To make sure they see the TOS/PP agreement, show it
+      // here.
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
 
       self.bind("#email", "keyup", emailKeyUp);
       self.click("#forgotPassword", forgotPassword);
diff --git a/resources/static/dialog/controllers/check_registration.js b/resources/static/dialog/js/modules/check_registration.js
similarity index 88%
rename from resources/static/dialog/controllers/check_registration.js
rename to resources/static/dialog/js/modules/check_registration.js
index efd38e246b6c7ee4c19515f46c5720679949a6c9..29094a1dcfd4bc7749ed50db4616f3d4baaa46c3 100644
--- a/resources/static/dialog/controllers/check_registration.js
+++ b/resources/static/dialog/js/modules/check_registration.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: true, PageController: 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
@@ -15,9 +15,14 @@ BrowserID.Modules.CheckRegistration = (function() {
     start: function(options) {
       var self=this;
       options = options || {};
-      options.required = !!options.required;
 
-      self.renderWait("confirm_email", options);
+      self.checkRequired(options, "email", "siteName");
+      var templateData = {
+        email: options.email,
+        required: options.required,
+        siteName: options.siteName
+      };
+      self.renderWait("confirm_email", templateData);
 
       self.email = options.email;
       self.verifier = options.verifier;
diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/js/modules/dialog.js
similarity index 85%
rename from resources/static/dialog/controllers/dialog.js
rename to resources/static/dialog/js/modules/dialog.js
index 930b7707eeb571d26fa6e2ec81ce267bc8e0fa06..7ed80ec7d026164860778e7cee7f979b557f9a18 100644
--- a/resources/static/dialog/controllers/dialog.js
+++ b/resources/static/dialog/js/modules/dialog.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: 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
@@ -12,6 +12,7 @@ BrowserID.Modules.Dialog = (function() {
       user = bid.User,
       errors = bid.Errors,
       dom = bid.DOM,
+      helpers = bid.Helpers,
       win = window,
       startExternalDependencies = true,
       channel,
@@ -73,10 +74,6 @@ BrowserID.Modules.Dialog = (function() {
     channel && channel.detach();
   }
 
-  function setOrigin(origin) {
-    user.setOrigin(origin);
-  }
-
   function onWindowUnload() {
     this.publish("window_unload");
   }
@@ -139,7 +136,7 @@ BrowserID.Modules.Dialog = (function() {
       var self=this,
           hash = win.location.hash;
 
-      setOrigin(origin_url);
+      user.setOrigin(origin_url);
 
 
       if (startExternalDependencies) {
@@ -159,9 +156,7 @@ BrowserID.Modules.Dialog = (function() {
       // verify params
       try {
         if (paramsFromRP.requiredEmail) {
-          if (!bid.verifyEmail(paramsFromRP.requiredEmail))
-            throw "invalid requiredEmail: (" + paramsFromRP.requiredEmail + ")";
-          params.requiredEmail = paramsFromRP.requiredEmail;
+          helpers.log("requiredEmail has been deprecated");
         }
 
         // support old parameter names...
@@ -169,8 +164,8 @@ BrowserID.Modules.Dialog = (function() {
         if (paramsFromRP.privacyURL) paramsFromRP.privacyPolicy = paramsFromRP.privacyURL;
 
         if (paramsFromRP.termsOfService && paramsFromRP.privacyPolicy) {
-          params.tosURL = fixupURL(origin_url, paramsFromRP.termsOfService);
-          params.privacyURL = fixupURL(origin_url, paramsFromRP.privacyPolicy);
+          params.termsOfService = fixupURL(origin_url, paramsFromRP.termsOfService);
+          params.privacyPolicy = fixupURL(origin_url, paramsFromRP.privacyPolicy);
         }
 
         if (paramsFromRP.siteLogo) {
@@ -196,24 +191,19 @@ BrowserID.Modules.Dialog = (function() {
           user.setReturnTo(returnTo);
         }
 
-
-        if (hash.indexOf("#CREATE_EMAIL=") === 0) {
-          var email = hash.replace(/#CREATE_EMAIL=/, "");
-          if (!bid.verifyEmail(email))
-            throw "invalid #CREATE_EMAIL= (" + email + ")";
+        if (hash.indexOf("#AUTH_RETURN") === 0) {
+          var primaryParams = JSON.parse(win.sessionStorage.primaryVerificationFlow);
+          params.email = primaryParams.email;
+          params.add = primaryParams.add;
           params.type = "primary";
-          params.email = email;
-          params.add = false;
-        }
-        else if (hash.indexOf("#ADD_EMAIL=") === 0) {
-          var email = hash.replace(/#ADD_EMAIL=/, "");
-          if (!bid.verifyEmail(email))
-            throw "invalid #ADD_EMAIL= (" + email + ")";
-          params.type = "primary";
-          params.email = email;
-          params.add = true;
+
+          // FIXME: if it's AUTH_RETURN_CANCEL, we should short-circuit
+          // the attempt at provisioning. For now, we let provisioning
+          // be tried and fail.
         }
 
+        // no matter what, we clear the primary flow state for this window
+        win.sessionStorage.primaryVerificationFlow = undefined;
       } catch(e) {
         // note: renderError accepts HTML and cheerfully injects it into a
         // frame with a powerful origin. So convert 'e' first.
diff --git a/resources/static/dialog/controllers/generate_assertion.js b/resources/static/dialog/js/modules/generate_assertion.js
similarity index 93%
rename from resources/static/dialog/controllers/generate_assertion.js
rename to resources/static/dialog/js/modules/generate_assertion.js
index eef1b2d0d3ccef38dddb34f94355064ee49c3bf3..d0045eee2f8987a60aeb79a2fc5e82c540042faa 100644
--- a/resources/static/dialog/controllers/generate_assertion.js
+++ b/resources/static/dialog/js/modules/generate_assertion.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
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 95%
rename from resources/static/dialog/controllers/is_this_your_computer.js
rename to resources/static/dialog/js/modules/is_this_your_computer.js
index 41fb15f36a029eb378485b316fa0bf4c54c78deb..b070364ee5bbc7f635f762af0898f790d69bd538 100644
--- a/resources/static/dialog/controllers/is_this_your_computer.js
+++ b/resources/static/dialog/js/modules/is_this_your_computer.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID:true, PageController: 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
diff --git a/resources/static/dialog/controllers/pick_email.js b/resources/static/dialog/js/modules/pick_email.js
similarity index 95%
rename from resources/static/dialog/controllers/pick_email.js
rename to resources/static/dialog/js/modules/pick_email.js
index b666c95e90d2d95f26bb9ca71f65695f1f581466..ecd77cbcb94c2160710fe787645a6488e1e777e7 100644
--- a/resources/static/dialog/controllers/pick_email.js
+++ b/resources/static/dialog/js/modules/pick_email.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -93,10 +93,13 @@ BrowserID.Modules.PickEmail = (function() {
 
       self.renderDialog("pick_email", {
         identities: identities,
-        siteemail: storage.site.get(origin, "email"),
-        privacy_url: options.privacyURL,
-        tos_url: options.tosURL
+        siteEmail: user.getOriginEmail()
       });
+
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
+
       dom.getElements("body").css("opacity", "1");
       if (dom.getElements("#selectEmail input[type=radio]:visible").length === 0) {
         // If there is only one email address, the radio button is never shown,
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 892448a5071df9acd2d0e21f5f5cb317d397a43b..1543f4b494005be8bdfb3fa9257dec465e58e76b 100644
--- a/resources/static/dialog/controllers/primary_user_provisioned.js
+++ b/resources/static/dialog/js/modules/primary_user_provisioned.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID:true, PageController: 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
diff --git a/resources/static/dialog/controllers/provision_primary_user.js b/resources/static/dialog/js/modules/provision_primary_user.js
similarity index 76%
rename from resources/static/dialog/controllers/provision_primary_user.js
rename to resources/static/dialog/js/modules/provision_primary_user.js
index 367195e4a0191f0054288f801d04534d4362a014..2df0b623d824a809543e1a33f359683ae8427b4b 100644
--- a/resources/static/dialog/controllers/provision_primary_user.js
+++ b/resources/static/dialog/js/modules/provision_primary_user.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID:true, PageController: 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
@@ -29,7 +29,9 @@ BrowserID.Modules.ProvisionPrimaryUser = (function() {
         case "primary.verify":
           self.close("primary_user_unauthenticated", {
             email: email,
-            auth_url: auth
+            auth_url: auth,
+            // XXX use self.addressInfo universally.
+            idpName: self.addressInfo.idpName
           });
           complete(true);
           break;
@@ -55,19 +57,15 @@ BrowserID.Modules.ProvisionPrimaryUser = (function() {
         throw "missing config option: email";
       }
 
-      if(!(auth && prov)) {
-        user.addressInfo(email, function(status) {
-          if(status.type === "primary") {
-            provisionPrimaryUser.call(self, email, status.auth, status.prov);
-          }
-          else {
-            self.renderError("error", { action: errors.provisioningBadPrimary });
-          }
-        }, self.getErrorDialog(errors.isEmailRegistered));
-      }
-      else {
-        provisionPrimaryUser.call(self, email, auth, prov);
-      }
+      user.addressInfo(email, function(status) {
+        self.addressInfo = status;
+        if(status.type === "primary") {
+          provisionPrimaryUser.call(self, email, status.auth, status.prov);
+        }
+        else {
+          self.renderError("error", { action: errors.provisioningBadPrimary });
+        }
+      }, self.getErrorDialog(errors.isEmailRegistered));
 
 
       ProvisionPrimaryUser.sc.start.call(self, options);
diff --git a/resources/static/dialog/controllers/required_email.js b/resources/static/dialog/js/modules/required_email.js
similarity index 93%
rename from resources/static/dialog/controllers/required_email.js
rename to resources/static/dialog/js/modules/required_email.js
index 6e5f97e59d25682fbda2022dba60da2a733440e8..5653913acbdcfed2d12e8c7b68941dc1bff1e5ee 100644
--- a/resources/static/dialog/controllers/required_email.js
+++ b/resources/static/dialog/js/modules/required_email.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -147,7 +147,13 @@ BrowserID.Modules.RequiredEmail = (function() {
               // We know the user has control of this address, give them
               // a chance to hit "sign in" before we kick them off to the
               // primary flow account.
-              showTemplate({ signin: true, primary: true });
+
+              // Show the Persona TOS/PP to any primary user who is authed with
+              // their IdP but not with Persona.  Unfortunately, addressInfo
+              // does not tell us whether a primary address already has an
+              // account, so we have to show the personaTOSPP to any user who
+              // is not authenticated.
+              showTemplate({ signin: true, primary: true, personaTOSPP: !auth_level });
             }
             else if(info.type === "primary" && !info.authed) {
               // User who does not control a primary address.
@@ -213,12 +219,15 @@ BrowserID.Modules.RequiredEmail = (function() {
           password: false,
           secondary_auth: false,
           primary: false,
-          privacy_url: options.privacyURL || null,
-          tos_url: options.tosURL || null
+          personaTOSPP: false
         }, templateData);
 
         self.renderDialog("required_email", templateData);
 
+        if (options.siteTOSPP) {
+          dialogHelpers.showRPTosPP.call(self);
+        }
+
         self.click("#sign_in", signIn);
         self.click("#verify_address", verifyAddress);
         self.click("#forgotPassword", forgotPassword);
diff --git a/resources/static/dialog/controllers/rp_info.js b/resources/static/dialog/js/modules/rp_info.js
similarity index 80%
rename from resources/static/dialog/controllers/rp_info.js
rename to resources/static/dialog/js/modules/rp_info.js
index 3bc9b20edd9efb34d9466a815024b6e09c0775e9..9fab0c7b295dbdc8160d72f63285e3fe0fd79b15 100644
--- a/resources/static/dialog/controllers/rp_info.js
+++ b/resources/static/dialog/js/modules/rp_info.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -7,7 +7,8 @@
 
 /**
  * Purpose:
- *  Display to the user RP related data such as hostname, name, and logo.
+ *  Display to the user RP related data such as hostname, sitename, logo,
+ *  TOS/PP, etc.
  */
 BrowserID.Modules.RPInfo = (function() {
   "use strict";
@@ -32,7 +33,9 @@ BrowserID.Modules.RPInfo = (function() {
       renderer.render("#rp_info", "rp_info", {
         hostname: options.hostname,
         siteName: options.siteName,
-        siteLogo: options.siteLogo
+        siteLogo: options.siteLogo,
+        privacyPolicy: options.privacyPolicy,
+        termsOfService: options.termsOfService
       });
 
       sc.start.call(this, options);
diff --git a/resources/static/dialog/controllers/set_password.js b/resources/static/dialog/js/modules/set_password.js
similarity index 79%
rename from resources/static/dialog/controllers/set_password.js
rename to resources/static/dialog/js/modules/set_password.js
index 7bc6848c59663f9f30ea5f8023feb0e97c9cb984..d1d3025767450ebc66e5ce570494b7e717544e09 100644
--- a/resources/static/dialog/controllers/set_password.js
+++ b/resources/static/dialog/js/modules/set_password.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -13,6 +13,7 @@ BrowserID.Modules.SetPassword = (function() {
       sc;
 
   function submit(callback) {
+    /*jshint validthis: true*/
     var pass = dom.getInner("#password"),
         vpass = dom.getInner("#vpassword"),
         options = this.options;
@@ -26,6 +27,7 @@ BrowserID.Modules.SetPassword = (function() {
   }
 
   function cancel() {
+    /*jshint validthis: true*/
     this.close("cancel_state");
   }
 
@@ -35,10 +37,16 @@ BrowserID.Modules.SetPassword = (function() {
       options = options || {};
 
       self.renderDialog("set_password", {
+        email: options.email,
         password_reset: !!options.password_reset,
-        cancelable: options.cancelable !== false
+        cancelable: options.cancelable !== false,
+        personaTOSPP: options.personaTOSPP
       });
 
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
+
       self.click("#cancel", cancel);
 
       sc.start.call(self, options);
diff --git a/resources/static/dialog/controllers/verify_primary_user.js b/resources/static/dialog/js/modules/verify_primary_user.js
similarity index 66%
rename from resources/static/dialog/controllers/verify_primary_user.js
rename to resources/static/dialog/js/modules/verify_primary_user.js
index 9e38e6be13bfec2f9039451cb860310b7b03a2f3..423175e569febcb726f721640b5060a1c7f156ba 100644
--- a/resources/static/dialog/controllers/verify_primary_user.js
+++ b/resources/static/dialog/js/modules/verify_primary_user.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global _: true, BrowserID: true, PageController: 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
@@ -12,21 +12,22 @@ BrowserID.Modules.VerifyPrimaryUser = (function() {
       add,
       email,
       auth_url,
+      dom = bid.DOM,
       helpers = bid.Helpers,
+      dialogHelpers = helpers.Dialog,
       complete = helpers.complete;
 
   function verify(callback) {
     this.publish("primary_user_authenticating");
 
-    // replace any hashes that may be there already.
-    var returnTo = win.document.location.href.replace(/#.*$/, "");
-
-    var type = add ? "ADD_EMAIL" : "CREATE_EMAIL";
-    var url = helpers.toURL(auth_url, {
-      email: email,
-      return_to: returnTo + "#" + type + "=" +email
+    // set up some information about what we're doing
+    win.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: add,
+      email: email
     });
 
+    var url = helpers.toURL(auth_url, {email: email});
+
     win.document.location = url;
 
     complete(callback);
@@ -47,12 +48,18 @@ BrowserID.Modules.VerifyPrimaryUser = (function() {
       email = data.email;
       auth_url = data.auth_url;
 
-      var templateData = helpers.extend({}, data, {
+      self.renderDialog("verify_primary_user", {
+        email: data.email,
+        auth_url: data.auth_url,
         requiredEmail: data.requiredEmail || false,
-        privacy_url: data.privacyURL || null,
-        tos_url: data.tosURL || null
+        personaTOSPP: data.personaTOSPP,
+        siteName: data.siteName,
+        idpName: data.idpName
       });
-      self.renderDialog("verify_primary_user", templateData);
+
+      if (data.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
 
       self.click("#cancel", cancel);
 
diff --git a/resources/static/dialog/start.js b/resources/static/dialog/js/start.js
similarity index 95%
rename from resources/static/dialog/start.js
rename to resources/static/dialog/js/start.js
index 70c574bad3f2e645532c48aee54fdec314b886fc..3536e1f25e1b9f346debb65f5ecc0966f04d0d21 100644
--- a/resources/static/dialog/start.js
+++ b/resources/static/dialog/js/start.js
@@ -15,7 +15,7 @@
   network.init();
 
   var hash = window.location.hash || "",
-      continuation = hash.indexOf("#CREATE_EMAIL") > -1 || hash.indexOf("#ADD_EMAIL") > -1;
+      continuation = hash.indexOf("#AUTH_RETURN") > -1;
 
   moduleManager.register("interaction_data", modules.InteractionData);
   moduleManager.start("interaction_data", { continuation: continuation });
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/dialog/views/add_email.ejs b/resources/static/dialog/views/add_email.ejs
index b98b22ab8708478c0dd7392de90e4bd2a78632eb..c702bd5ac7d85e959aba351332ce254bc4d01322 100644
--- a/resources/static/dialog/views/add_email.ejs
+++ b/resources/static/dialog/views/add_email.ejs
@@ -2,14 +2,12 @@
    - 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/. -->
 
-  <strong><%= gettext('Sign in using') %></strong>
-
   <div id="addEmail" class="cf form_section">
 
       <ul class="inputs">
           <li>
-              <label for="newEmail"><%= gettext('New email address') %></label>
-              <input id="newEmail" name="newEmail" type="email" autocapitalize="off" autocorrect="off" maxlength="254" <% if (typeof email !== "undefined") { %> value="<%= email %>" <% } %>/>
+              <label for="newEmail"><%= gettext('Add another email address to your Persona password.') %></label>
+              <input id="newEmail" name="newEmail" type="email" autocapitalize="off" autocorrect="off" maxlength="254" <% if (typeof email !== "undefined") { %> value="<%= email %>" <% } %> placeholder="<%= gettext("email address") %>"/>
 
               <div id="email_format" class="tooltip" for="newEmail">
                 <%= gettext('This field must be an email address.') %>
@@ -29,28 +27,14 @@
           </li>
 
           <li id="hint_section" class="addressInfo">
-              <%= gettext("Please hold on while we get information about your email provider.") %>
+              <%= gettext("Checking with your email provider.") %>
           </li>
       </ul>
 
-      <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('add'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
-          <button id="addNewEmail"><%= gettext('add') %></button>
-          <a href="#" id="cancel" class="action"><%= gettext('cancel') %></a>
-        <% if (privacy_url && tos_url) { %>
-          </p>
-        <% } %>
-      </div>
+      <p class="submit cf buttonrow">
+        <button id="addNewEmail"><%= gettext('add') %></button>
+        <a href="#" id="cancel" class="right"><%= gettext('cancel') %></a>
+      </p>
 
   </div>
 
diff --git a/resources/static/dialog/views/authenticate.ejs b/resources/static/dialog/views/authenticate.ejs
index 55e461d5ceaed29e52ef2f148466d2698beb7c7e..8fac82a6afa7507939539cc50064b14b4dfe0ad5 100644
--- a/resources/static/dialog/views/authenticate.ejs
+++ b/resources/static/dialog/views/authenticate.ejs
@@ -1,13 +1,18 @@
 <% /* 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/. */ %>
-  <strong><%= gettext('Sign in using') %></strong>
+
   <div class="form_section">
+      <p class="start">
+          <%= format(gettext('%s uses Persona instead of usernames to sign you in.'), ["<strong>" + siteName +"</strong>"]) %>
+
+      </p>
+
       <ul class="inputs">
 
           <li>
-              <label for="email"><%= gettext('Email') %></label>
-              <input id="email" type="email" autocapitalize="off" autocorrect="off" value="<%= email %>" maxlength="254" tabindex="1"/>
+              <label for="email"><%= gettext('To sign in with Persona, please enter your email address.') %></label>
+              <input id="email" type="email" autocapitalize="off" autocorrect="off" value="<%= email %>" maxlength="254" tabindex="1" placeholder="<%= gettext('enter email address') %>"/>
 
               <div id="email_format" class="tooltip" for="email">
                 <%= gettext('This field must be an email address.') %>
@@ -20,54 +25,44 @@
               <div id="could_not_add" class="tooltip" for="email">
                 <%= gettext('We just sent an email to that address! If you really want to send another, wait a minute or two and try again.') %>
               </div>
-
-              <div id="already_registered" class="tooltip" for="email">
-                That email address is already registered!  You should try again.
-              </div>
           </li>
 
-          <li id="hint_section" class="start">
-              <%= format(gettext('Enter your email address to sign in to <strong>%s</strong>'), [sitename]) %>
 
-          </li>
-
-          <li id="hint_section" class="addressInfo">
-              <%= gettext("Please hold on while we get information about your email provider.") %>
+          <li id="hint_section" class="addressInfo submit">
+              <%= gettext("Checking with your email provider.") %>
           </li>
 
           <li class="returning">
 
-              <a id="forgotPassword" class="forgot right" href="#" tabindex="4"><%= gettext('forgot your password?') %></a>
-              <label for="password"><%= gettext('Password') %></label>
+              <label for="password" class="hidden"><%= gettext('Password') %></label>
 
-              <input id="password" type="password" maxlength="80" tabindex="2" />
+              <input id="password" type="password" maxlength="80" tabindex="2" placeholder="<%= gettext('password') %>" />
 
               <div id="password_required" class="tooltip" for="password">
                 <%= gettext('The password field is required.') %>
               </div>
 
               <div id="cannot_authenticate" class="tooltip" for="password">
-                <%= gettext('The account cannot be logged in with this username and password.') %>
+                <%= gettext('This email address and password do not match.') %>
               </div>
           </li>
 
       </ul>
 
-      <div class="submit cf">
-        <p class="cf">
-          <button class="start addressInfo" tabindex="3"><%= gettext('next') %></button>
-          <button class="returning" tabindex="3"><%= gettext('sign in') %></button>
-        </p>
-        <% if (privacy_url && tos_url) { %>
-           <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('next'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-      <% } %>
-
-      </div>
+      <p class="submit cf buttonrow">
+        <button class="start addressInfo"><%= gettext('next') %></button>
+        <button class="returning"><%= gettext('sign in') %></button>
+        <a id="forgotPassword" class="returning" href="#"><%= gettext('Forgot your password?') %></a>
+      </p>
+
+
+      <p class="submit tospp">
+         <%= format(
+              gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                   [ "Persona",
+                     ' href="https://login.persona.org/tos" target="_new"',
+                     ' href="https://login.persona.org/privacy" target="_new"',
+                   ]) %>
+      </p>
+
   </div>
diff --git a/resources/static/dialog/views/confirm_email.ejs b/resources/static/dialog/views/confirm_email.ejs
index 01ab935ed3d7b5206b7ae244bf4eec9cda20fa44..380b051ef47b8db3368807163de78aaa99174a3f 100644
--- a/resources/static/dialog/views/confirm_email.ejs
+++ b/resources/static/dialog/views/confirm_email.ejs
@@ -2,22 +2,13 @@
       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/. */ %>
 
-    <h2><%= gettext('Check your email!') %></h2>
+    <h2><%= gettext('Confirm your email address') %></h2>
 
     <p>
-      <%= format(gettext('We sent a confirmation email to <strong>%s</strong>'), [email]) %>
+      <%= format(gettext('Check your email at %s.'), ["<strong>" + email + "</strong>"]) %>
     </p>
 
     <p>
-      <%= gettext('To finish signing in just click the verify link we sent to your email address.') %>
-    </p>
-    <br />
-
-    <p>
-      <% if(required) { %>
-        <%= format(gettext('If this is a mistake, just ignore the sent email and <a %s>cancel</a>.'), [' href="#" id="back"']) %>
-      <% } else { %>
-        <%= format(gettext('If this is a mistake, just ignore the sent email and <a %s>use another email address</a>.'), [' href="#" id="back"']) %>
-      <% } %>
+      <%= format(gettext('Click the link in the confirmation email.  You\'ll then immediately be signed in to %s.'), ["<strong>" + siteName + "</strong>"]) %>
     </p>
 
diff --git a/resources/static/dialog/views/is_this_your_computer.ejs b/resources/static/dialog/views/is_this_your_computer.ejs
index 52fa24c45eb1889e1c4376c4aab387d5eb49b02d..c359518caf82feb178f294aa205378ec86c52d78 100644
--- a/resources/static/dialog/views/is_this_your_computer.ejs
+++ b/resources/static/dialog/views/is_this_your_computer.ejs
@@ -3,19 +3,20 @@
       file, You can obtain one at http://mozilla.org/MPL/2.0/. */ %>
 
   <div id="your_computer_content">
-    <h2><%= gettext('If you don\'t mind me asking, is this your computer?') %></h2>
-
+    <h2><%= gettext('Remember You?') %></h2>
+	<p><%= gettext('How long would you like to remain signed in with Persona?') %></p>
     <ul>
       <li>
-        <button id="this_is_my_computer" tabindex="3"><%= gettext('yes') %></button>
-        <%= gettext('If so, we\'ll keep you logged in for two weeks.') %>
+        <button id="this_is_my_computer" tabindex="3"><%= gettext('One month') %></button>
+        <%= gettext('I trust this computer.') %>
       </li>
 
       <li>
-        <button id="this_is_not_my_computer" class="negative" tabindex="3"><%= gettext('no') %></button>
-        <%= gettext('If you\'re at a public computer such as a library or internet cafe, choose this.  We\'ll prompt you for your password in an hour for greater security.') %>
+        <button id="this_is_not_my_computer" class="negative" tabindex="3"><%= gettext('This session only') %></button>
+        <%= gettext('This is not my computer.') %>
       </li>
     </ul>
+    <p><%= gettext('Note: You can of course sign out of Persona at any time.') %></p>
   </div>
 
 
diff --git a/resources/static/dialog/views/pick_email.ejs b/resources/static/dialog/views/pick_email.ejs
index c189f521055d156ee397366df6d6306a22645f98..244d801fa400c6b33ffceba5c354a34e595a66d9 100644
--- a/resources/static/dialog/views/pick_email.ejs
+++ b/resources/static/dialog/views/pick_email.ejs
@@ -3,38 +3,30 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 
-  <strong><%= gettext('Sign in using') %></strong>
+
+  <p>
+    <%= gettext('Sign in as...') %>
+  </p>
 
   <div id="selectEmail" class="form_section">
       <ul class="inputs">
           <% _.each(identities, function(item, index) { var emailAddress = item.address, id = "email_" + index; %>
               <li>
-                  <label for="<%= id %>" class="serif<% if (emailAddress === siteemail) { %> preselected<% } %> selectable" title="<%= emailAddress %>" >
+                  <label for="<%= id %>" class="<% if (emailAddress === siteEmail) { %> preselected<% } %> selectable" title="<%= emailAddress %>" >
                     <input type="radio" name="email" id="<%= id %>" value="<%= emailAddress %>"
-                        <% if (emailAddress === siteemail) { %> checked="checked" <% } %>
+                        <% if (emailAddress === siteEmail) { %> checked="checked" <% } %>
                       />
                     <%= emailAddress %>
                   </label>
               </li>
           <% }); %>
       </ul>
-      <a id="useNewEmail" class="emphasize" href="#"><%= gettext('Add another email') %></a>
-      <a id="thisIsNotMe" class="emphasize" href="#"><%= gettext('This is not me...') %></a>
+
+      <a id="useNewEmail" class="emphasize" href="#"><%= gettext('Add another email address') %></a>
+      <a id="thisIsNotMe" class="emphasize" href="#"><%= gettext('This is not me') %></a>
 
 
-      <div class="submit add cf">
-      <p class="cf">
+      <p class="submit add cf">
         <button id="signInButton"><%= gettext('sign in') %></button>
       </p>
-      <% if (privacy_url && tos_url) { %>
-        <p class="tospp">
-          <%= format(
-            gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                    [ gettext('sign in'),
-                      format(' href="%s" target="_new"', [tos_url]),
-                      format(' href="%s" target="_new"', [privacy_url])
-                    ]) %>
-        </p>
-      <% } %>
-      </div>
   </div>
diff --git a/resources/static/dialog/views/required_email.ejs b/resources/static/dialog/views/required_email.ejs
index a1e0fdace2304f322d97fcac29b3cf65a664c10e..973048b7b76c981361cecc74d161dfbc7897ae14 100644
--- a/resources/static/dialog/views/required_email.ejs
+++ b/resources/static/dialog/views/required_email.ejs
@@ -41,7 +41,7 @@
                   </div>
 
                   <div id="cannot_authenticate" class="tooltip" for="password">
-                    <%= gettext("The account cannot be logged in with this username and password.") %>
+                    <%= gettext('This email address and password do not match.') %>
                   </div>
 
               </li>
@@ -50,17 +50,7 @@
       </ul>
 
       <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('sign in'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
+          <p class="cf">
             <% if (signin) { %>
               <button id="sign_in" tabindex="3"><%= gettext("sign in") %></button>
             <% } else if (verify) { %>
@@ -70,8 +60,16 @@
             <% if (secondary_auth) { %>
               <a href="#" id="cancel" class="action" tabindex="4"><%= gettext("cancel") %></a>
             <% } %>
-        <% if (privacy_url && tos_url) { %>
           </p>
-        <% } %>
+          <% if (personaTOSPP) { %>
+            <p class="tospp">
+               <%= format(
+                    gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                         [ "Persona",
+                           ' href="https://login.persona.org/tos" target="_new"',
+                           ' href="https://login.persona.org/privacy" target="_new"',
+                         ]) %>
+            </p>
+          <% } %>
       </div>
   </div>
diff --git a/resources/static/dialog/views/rp_info.ejs b/resources/static/dialog/views/rp_info.ejs
index 5928a18d6c945d726598017558fbb69f1f4d16e1..724a3e1925dfbf9a1c5d58d6e8bfc67278d31c44 100644
--- a/resources/static/dialog/views/rp_info.ejs
+++ b/resources/static/dialog/views/rp_info.ejs
@@ -11,11 +11,18 @@
   <h2 id="rp_name"><%= siteName %></h2>
 <% } %>
 
-<% if(hostname) { %>
-  <% if(siteName) { %>
-    <h3 id="rp_hostname"><%= hostname %></h3>
-  <% } else { %>
-    <h2 id="rp_hostname"><%= hostname %></h2>
-  <% } %>
+<% if(siteName) { %>
+  <h3 id="rp_hostname"><%= hostname %></h3>
+<% } else { %>
+  <h2 id="rp_hostname"><%= hostname %></h2>
 <% } %>
 
+<% if(privacyPolicy && termsOfService) { %>
+  <p id="rptospp" class="tospp">
+    <%= format(gettext("By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>."),
+    [ siteName || hostname,
+     ' href="' + termsOfService + '" id="rp_tos" target="_blank"',
+     ' href="' + privacyPolicy + '" id="rp_pp" target="_blank"'
+     ]) %>
+   </p>
+<% } %>
diff --git a/resources/static/dialog/views/set_password.ejs b/resources/static/dialog/views/set_password.ejs
index d244293925fc8e8f299af68f59774d156bc32e87..87bc118de6dc64a0b61be3a84e7295bdb2b0631c 100644
--- a/resources/static/dialog/views/set_password.ejs
+++ b/resources/static/dialog/views/set_password.ejs
@@ -2,22 +2,27 @@
    - 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/. -->
 
-  <strong><%= gettext('Welcome to Persona!') %></strong>
-
   <div class="form_section" id="set_password">
+      <% if (password_reset) { %>
+        <strong><%= gettext("Reset your password") %></strong>
+      <% } %>
+
       <ul class="inputs">
           <li>
-            <% if(password_reset || !cancelable) { %>
-              <%= gettext("Choose a new password you'll use when you sign in with Persona.") %>
-            <% } else { %>
-              <%= gettext("Next, choose a new password you'll use when you sign in with Persona.") %>
+            <% if (!password_reset) { %>
+              <%= gettext("Your email address is new to us. Please create a password to use with Persona.") %>
             <% } %>
           </li>
 
 
           <li>
-              <label for="password"><%= gettext('Password') %></label>
-              <input id="password" type="password" maxlength="80" />
+              <% if (password_reset) { %>
+                <label for="password"><%= format(gettext("Create a new password to use with %s."), ["Persona"]) %></label>
+              <% } else { %>
+                <label for="password"><%= format(gettext("%s"), ["<strong>" + email + "</strong>"]) %></label>
+              <% } %>
+
+              <input id="password" type="password" maxlength="80" placeholder="<%= gettext("create password") %>"/>
 
               <div class="tooltip" id="password_required" for="password">
                 <%= gettext('Password is required.') %>
@@ -33,26 +38,44 @@
           </li>
 
           <li>
-              <label for="vpassword"><%= gettext('Verify Password') %></label>
-              <input id="vpassword" placeholder="<%= gettext('Repeat Password') %>" type="password" maxlength=80 />
+              <label for="vpassword" class="hidden"><%= gettext('Verify Password') %></label>
+              <input id="vpassword" placeholder="<%= gettext('verify password') %>" type="password" maxlength="80" />
 
               <div class="tooltip" id="vpassword_required" for="vpassword">
                 <%= gettext('Verification password is required.') %>
               </div>
 
               <div class="tooltip" id="passwords_no_match" for="vpassword">
-                <%= gettext('Passwords do not match.') %>
+                <%= gettext('These passwords don\'t match!') %>
               </div>
           </li>
 
       </ul>
 
-      <div class="submit cf">
+      <p class="submit cf buttonrow">
           <button id="<%= password_reset ? "password_reset" : "verify_user" %>">
-            <%= password_reset ? gettext('reset password') : gettext('verify email') %>
+            <%= (password_reset ? gettext('reset password') : gettext('done')) %>
           </button>
+
           <% if(cancelable) { %>
-            <a id="cancel" class="action" href="#"><%= gettext('cancel') %></a>
+            <a id="cancel" class="right" href="#"><%= gettext('cancel') %></a>
           <% } %>
-      </div>
+      </p>
+
+      <% if (password_reset) { %>
+        <p class="submit">
+          <%= gettext("Note: This password will be used with all of your Persona email addresses.") %>
+        </p>
+      <% } %>
+
+      <% if (personaTOSPP) { %>
+        <p id="persona_tospp" class="submit tospp">
+           <%= format(
+              gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                   [ "Persona",
+                     ' href="https://login.persona.org/tos" target="_new"',
+                     ' href="https://login.persona.org/privacy" target="_new"',
+                   ]) %>
+        </p>
+      <% } %>
   </div>
diff --git a/resources/static/dialog/views/verify_primary_user.ejs b/resources/static/dialog/views/verify_primary_user.ejs
index 0a3de6851ed2412d1e3fdefbddf12548b777bb66..d186d200fba22a1067e2f469d5cd712ed409c848 100644
--- a/resources/static/dialog/views/verify_primary_user.ejs
+++ b/resources/static/dialog/views/verify_primary_user.ejs
@@ -2,74 +2,31 @@
    - 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/. -->
 
-<% if (requiredEmail) { %>
-  <strong><%= gettext('The site requested you sign in using') %></strong>
-  <div class="form_section">
-      <ul class="inputs">
-
-          <li>
-              <label for="email"><%= gettext('Email') %></label>
-              <input id="required_email" type="email" value="<%= email %>" disabled />
-              <div id="could_not_add" class="tooltip" for="required_email">
-                <%= gettext("We just sent an email to that address! If you really want to send another, wait a minute or two and try again.") %>
-              </div>
-          </li>
-
-          <li>
-              <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %>
-              <p>
-                  <strong><%= auth_url %></strong>.
-              </p>
-          </li>
-      </ul>
-
-      <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('verify'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
-          <button id="verifyWithPrimary"><%= gettext("verify") %></button>
-        <% if (privacy_url && tos_url) { %>
-          </p>
-        <% } %>
-      </div>
-  </div>
-
-<% } else { %>
-  <strong><%= gettext("Verify With Email Provider") %></strong>
-
-  <div class="cf form_section">
-    <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %>
-    <p>
-      <strong><%= auth_url %></strong>.
+<div class="cf form_section">
+	<h3>
+    <%= format(gettext("%s makes this easy"), [idpName]) %>
+  </h3>
+
+  <p>
+    <%= format(gettext("Persona lets you use your %s account to sign into sites like %s."), [idpName, siteName]) %>
+  </p>
+  <p>
+    <%= format(gettext("Once you verify your account there, you will be signed in to %s."), [siteName]) %>
+  </p>
+    <p class="submit cf buttonrow">
+        <button id="verifyWithPrimary"><%= format(gettext("sign in with %s"), [idpName]) %></button>
+        <a href="#" id="cancel" class="emphasize right"><%= gettext("Use a different email address") %></a>
     </p>
 
-    <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('verify'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
-          <button id="verifyWithPrimary"><%= gettext("verify") %></button>
-          <a href="#" id="cancel" class="action"><%= gettext("cancel") %></a>
-        <% if (privacy_url && tos_url) { %>
-          </p>
-        <% } %>
-    </div>
-
-  </div>
-<% } %>
-
+    <% if (personaTOSPP) { %>
+      <p id="persona_tospp" class="submit tospp">
+         <%= format(
+            gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                 [ "Persona",
+                   ' href="https://login.persona.org/tos" target="_new"',
+                   ' href="https://login.persona.org/privacy" target="_new"',
+                 ]) %>
+      </p>
+    <% } %>
+
+</div>
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/include_js/include.js b/resources/static/include_js/include.js
index f81e73d4356726eac2b5ebb3ba0334d0cb029f04..3516f7af707e0fc15aa87f1f4140ec8b68b34539 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -1027,18 +1027,42 @@
 
       _open_hidden_iframe();
 
+      // back compat support for loggedInEmail
+      if (typeof options.loggedInEmail !== 'undefined' &&
+          typeof options.loggedInUser !== 'undefined') {
+        throw "you cannot supply *both* loggedInEmail and loggedInUser";
+      }
+      else if(typeof options.loggedInEmail !== 'undefined') {
+        try {
+          console.log("loggedInEmail has been deprecated");
+        } catch(e) {
+          /* ignore error */
+        }
+
+        options.loggedInUser = options.loggedInEmail;
+        delete options.loggedInEmail;
+      }
+
       // check that the commChan was properly initialized before interacting with it.
       // on unsupported browsers commChan might still be undefined, in which case
       // we let the dialog display the "unsupported browser" message upon spawning.
-      if (typeof options.loggedInEmail !== 'undefined' && commChan) {
+      if (typeof options.loggedInUser !== 'undefined' && commChan) {
         commChan.notify({
           method: 'loggedInUser',
-          params: options.loggedInEmail
+          params: options.loggedInUser
         });
       }
     }
 
     function internalRequest(options) {
+      if (options.requiredEmail) {
+        try {
+          console.log("requiredEmail has been deprecated");
+        } catch(e) {
+          /* ignore error */
+        }
+      }
+
       // focus an existing window
       if (w) {
         try {
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..d73c65a9f1fbfe26c3f45fbffef015accc062cf3 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;
 
@@ -447,10 +447,6 @@ button.delete:active {
   padding: 0;
 }
 
-#signUpForm .submit {
-  height: 28px;
-}
-
 #signUpForm > .siteinfo {
   margin-bottom: 10px;
 }
@@ -459,10 +455,18 @@ button.delete:active {
   display: none;
 }
 
+.submit > p {
+  line-height: 28px;
+}
+
 .submit .remember {
   float: left;
 }
 
+.tospp {
+  font-size: 13px;
+}
+
 .enter_password .password_entry, .enter_verify_password #verify_password, .known_secondary .password_entry,
 .unknown_secondary #unknown_secondary, .verify_primary #verify_primary {
   display: block;
@@ -525,7 +529,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 98%
rename from resources/static/test/cases/shared/browser-support.js
rename to resources/static/test/cases/common/js/browser-support.js
index 5acce790f6855f07596d3b3ced3aaf31e9f89497..53c55c97151c4c4100c1953816e659e62fadefca 100644
--- a/resources/static/test/cases/shared/browser-support.js
+++ b/resources/static/test/cases/common/js/browser-support.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/class.js b/resources/static/test/cases/common/js/class.js
similarity index 97%
rename from resources/static/test/cases/shared/class.js
rename to resources/static/test/cases/common/js/class.js
index bf9306ac693ccdcca2be44b528dfec6f9d19997c..1180ae6ca049e2530d9bb875ef969c9ccd87ebce 100644
--- a/resources/static/test/cases/shared/class.js
+++ b/resources/static/test/cases/common/js/class.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/command.js b/resources/static/test/cases/common/js/command.js
similarity index 95%
rename from resources/static/test/cases/shared/command.js
rename to resources/static/test/cases/common/js/command.js
index 0fd003bb0f9f79143a4e813b1b5ae4b1004f6859..ebe06d3ca909a3a8002fa9646677e019c319d2a8 100644
--- a/resources/static/test/cases/shared/command.js
+++ b/resources/static/test/cases/common/js/command.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
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 92%
rename from resources/static/test/cases/shared/enable_cookies_url.js
rename to resources/static/test/cases/common/js/enable_cookies_url.js
index fb48744f1b53ec23a9a084d8949949c5653318c7..d6256521dfd87becded3fd77eb49c1e98221a7ad 100644
--- a/resources/static/test/cases/shared/enable_cookies_url.js
+++ b/resources/static/test/cases/common/js/enable_cookies_url.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/error-display.js b/resources/static/test/cases/common/js/error-display.js
similarity index 93%
rename from resources/static/test/cases/shared/error-display.js
rename to resources/static/test/cases/common/js/error-display.js
index a3903093820de3fa84ba236b6a47d1dc31130607..02e600c6bc7812e8c221fe0b65a90b2b9e71355e 100644
--- a/resources/static/test/cases/shared/error-display.js
+++ b/resources/static/test/cases/common/js/error-display.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
diff --git a/resources/static/test/cases/shared/helpers.js b/resources/static/test/cases/common/js/helpers.js
similarity index 99%
rename from resources/static/test/cases/shared/helpers.js
rename to resources/static/test/cases/common/js/helpers.js
index 263cad7e298134de8a481f9c94c3e34102a56d60..8d9c97ebd3e2302334c1dac627539d7088bb3d16 100644
--- a/resources/static/test/cases/shared/helpers.js
+++ b/resources/static/test/cases/common/js/helpers.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/history.js b/resources/static/test/cases/common/js/history.js
similarity index 96%
rename from resources/static/test/cases/shared/history.js
rename to resources/static/test/cases/common/js/history.js
index 7d29e4d33a94a6eb9ee5293637b393cee725c332..394ccc23acbfe7b527435cc262e791070c8cecbe 100644
--- a/resources/static/test/cases/shared/history.js
+++ b/resources/static/test/cases/common/js/history.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
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 98%
rename from resources/static/test/cases/shared/models/interaction_data.js
rename to resources/static/test/cases/common/js/models/interaction_data.js
index 8f229c141f738196db302ba9e43c11edf46f30bf..b09f8a2ad008612109786aaa3aafb76cfe8fed63 100644
--- a/resources/static/test/cases/shared/models/interaction_data.js
+++ b/resources/static/test/cases/common/js/models/interaction_data.js
@@ -1,5 +1,5 @@
 
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 96%
rename from resources/static/test/cases/shared/modules/cookie_check.js
rename to resources/static/test/cases/common/js/modules/cookie_check.js
index b6358ba50fe1571fec66ee894aa40d8cacd4a6d3..593f8a72b41c96f0adef1419d267387cfeeaebb2 100644
--- a/resources/static/test/cases/shared/modules/cookie_check.js
+++ b/resources/static/test/cases/common/js/modules/cookie_check.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 99%
rename from resources/static/test/cases/shared/modules/interaction_data.js
rename to resources/static/test/cases/common/js/modules/interaction_data.js
index 434631b9b747fe58b7cd8b979325edef22ee1112..1beb33008293d297a40f88cabcd519e042959ef8 100644
--- a/resources/static/test/cases/shared/modules/interaction_data.js
+++ b/resources/static/test/cases/common/js/modules/interaction_data.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 99%
rename from resources/static/test/cases/shared/modules/page_module.js
rename to resources/static/test/cases/common/js/modules/page_module.js
index ebd90fdba6b1f573a564ed409aca4196a781926a..da03cc638b5142d7ef25d6bec2b95f9e4866b5b4 100644
--- a/resources/static/test/cases/shared/modules/page_module.js
+++ b/resources/static/test/cases/common/js/modules/page_module.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 96%
rename from resources/static/test/cases/shared/modules/xhr_delay.js
rename to resources/static/test/cases/common/js/modules/xhr_delay.js
index 29585e1dbc3501a6a1903cd25ace5bac62f6982e..81e7c06d7b6ad3412c489a14f8b00cd88266248a 100644
--- a/resources/static/test/cases/shared/modules/xhr_delay.js
+++ b/resources/static/test/cases/common/js/modules/xhr_delay.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 95%
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
index 09552724e1fa086e95fef4378529d235b4209d7a..b3152c6eb98da4efa203cddfd2b1efcd2571dcc1 100644
--- a/resources/static/test/cases/shared/modules/xhr_disable_form.js
+++ b/resources/static/test/cases/common/js/modules/xhr_disable_form.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/shared/network.js b/resources/static/test/cases/common/js/network.js
similarity index 99%
rename from resources/static/test/cases/shared/network.js
rename to resources/static/test/cases/common/js/network.js
index 4ce7680923dd3d0f03d8c57f771d69d461ae7e53..5fac936df6c34fa014b5c2e2cf59d2b36de319d9 100644
--- a/resources/static/test/cases/shared/network.js
+++ b/resources/static/test/cases/common/js/network.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global asyncTest: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/renderer.js b/resources/static/test/cases/common/js/renderer.js
similarity index 94%
rename from resources/static/test/cases/shared/renderer.js
rename to resources/static/test/cases/common/js/renderer.js
index ab6f605d9a8a00ce841d8bb9ea618bef7ff18b3c..f3895f8665466b124c85ccc0680c2abd6d49c3d5 100644
--- a/resources/static/test/cases/shared/renderer.js
+++ b/resources/static/test/cases/common/js/renderer.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
diff --git a/resources/static/test/cases/shared/screens.js b/resources/static/test/cases/common/js/screens.js
similarity index 97%
rename from resources/static/test/cases/shared/screens.js
rename to resources/static/test/cases/common/js/screens.js
index 98f75978889c33ddbae761a25fb628dfa71c67b9..c989d5cfb43c6df7f1220f74f2e6962eac618f46 100644
--- a/resources/static/test/cases/shared/screens.js
+++ b/resources/static/test/cases/common/js/screens.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/state_machine.js b/resources/static/test/cases/common/js/state_machine.js
similarity index 98%
rename from resources/static/test/cases/shared/state_machine.js
rename to resources/static/test/cases/common/js/state_machine.js
index 93f5eb58fe26fba3d811ae1f490c9299b0dee7f6..60b5982ccb4e8f76f363914e25c18400b9747d0c 100644
--- a/resources/static/test/cases/shared/state_machine.js
+++ b/resources/static/test/cases/common/js/state_machine.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
diff --git a/resources/static/test/cases/shared/storage.js b/resources/static/test/cases/common/js/storage.js
similarity index 99%
rename from resources/static/test/cases/shared/storage.js
rename to resources/static/test/cases/common/js/storage.js
index 2b7fd3d0789b6c3d171d816a3a6500c0efd7c50a..a347507f8e6bc74677a978db6af86c2c878fbace 100644
--- a/resources/static/test/cases/shared/storage.js
+++ b/resources/static/test/cases/common/js/storage.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/shared/tooltip.js b/resources/static/test/cases/common/js/tooltip.js
similarity index 96%
rename from resources/static/test/cases/shared/tooltip.js
rename to resources/static/test/cases/common/js/tooltip.js
index 16605f018947b1d5d1c80d5eba7176bab9ab24c8..b078911203a99b71d29d57439647c2c9ff8fb2ab 100644
--- a/resources/static/test/cases/shared/tooltip.js
+++ b/resources/static/test/cases/common/js/tooltip.js
@@ -1,4 +1,4 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*globals BrowserID: true, _: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
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 26c47010104631cc42cdd9ac2c77caff04b6c9e5..8713e750ffe2d67d86d74d2de0362bb43db82a2d 100644
--- a/resources/static/test/cases/shared/user.js
+++ b/resources/static/test/cases/common/js/user.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, strictEqual: true, BrowserID: 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
@@ -92,6 +92,22 @@ var jwcrypto = require("./lib/jwcrypto");
     equal(lib.getReturnTo(), returnTo, "get/setReturnTo work as expected");
   });
 
+  test("setOriginEmail/getOriginEmail", function() {
+    storage.addEmail("testuser@testuser.com", { type: "primary" });
+    storage.addEmail("testuser2@testuser.com", { type: "primary" });
+
+    lib.setOrigin("http://testdomain.org");
+
+    lib.setOriginEmail("testuser@testuser.com");
+    equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email");
+
+    lib.setOrigin("http://othertestdomain.org");
+    lib.setOriginEmail("testuser2@testuser.com");
+
+    lib.setOrigin("http://testdomain.org");
+    equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email");
+  });
+
   test("getStoredEmailKeypairs without key - return all identities", function() {
     var identities = lib.getStoredEmailKeypairs();
     equal("object", typeof identities, "object returned");
@@ -159,15 +175,14 @@ var jwcrypto = require("./lib/jwcrypto");
 
   asyncTest("createPrimaryUser with primary, user verified with primary - expect 'primary.verified'", function() {
     xhr.useResult("primary");
-    provisioning.setStatus(provisioning.AUTHENTICATED, function() {
-      lib.createPrimaryUser({email: "unregistered@testuser.com"}, function(status) {
-        equal(status, "primary.verified", "primary user is already verified, correct status");
-        network.checkAuth(function(authenticated) {
-          equal(authenticated, "assertion", "after provisioning user, user should be automatically authenticated to Persona");
-          start();
-        });
-      }, testHelpers.unexpectedXHRFailure);
-    });
+    provisioning.setStatus(provisioning.AUTHENTICATED);
+    lib.createPrimaryUser({email: "unregistered@testuser.com"}, function(status) {
+      equal(status, "primary.verified", "primary user is already verified, correct status");
+      network.checkAuth(function(authenticated) {
+        equal(authenticated, "assertion", "after provisioning user, user should be automatically authenticated to Persona");
+        start();
+      });
+    }, testHelpers.unexpectedXHRFailure);
   });
 
   asyncTest("createPrimaryUser with primary, user must authenticate with primary - expect 'primary.verify'", function() {
@@ -1136,6 +1151,7 @@ var jwcrypto = require("./lib/jwcrypto");
         equal(info.type, "primary", "correct type");
         equal(info.email, "registered@testuser.com", "correct email");
         equal(info.authed, true, "user is authenticated with IdP");
+        equal(info.idpName, "testuser.com", "unknown IdP, use email host portion for name");
         start();
       },
       testHelpers.unexpectedFailure
diff --git a/resources/static/test/cases/shared/validation.js b/resources/static/test/cases/common/js/validation.js
similarity index 99%
rename from resources/static/test/cases/shared/validation.js
rename to resources/static/test/cases/common/js/validation.js
index 4308cd83d6f320bee6ce5d4fc31a5fb6ff06a735..2ca280e8df228b9dec34dd96c91fdc40de5bf3e9 100644
--- a/resources/static/test/cases/shared/validation.js
+++ b/resources/static/test/cases/common/js/validation.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/shared/xhr.js b/resources/static/test/cases/common/js/xhr.js
similarity index 98%
rename from resources/static/test/cases/shared/xhr.js
rename to resources/static/test/cases/common/js/xhr.js
index 2edd1f7dd3a2de1d8186f2e8037e77f8070adde9..b1a06464dfd01bf1ada65238323e5407f9153a92 100644
--- a/resources/static/test/cases/shared/xhr.js
+++ b/resources/static/test/cases/common/js/xhr.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global asyncTest: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
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 99%
rename from resources/static/test/cases/resources/helpers.js
rename to resources/static/test/cases/dialog/js/misc/helpers.js
index 7bd85e9961a480f12562fdf27784707f64259d9f..edc1fa98a8a5417630559085b2218cc2ee9a66fc 100644
--- a/resources/static/test/cases/resources/helpers.js
+++ b/resources/static/test/cases/dialog/js/misc/helpers.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/resources/internal_api.js b/resources/static/test/cases/dialog/js/misc/internal_api.js
similarity index 99%
rename from resources/static/test/cases/resources/internal_api.js
rename to resources/static/test/cases/dialog/js/misc/internal_api.js
index 5e3ebfe4c4a561e22b3a110c65ec4b57ad9f812b..f8a1de54a084e0879acca63400417ab5196a2c95 100644
--- a/resources/static/test/cases/resources/internal_api.js
+++ b/resources/static/test/cases/dialog/js/misc/internal_api.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/resources/state.js b/resources/static/test/cases/dialog/js/misc/state.js
similarity index 90%
rename from resources/static/test/cases/resources/state.js
rename to resources/static/test/cases/dialog/js/misc/state.js
index 8d6850d399428fb18c5348332367ffc39a9db33d..d6535638fd817d6385d7950077102dfbffd5fb72 100644
--- a/resources/static/test/cases/resources/state.js
+++ b/resources/static/test/cases/dialog/js/misc/state.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -34,6 +34,14 @@
     }
   }
 
+  function testActionStarted(actionName, requiredOptions) {
+    ok(actions.called[actionName], actionName + "called");
+    for(var key in requiredOptions) {
+      equal(actions.info[actionName][key], requiredOptions[key],
+          actionName + " called with " + key + "=" + requiredOptions[key]);
+    }
+  }
+
   function createMachine() {
     machine = bid.State.create();
     actions = new ActionsMock();
@@ -127,6 +135,16 @@
     equal(actions.info.doResetPassword.email, TEST_EMAIL, "correct email sent to doResetPassword");
   });
 
+  test("start - RPInfo always started", function() {
+    mediator.publish("start", {
+      termsOfService: "https://browserid.org/TOS.html",
+      privacyPolicy: "https://browserid.org/priv.html"
+    });
+
+    ok(actions.info.doRPInfo.termsOfService, "doRPInfo called with termsOfService set");
+    ok(actions.info.doRPInfo.privacyPolicy, "doRPInfo called with privacyPolicy set");
+  });
+
   test("user_staged - call doConfirmUser", function() {
     mediator.publish("user_staged", { email: TEST_EMAIL });
 
@@ -254,13 +272,13 @@
     mediator.publish("authenticated", { email: TEST_EMAIL });
   });
 
-  test("forgot_password", function() {
+  test("forgot_password - call doForgotPassword with correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
     mediator.publish("forgot_password", {
       email: TEST_EMAIL,
       requiredEmail: true
     });
-    equal(actions.info.doForgotPassword.email, TEST_EMAIL, "correct email passed");
-    equal(actions.info.doForgotPassword.requiredEmail, true, "correct requiredEmail passed");
+    testActionStarted("doForgotPassword", { email: TEST_EMAIL, requiredEmail: true });
   });
 
   test("password_reset to user_confirmed - call doUserStaged then doEmailConfirmed", function() {
@@ -379,12 +397,11 @@
     ok(actions.called.doNotMe, "doNotMe has been called");
   });
 
-  test("authenticate", function() {
-    mediator.publish("authenticate", {
-      email: TEST_EMAIL
-    });
+  test("authenticate - call doAuthenticate with the correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
+    mediator.publish("authenticate", { email: TEST_EMAIL });
 
-    equal(actions.info.doAuthenticate.email, TEST_EMAIL, "authenticate with testuser@testuser.com");
+    testActionStarted("doAuthenticate", { email: TEST_EMAIL, siteTOSPP: true });
   });
 
   test("start with no special parameters - go straight to checking auth", function() {
@@ -410,16 +427,23 @@
   });
 
 
+  test("add_email - call doAddEmail with correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
+    mediator.publish("add_email");
+    testActionStarted("doAddEmail");
+  });
+
   asyncTest("email_chosen with secondary email, user must authenticate - call doAuthenticateWithRequiredEmail", function() {
     var email = TEST_EMAIL;
     storage.addEmail(email, { type: "secondary" });
 
     xhr.setContextInfo("auth_level", "assertion");
 
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
     mediator.publish("email_chosen", {
       email: email,
       complete: function() {
-        equal(actions.called.doAuthenticateWithRequiredEmail, true, "doAuthenticateWithRequiredEmail called");
+        testActionStarted("doAuthenticateWithRequiredEmail", { siteTOSPP: false });
         start();
       }
     });
@@ -469,15 +493,14 @@
   test("null assertion generated - preserve original options in doPickEmail", function() {
     mediator.publish("start", {
       hostname: "http://example.com",
-      privacyURL: "http://example.com/priv.html",
-      tosURL: "http://example.com/tos.html"
+      privacyPolicy: "http://example.com/priv.html",
+      termsOfService: "http://example.com/tos.html"
     });
     mediator.publish("assertion_generated", { assertion: null });
 
     equal(actions.called.doPickEmail, true, "doPickEmail callled");
     equal(actions.info.doPickEmail.origin, "http://example.com", "hostname preserved");
-    equal(actions.info.doPickEmail.privacyURL, "http://example.com/priv.html", "privacyURL preserved");
-    equal(actions.info.doPickEmail.tosURL, "http://example.com/tos.html", "tosURL preserved");
+    equal(actions.info.doPickEmail.siteTOSPP, true, "siteTOSPP preserved");
   });
 
   test("add_email - call doAddEmail", function() {
diff --git a/resources/static/test/cases/controllers/actions.js b/resources/static/test/cases/dialog/js/modules/actions.js
similarity index 83%
rename from resources/static/test/cases/controllers/actions.js
rename to resources/static/test/cases/dialog/js/modules/actions.js
index 8d90d69f88dfd2bcd2b72865732ff267e1593e3a..3f98fc81b8d968879c1c34738a00b4eab5500111 100644
--- a/resources/static/test/cases/controllers/actions.js
+++ b/resources/static/test/cases/dialog/js/modules/actions.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -75,12 +75,12 @@
   });
 
   asyncTest("doConfirmUser - start the check_registration service", function() {
-    testActionStartsModule("doConfirmUser", {email: TEST_EMAIL},
+    testActionStartsModule("doConfirmUser", {email: TEST_EMAIL, siteName: "Unit Test Site"},
       "check_registration");
   });
 
   asyncTest("doConfirmEmail - start the check_registration service", function() {
-    testActionStartsModule("doConfirmEmail", {email: TEST_EMAIL},
+    testActionStartsModule("doConfirmEmail", {email: TEST_EMAIL, siteName: "Unit Test Site"},
       "check_registration");
   });
 
@@ -88,7 +88,6 @@
     testActionStartsModule('doGenerateAssertion', { email: TEST_EMAIL }, "generate_assertion");
   });
 
-
   asyncTest("doStageUser with successful creation - trigger user_staged", function() {
     createController({
       ready: function() {
@@ -109,5 +108,21 @@
   asyncTest("doForgotPassword - call the set_password controller with reset_password true", function() {
     testActionStartsModule('doForgotPassword', { email: TEST_EMAIL }, "set_password");
   });
+
+  asyncTest("doRPInfo - start the rp_info service", function() {
+    createController({
+      ready: function() {
+        var error;
+        try {
+          controller.doRPInfo({ name: "browserid.org" });
+        } catch(e) {
+          error = e;
+        }
+
+        equal(error, "module not registered for rp_info", "correct service started");
+        start();
+      }
+    });
+  });
 }());
 
diff --git a/resources/static/test/cases/controllers/add_email.js b/resources/static/test/cases/dialog/js/modules/add_email.js
similarity index 93%
rename from resources/static/test/cases/controllers/add_email.js
rename to resources/static/test/cases/dialog/js/modules/add_email.js
index 6d635e82286fcb606da9e1fe8dd8c99307b40b9e..7e7000ca556cc3953ef6ec898a7c96a37c2dc8df 100644
--- a/resources/static/test/cases/controllers/add_email.js
+++ b/resources/static/test/cases/dialog/js/modules/add_email.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -41,16 +41,6 @@
     controller.start(options || {});
   }
 
-  test("privacyURL and tosURL specified - show TOS/PP", function() {
-    equal($(".tospp").length, 0, "tospp has not yet been added to the DOM");
-    createController({
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html",
-    });
-
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
-  });
-
   test("addEmail with specified email address - fill in email", function() {
     createController({ email: "testuser@testuser.com" });
     ok($("#newEmail").val(), "testuser@testuser.com", "email prepopulated");
diff --git a/resources/static/test/cases/controllers/authenticate.js b/resources/static/test/cases/dialog/js/modules/authenticate.js
similarity index 99%
rename from resources/static/test/cases/controllers/authenticate.js
rename to resources/static/test/cases/dialog/js/modules/authenticate.js
index 6827fce0d83f54e2cb3f554eba3c990c8e055ce1..7952811e9e066da8631b160d9780d0b68453f112 100644
--- a/resources/static/test/cases/controllers/authenticate.js
+++ b/resources/static/test/cases/dialog/js/modules/authenticate.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/controllers/check_registration.js b/resources/static/test/cases/dialog/js/modules/check_registration.js
similarity index 96%
rename from resources/static/test/cases/controllers/check_registration.js
rename to resources/static/test/cases/dialog/js/modules/check_registration.js
index 03a01bf0a23fed05da0e3de64fab2eb43843e9eb..82da23a350875070a5a3b925341c19fa0d5e3f29 100644
--- a/resources/static/test/cases/controllers/check_registration.js
+++ b/resources/static/test/cases/dialog/js/modules/check_registration.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -19,7 +19,8 @@
       email: "registered@testuser.com",
       verifier: verifier,
       verificationMessage: message,
-      required: required
+      required: required,
+      siteName: "Unit Test Site"
     });
   }
 
diff --git a/resources/static/test/cases/controllers/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js
similarity index 76%
rename from resources/static/test/cases/controllers/dialog.js
rename to resources/static/test/cases/dialog/js/modules/dialog.js
index f1ddb37c6f57dfe7afc6d744d0bde0dafb1419b8..87ea235e58aaf7a7b8738569c5bcefd3f477dd09 100644
--- a/resources/static/test/cases/controllers/dialog.js
+++ b/resources/static/test/cases/dialog/js/modules/dialog.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -50,6 +50,8 @@
     },
 
     navigator: {},
+
+    sessionStorage: {}
   };
 
   function createController(config) {
@@ -134,8 +136,12 @@
     });
   });
 
-  asyncTest("initialization with #CREATE_EMAIL=testuser@testuser.com - trigger start with correct params", function() {
-    winMock.location.hash = "#CREATE_EMAIL=testuser@testuser.com";
+  asyncTest("initialization with #AUTH_RETURN and add=false - trigger start with correct params", function() {
+    winMock.location.hash = "#AUTH_RETURN";
+    winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: false,
+      email: TESTEMAIL
+    });
 
     createController({
       ready: function() {
@@ -157,8 +163,12 @@
     });
   });
 
-  asyncTest("initialization with #ADD_EMAIL=testuser@testuser.com - trigger start with correct params", function() {
-    winMock.location.hash = "#ADD_EMAIL=testuser@testuser.com";
+  asyncTest("initialization with #AUTH_RETURN and add=true - trigger start with correct params", function() {
+    winMock.location.hash = "#AUTH_RETURN";
+    winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: true,
+      email: TESTEMAIL
+    });
 
     createController({
       ready: function() {
@@ -198,66 +208,8 @@
     });
   });
 
-  asyncTest("get with invalid requiredEmail - print error screen", function() {
-    createController({
-      ready: function() {
-        mediator.subscribe("start", function(msg, info) {
-          ok(false, "start should not have been called");
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: "bademail"
-        });
-        equal(retval, "invalid requiredEmail: (bademail)", "expected error");
-        testErrorVisible();
-        start();
-      }
-    });
-  });
-
-  asyncTest("get with script containing requiredEmail - print error screen", function() {
-    createController({
-      ready: function() {
-        mediator.subscribe("start", function(msg, info) {
-          ok(false, "start should not have been called");
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: "<script>window.scriptRun=true;</script>testuser@testuser.com"
-        });
-
-        // If requiredEmail is not properly escaped, scriptRun will be true.
-        equal(typeof window.scriptRun, "undefined", "script was not run");
-        equal(retval, "invalid requiredEmail: (<script>window.scriptRun=true;</script>testuser@testuser.com)", "expected error");
-        testErrorVisible();
-        start();
-      }
-    });
-  });
-
-  asyncTest("get with valid requiredEmail - go to start", function() {
-    createController({
-      ready: function() {
-        var startInfo;
-        mediator.subscribe("start", function(msg, info) {
-          startInfo = info;
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: TESTEMAIL
-        });
-
-        testHelpers.testObjectValuesEqual(startInfo, {
-          requiredEmail: TESTEMAIL
-        });
-        equal(typeof retval, "undefined", "no error expected");
-        testErrorNotVisible();
-        start();
-      }
-    });
-  });
 
-  asyncTest("get with relative tosURL & valid privacyURL - print error screen", function() {
+  asyncTest("get with relative termsOfService & valid privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -265,8 +217,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "relative.html",
-          privacyURL: "/privacy.html"
+          termsOfService: "relative.html",
+          privacyPolicy: "/privacy.html"
         });
         equal(retval, "relative urls not allowed: (relative.html)", "expected error");
         testErrorVisible();
@@ -275,7 +227,7 @@
     });
   });
 
-  asyncTest("get with script containing tosURL - print error screen", function() {
+  asyncTest("get with script containing termsOfService - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -283,11 +235,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "relative.html<script>window.scriptRun=true;</script>",
-          privacyURL: "/privacy.html"
+          termsOfService: "relative.html<script>window.scriptRun=true;</script>",
+          privacyPolicy: "/privacy.html"
         });
 
-        // If tosURL is not properly escaped, scriptRun will be true.
+        // If termsOfService is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -296,7 +248,7 @@
     });
   });
 
-  asyncTest("get with valid tosURL & relative privacyURL - print error screen", function() {
+  asyncTest("get with valid termsOfService & relative privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -304,8 +256,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html"
         });
         equal(retval, "relative urls not allowed: (relative.html)", "expected error");
         testErrorVisible();
@@ -314,7 +266,7 @@
     });
   });
 
-  asyncTest("get with script containing privacyURL - print error screen", function() {
+  asyncTest("get with script containing privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -322,11 +274,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html<script>window.scriptRun=true;</script>"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html<script>window.scriptRun=true;</script>"
         });
 
-        // If privacyURL is not properly escaped, scriptRun will be true.
+        // If privacyPolicy is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -335,7 +287,7 @@
     });
   });
 
-  asyncTest("get with privacyURL - print error screen", function() {
+  asyncTest("get with privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -343,11 +295,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html<script>window.scriptRun=true;</script>"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html<script>window.scriptRun=true;</script>"
         });
 
-        // If privacyURL is not properly escaped, scriptRun will be true.
+        // If privacyPolicy is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -356,7 +308,7 @@
     });
   });
 
-  asyncTest("get with javascript protocol for privacyURL - print error screen", function() {
+  asyncTest("get with javascript protocol for privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -364,8 +316,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "javascript:alert(1)"
+          termsOfService: "/tos.html",
+          privacyPolicy: "javascript:alert(1)"
         });
 
         equal(retval, "relative urls not allowed: (javascript:alert(1))", "expected error");
@@ -375,7 +327,7 @@
     });
   });
 
-  asyncTest("get with invalid httpg protocol for privacyURL - print error screen", function() {
+  asyncTest("get with invalid httpg protocol for privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -383,8 +335,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "httpg://testdomain.com/privacy.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "httpg://testdomain.com/privacy.html"
         });
 
         equal(retval, "relative urls not allowed: (httpg://testdomain.com/privacy.html)", "expected error");
@@ -395,7 +347,7 @@
   });
 
 
-  asyncTest("get with valid absolute tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid absolute termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -404,13 +356,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "/privacy.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         equal(typeof retval, "undefined", "no error expected");
@@ -420,7 +372,7 @@
     });
   });
 
-  asyncTest("get with valid fully qualified http tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid fully qualified http termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -429,13 +381,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         equal(typeof retval, "undefined", "no error expected");
@@ -446,7 +398,7 @@
   });
 
 
-  asyncTest("get with valid fully qualified https tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid fully qualified https termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -455,13 +407,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: HTTPS_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTPS_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTPS_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTPS_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html"
         });
         equal(typeof retval, "undefined", "no error expected");
         testErrorNotVisible();
diff --git a/resources/static/test/cases/controllers/forgot_password.js b/resources/static/test/cases/dialog/js/modules/forgot_password.js
similarity index 96%
rename from resources/static/test/cases/controllers/forgot_password.js
rename to resources/static/test/cases/dialog/js/modules/forgot_password.js
index d81d8b1de331c895aab433830b56b639d8744411..4d782af44d9c8f7d004bc09fdfe2a487301455b2 100644
--- a/resources/static/test/cases/controllers/forgot_password.js
+++ b/resources/static/test/cases/dialog/js/modules/forgot_password.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/controllers/generate_assertion.js b/resources/static/test/cases/dialog/js/modules/generate_assertion.js
similarity index 95%
rename from resources/static/test/cases/controllers/generate_assertion.js
rename to resources/static/test/cases/dialog/js/modules/generate_assertion.js
index 436cffe82c87df0ec7a1c26ef8de935ddfdb5d62..04201f05e33a12103334a3275476e3c45f5329c0 100644
--- a/resources/static/test/cases/controllers/generate_assertion.js
+++ b/resources/static/test/cases/dialog/js/modules/generate_assertion.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global asyncTest: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 97%
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
index 9e4945fd0873ab92be0ff0480d6cb757163aa6ff..cc31ae506656633f24b9b961339642b5c7862cb0 100644
--- a/resources/static/test/cases/controllers/is_this_your_computer.js
+++ b/resources/static/test/cases/dialog/js/modules/is_this_your_computer.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/controllers/pick_email.js b/resources/static/test/cases/dialog/js/modules/pick_email.js
similarity index 98%
rename from resources/static/test/cases/controllers/pick_email.js
rename to resources/static/test/cases/dialog/js/modules/pick_email.js
index 555d2f4631c63ae0c9949339feab0e389c180e2c..ff7b28eb56aaf4720d933daecc8a868563313476 100644
--- a/resources/static/test/cases/controllers/pick_email.js
+++ b/resources/static/test/cases/dialog/js/modules/pick_email.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 98%
rename from resources/static/test/cases/controllers/primary_user_provisioned.js
rename to resources/static/test/cases/dialog/js/modules/primary_user_provisioned.js
index fb228c05c1ed1a9a2e25648e2cc88547fc53989c..e17d3e4c5a0791b57677c963f924a42b88cf4b3f 100644
--- a/resources/static/test/cases/controllers/primary_user_provisioned.js
+++ b/resources/static/test/cases/dialog/js/modules/primary_user_provisioned.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
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 95%
rename from resources/static/test/cases/controllers/provision_primary_user.js
rename to resources/static/test/cases/dialog/js/modules/provision_primary_user.js
index 18ecf1133902e243fea8da47bc1901c7935b864a..d5c540849802c80b10796e8efd7677491cb51645 100644
--- a/resources/static/test/cases/controllers/provision_primary_user.js
+++ b/resources/static/test/cases/dialog/js/modules/provision_primary_user.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -10,6 +10,7 @@
       bid = BrowserID,
       storage = bid.Storage,
       user = bid.User,
+      network = bid.Network,
       register = bid.TestHelpers.register,
       xhr = bid.Mocks.xhr,
       mediator = bid.Mediator,
@@ -56,6 +57,7 @@
 
   asyncTest("create controller with all fields specified, user authenticated with primary - expected user provisioned", function() {
     provisioning.setStatus(provisioning.AUTHENTICATED);
+    xhr.useResult("primary");
 
     mediator.subscribe("primary_user_provisioned", function(msg, info) {
       ok(info.assertion, "assertion available");
@@ -72,6 +74,7 @@
 
   asyncTest("create controller with all fields specified, user not authenticated with primary - expected user must authenticate", function() {
     provisioning.setStatus(provisioning.NOT_AUTHENTICATED);
+    xhr.useResult("primary");
 
     mediator.subscribe("primary_user_unauthenticated", function(msg, info) {
       equal(info.auth_url, "https://auth_url", "primary information fetched");
@@ -86,8 +89,8 @@
   });
 
   asyncTest("create controller with missing auth/prov, user authenticated with primary - expected to request provisioning info from backend, user provisioned", function() {
-    xhr.useResult("primary");
     provisioning.setStatus(provisioning.AUTHENTICATED);
+    xhr.useResult("primary");
 
     mediator.subscribe("primary_user_provisioned", function(msg, info) {
       equal(info.email, "unregistered@testuser.com", "user is provisioned after requesting info from backend");
diff --git a/resources/static/test/cases/controllers/required_email.js b/resources/static/test/cases/dialog/js/modules/required_email.js
similarity index 97%
rename from resources/static/test/cases/controllers/required_email.js
rename to resources/static/test/cases/dialog/js/modules/required_email.js
index 53fbed0c1c6c2290a752ec66f4c88cd2de427581..2c42d0c1ed1df24e263d1d68c32d93f0a0bc7384 100644
--- a/resources/static/test/cases/controllers/required_email.js
+++ b/resources/static/test/cases/dialog/js/modules/required_email.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global asyncTest: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -95,17 +95,16 @@
   }
 
 
-  asyncTest("privacyURL and tosURL specified - show TOS/PP", function() {
+  asyncTest("siteTOSPP specified - show TOS/PP", function() {
     var email = "registered@testuser.com";
     xhr.useResult("known_secondary");
+    xhr.setContextInfo("auth_level", "password");
 
-    equal($(".tospp").length, 0, "tospp has not yet been added to the DOM");
     createController({
-      email: "registered@testuser.com",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html",
+      email: email,
+      siteTOSPP: true,
       ready: function() {
-        equal($(".tospp").length, 1, "tospp has been added to the DOM");
+        testHelpers.testRPTosPPShown();
         start();
       }
     });
diff --git a/resources/static/test/cases/controllers/rp_info.js b/resources/static/test/cases/dialog/js/modules/rp_info.js
similarity index 79%
rename from resources/static/test/cases/controllers/rp_info.js
rename to resources/static/test/cases/dialog/js/modules/rp_info.js
index 0b89868ab973653fcec4d665f707b5f77b5580dc..a73fe472c680de4d0ce16af7700835e9901e0f93 100644
--- a/resources/static/test/cases/controllers/rp_info.js
+++ b/resources/static/test/cases/dialog/js/modules/rp_info.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -13,8 +13,11 @@
       register = bid.TestHelpers.register,
       WindowMock = bid.Mocks.WindowMock,
       RP_HOSTNAME = "hostname.org",
-      RP_NAME = "RP Name",
-      RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg";
+      RP_NAME = "The Planet's Most Awesome Site",
+      RP_TOS_URL = "https://browserid.org/TOS.html",
+      RP_PP_URL = "https://browserid.org/priv.html",
+      RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg",
+      mediator = bid.Mediator;
 
   module("controllers/rp_info", {
     setup: testHelpers.setup,
@@ -84,5 +87,17 @@
     equal($("#rp_logo").attr("src"), RP_HTTPS_LOGO, "rp logo shown");
   });
 
+  test("privacyPolicy, termsOfService specified - show TOS/PP info", function() {
+    createController({
+      siteName: RP_NAME,
+      privacyPolicy: RP_PP_URL,
+      termsOfService: RP_TOS_URL
+    });
+
+    equal($("#rp_name").text(), RP_NAME, "RP's name is set");
+    equal($("#rp_tos").attr("href"), RP_TOS_URL, "RP's TOS is set");
+    equal($("#rp_pp").attr("href"), RP_PP_URL, "RP's Privacy Policy is set");
+  });
+
 }());
 
diff --git a/resources/static/test/cases/controllers/set_password.js b/resources/static/test/cases/dialog/js/modules/set_password.js
similarity index 75%
rename from resources/static/test/cases/controllers/set_password.js
rename to resources/static/test/cases/dialog/js/modules/set_password.js
index a1ae3b3c683434ebf000678e3b77943fbaf15d82..529ef0d1c18aa7b3c44e7d60c91cd1fe18460396 100644
--- a/resources/static/test/cases/controllers/set_password.js
+++ b/resources/static/test/cases/dialog/js/modules/set_password.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -10,6 +10,8 @@
       el = $("body"),
       bid = BrowserID,
       testHelpers = bid.TestHelpers,
+      testElementExists = testHelpers.testElementExists,
+      testElementNotExists = testHelpers.testElementDoesNotExist,
       register = testHelpers.register,
       controller;
 
@@ -33,22 +35,29 @@
 
   test("create with no options - show template, user must verify email, can cancel", function() {
     ok($("#set_password").length, "set_password template added");
-    equal($("#verify_user").length, 1, "correct button shown");
-    equal($("#cancel").length, 1, "cancel button shown");
+    testElementExists("#verify_user");
+    testElementExists("#cancel");
+    testElementNotExists("#persona_tospp");
   });
 
   test("create with password_reset option - show template, show reset password button", function() {
     controller.destroy();
     createController({ password_reset: true });
-    ok($("#set_password").length, "set_password template added");
-    equal($("#password_reset").length, 1, "correct button shown");
-    equal($("#cancel").length, 1, "cancel button shown");
+    testElementExists("#set_password");
+    testElementExists("#password_reset");
+    testElementExists("#cancel");
+  });
+
+  test("create with personaTOSPP option - show Persona TOS/PP", function() {
+    controller.destroy();
+    createController({ personaTOSPP: true });
+    testElementExists("#persona_tospp");
   });
 
   test("create with cancelable=false option - cancel button not shown", function() {
     controller.destroy();
     createController({ cancelable: false });
-    equal($("#cancel").length, 0, "cancel button not shown");
+    testElementNotExists("#cancel");
   });
 
   asyncTest("submit with good password/vpassword - password_set message raised", function() {
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 76%
rename from resources/static/test/cases/controllers/verify_primary_user.js
rename to resources/static/test/cases/dialog/js/modules/verify_primary_user.js
index 5203792b726a532a914f7ef376e269a4f1e86863..cce8e0c1932acffa2e02dd1ec52aadb9af8ea81a 100644
--- a/resources/static/test/cases/controllers/verify_primary_user.js
+++ b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global asyncTest: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: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
@@ -10,6 +10,8 @@
       controller,
       el,
       testHelpers = bid.TestHelpers,
+      testElementExists = testHelpers.testElementExists,
+      testElementNotExists = testHelpers.testElementDoesNotExist,
       WindowMock = bid.Mocks.WindowMock,
       win,
       mediator = bid.Mediator;
@@ -33,41 +35,44 @@
     }
   });
 
-  test("create with privacyURL and tosURL defined - show TOS/PP", function() {
+  test("personaTOSPP true, requiredEmail: true - show TOS/PP", function() {
     createController({
       window: win,
       add: false,
       email: "unregistered@testuser.com",
       auth_url: "http://testuser.com/sign_in",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html"
+      requiredEmail: true,
+      personaTOSPP: false
     });
 
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
+    testElementNotExists("#persona_tospp");
   });
 
-  test("create with requiredEmail, privacyURL and tosURL defined - show TOS/PP", function() {
+  test("personaTOSPP true, requiredEmail: false - show TOS/PP", function() {
     createController({
       window: win,
       add: false,
-      requiredEmail: "unregistered@testuser.com",
       email: "unregistered@testuser.com",
       auth_url: "http://testuser.com/sign_in",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html"
+      requiredEmail: false,
+      personaTOSPP: false
     });
 
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
+    testElementNotExists("#persona_tospp");
   });
 
-  asyncTest("submit with `add: false` option opens a new tab with CREATE_EMAIL URL", function() {
+  asyncTest("submit with `add: false` option opens a new tab with proper URL (updated for sessionStorage)", function() {
     var messageTriggered = false;
     createController({
       window: win,
       add: false,
       email: "unregistered@testuser.com",
-      auth_url: "http://testuser.com/sign_in"
+      auth_url: "http://testuser.com/sign_in",
+      personaTOSPP: true
     });
+
+    testElementExists("#persona_tospp");
+
     mediator.subscribe("primary_user_authenticating", function() {
       messageTriggered = true;
     });
@@ -77,26 +82,29 @@
     win.document.location.hash = "#NATIVE";
 
     controller.submit(function() {
-      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23CREATE_EMAIL%3Dunregistered%40testuser.com");
+      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com");
       equal(messageTriggered, true, "primary_user_authenticating triggered");
       start();
     });
   });
 
-  asyncTest("submit with `add: true` option opens a new tab with ADD_EMAIL URL", function() {
+  asyncTest("submit with `add: true` option opens a new tab with proper URL (updated for sessionStorage)", function() {
     createController({
       window: win,
       add: true,
       email: "unregistered@testuser.com",
-      auth_url: "http://testuser.com/sign_in"
+      auth_url: "http://testuser.com/sign_in",
+      personaTOSPP: true
     });
 
+    testElementExists("#persona_tospp");
+
     // Also checking to make sure the NATIVE is stripped out.
     win.document.location.href = "sign_in";
     win.document.location.hash = "#NATIVE";
 
     controller.submit(function() {
-      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23ADD_EMAIL%3Dunregistered%40testuser.com");
+      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com");
       start();
     });
   });
diff --git a/resources/static/test/cases/include.js b/resources/static/test/cases/include.js
index 0cadc3e3576f2241abf572d26c291b005f5b2993..35cfd9894fd6822df4dc2d2f32a3895634ca5d8f 100644
--- a/resources/static/test/cases/include.js
+++ b/resources/static/test/cases/include.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/pages/about.js b/resources/static/test/cases/pages/js/about.js
similarity index 94%
rename from resources/static/test/cases/pages/about.js
rename to resources/static/test/cases/pages/js/about.js
index 298642ce39fc66e35c73f04e9a6be107b781ebdd..e00fbf5674a9dd6efc337b1005649df49891ebcd 100644
--- a/resources/static/test/cases/pages/about.js
+++ b/resources/static/test/cases/pages/js/about.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/pages/browserid.js b/resources/static/test/cases/pages/js/browserid.js
similarity index 86%
rename from resources/static/test/cases/pages/browserid.js
rename to resources/static/test/cases/pages/js/browserid.js
index 0232715a3d8e1b9c1c3775636e0e82cba4694ee5..661382d958c69eda4ce6d686eeaa2ca770d32198 100644
--- a/resources/static/test/cases/pages/browserid.js
+++ b/resources/static/test/cases/pages/js/browserid.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/pages/forgot.js b/resources/static/test/cases/pages/js/forgot.js
similarity index 98%
rename from resources/static/test/cases/pages/forgot.js
rename to resources/static/test/cases/pages/js/forgot.js
index d1b1466193204d8a576cd6fc0a99bdc23ab57b39..120026480de3836ae0791345d500eda702970d84 100644
--- a/resources/static/test/cases/pages/forgot.js
+++ b/resources/static/test/cases/pages/js/forgot.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/pages/manage_account.js b/resources/static/test/cases/pages/js/manage_account.js
similarity index 99%
rename from resources/static/test/cases/pages/manage_account.js
rename to resources/static/test/cases/pages/js/manage_account.js
index c40870aac35ff7e2c0fc16a078a7106e91f47f5d..d4fc4b1f8412f55b978e458133608e92389f24f8 100644
--- a/resources/static/test/cases/pages/manage_account.js
+++ b/resources/static/test/cases/pages/js/manage_account.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/pages/page_helpers.js b/resources/static/test/cases/pages/js/page_helpers.js
similarity index 99%
rename from resources/static/test/cases/pages/page_helpers.js
rename to resources/static/test/cases/pages/js/page_helpers.js
index 5df6b052f6f8d79a3539b7d453da436bdc361824..4b9585ad7dedd53bb3be5478797674406428f400 100644
--- a/resources/static/test/cases/pages/page_helpers.js
+++ b/resources/static/test/cases/pages/js/page_helpers.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/cases/pages/signin.js b/resources/static/test/cases/pages/js/signin.js
similarity index 99%
rename from resources/static/test/cases/pages/signin.js
rename to resources/static/test/cases/pages/js/signin.js
index 79baa83d22529f7981ad6f6d499ce7cec383d999..4927cb5e602a668eeb4ba1b524a12a280e511928 100644
--- a/resources/static/test/cases/pages/signin.js
+++ b/resources/static/test/cases/pages/js/signin.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/cases/pages/signup.js b/resources/static/test/cases/pages/js/signup.js
similarity index 99%
rename from resources/static/test/cases/pages/signup.js
rename to resources/static/test/cases/pages/js/signup.js
index 92c2cd00573462efdfb27549a73f4711ab56fc7f..7db9cae261cf2840ab49f59b2d1e31eeadf896c4 100644
--- a/resources/static/test/cases/pages/signup.js
+++ b/resources/static/test/cases/pages/js/signup.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
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 98%
rename from resources/static/test/cases/pages/verify_secondary_address.js
rename to resources/static/test/cases/pages/js/verify_secondary_address.js
index ac4ddaa8b832c09fe5a03cd19c8a49a29fdcd37e..f15227bc0934937982552abb941804c3fe69463f 100644
--- a/resources/static/test/cases/pages/verify_secondary_address.js
+++ b/resources/static/test/cases/pages/js/verify_secondary_address.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global test: true, start: true, module: true, ok: true, equal: true, BrowserID: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
diff --git a/resources/static/test/mocks/cachify.js b/resources/static/test/mocks/cachify.js
index 7f95a7aa46a82b816d4c07b6967136c6fb22d342..6b1abe0c7827fe1ba21c787a5671a50ff5f38dcb 100644
--- a/resources/static/test/mocks/cachify.js
+++ b/resources/static/test/mocks/cachify.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
diff --git a/resources/static/test/mocks/mocks.js b/resources/static/test/mocks/mocks.js
index 21d167af5df37e07262b87c6331a3258ff24bb37..73a115855dac2d6e115489ded66c1662b29c5794 100644
--- a/resources/static/test/mocks/mocks.js
+++ b/resources/static/test/mocks/mocks.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
diff --git a/resources/static/test/mocks/provisioning.js b/resources/static/test/mocks/provisioning.js
index cdee24b1306b946483456f8fc2fad4e6ca16de3a..13907e4d7b981c59658f65268b32cec9f8cdfe8c 100644
--- a/resources/static/test/mocks/provisioning.js
+++ b/resources/static/test/mocks/provisioning.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
@@ -16,12 +16,26 @@ BrowserID.Mocks.Provisioning = (function() {
 
   function Provisioning(info, onsuccess, onfailure) {
     if(status === Provisioning.AUTHENTICATED) {
-      onsuccess(keypair, cert);
+      if (!keypair) {
+        // JWCrypto relies on there being a random seed.  The random seed is
+        // gotten whenever network.withContext is called.  Since this is
+        // supposed to mock the IdP provisioning step which will not call
+        // network.withContext, add a random seed to ensure that we can get our
+        // keypair.
+        jwcrypto.addEntropy("H+ZgKuhjVckv/H4i0Qvj/JGJEGDVOXSIS5RCOjY9/Bo=");
+        jwcrypto.generateKeypair({algorithm: "DS", keysize: 256}, function(err, kp) {
+          keypair = kp;
+          if (onsuccess) onsuccess(keypair, cert);
+        });
+      }
+      else {
+        if (onsuccess) onsuccess(keypair, cert);
+      }
     }
     else onfailure(failure);
   }
 
-  Provisioning.setStatus = function(newStatus, cb) {
+  Provisioning.setStatus = function(newStatus) {
     failure = null;
 
     status = newStatus;
@@ -31,14 +45,6 @@ BrowserID.Mocks.Provisioning = (function() {
         code: "primaryError",
         msg: "user is not authenticated as target user"
       };
-      if (cb) cb();
-    }
-    else if(newStatus === Provisioning.AUTHENTICATED) {
-      if (!keypair)
-        jwcrypto.generateKeypair({algorithm: "DS", keysize: 256}, function(err, kp) {
-          keypair = kp;
-          if (cb) cb();
-        });
     }
   };
 
diff --git a/resources/static/test/mocks/winchan.js b/resources/static/test/mocks/winchan.js
index 43ecf8dc51c519fdf173298db27290ec23ac2d78..cab49e566953dcf7c2b3f214afef4ea41710bc83 100644
--- a/resources/static/test/mocks/winchan.js
+++ b/resources/static/test/mocks/winchan.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
diff --git a/resources/static/test/mocks/window.js b/resources/static/test/mocks/window.js
index 510514288a7897747ba90d6c00b38be5641a0ba4..e41e909e232e8d478792b234fb1fa628550dc5cb 100644
--- a/resources/static/test/mocks/window.js
+++ b/resources/static/test/mocks/window.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global BrowserID: 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
@@ -15,6 +15,7 @@ BrowserID.Mocks.WindowMock = (function() {
 
   function WindowMock() {
     this.document = new DocumentMock();
+    this.sessionStorage = {};
   }
   WindowMock.prototype = {
     open: function(url, name, options) {
diff --git a/resources/static/test/mocks/xhr.js b/resources/static/test/mocks/xhr.js
index e2d56028acabded23da7beaeea1bfa7726cfc26b..95f6b1d192ce8d0bb9ddd7063a796daa51b0af63 100644
--- a/resources/static/test/mocks/xhr.js
+++ b/resources/static/test/mocks/xhr.js
@@ -1,4 +1,4 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
+/*jshint browser: true, forin: true, laxbreak: true */
 /*global start: true, stop: true, module: true, ok: true, equal: true, BrowserID: 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
diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js
index 119bd73f64a8f23eec6b6655e63781e11459b751..66868076a8c8cd477b25b2db0e5e1e6023595037 100644
--- a/resources/static/test/testHelpers/helpers.js
+++ b/resources/static/test/testHelpers/helpers.js
@@ -221,10 +221,6 @@ BrowserID.TestHelpers = (function() {
       }
     },
 
-    testHasClass: function(selector, className, msg) {
-      ok($(selector).hasClass(className), msg || selector + " has className: " + className);
-    },
-
     testUndefined: function(toTest, msg) {
       equal(typeof toTest, "undefined", msg || "object is undefined");
     },
@@ -235,8 +231,33 @@ BrowserID.TestHelpers = (function() {
 
     testVisible: function(selector, msg) {
       ok($(selector).is(":visible"), msg || selector + " should be visible");
-    }
+    },
 
+    testHasClass: function(selector, className, msg) {
+      ok($(selector).hasClass(className),
+          msg || (selector + " has className " + className));
+    },
+
+    testNotHasClass: function(selector, className, msg) {
+      ok(!$(selector).hasClass(className),
+          msg || (selector + " does not have className " + className));
+    },
+
+    testElementExists: function(selector, msg) {
+      ok($(selector).length, msg || ("element '" + selector + "' exists"));
+    },
+
+    testElementDoesNotExist: function(selector, msg) {
+      ok(!$(selector).length, msg || ("element '" + selector + "' does not exist"));
+    },
+
+    testRPTosPPShown: function(msg) {
+      TestHelpers.testHasClass("body", "rptospp", msg || "RP TOS/PP shown");
+    },
+
+    testRPTosPPNotShown: function(msg) {
+      TestHelpers.testNotHasClass("body", "rptospp", msg || "RP TOS/PP not shown");
+    }
   };
 
   return TestHelpers;
diff --git a/resources/views/about.ejs b/resources/views/about.ejs
index dfdeba036ae1e32c4470c9530e9b96b6a31d2792..4ea433948b01e4d7b87b5c5957fad13fcda54322 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="<%- cachify('/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="<%- cachify('/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="<%- cachify('/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.ejs b/resources/views/dialog.ejs
index 9bf2250be9fd1a0834b44c4843125e54fe8f2c7e..3402c4dbd759ccd1ba850d1f02f760d01d1f0a8b 100644
--- a/resources/views/dialog.ejs
+++ b/resources/views/dialog.ejs
@@ -28,23 +28,28 @@
     <section id="wait">
         <!-- because each section is an absolutely positioned element, we have to use the inner table container element to be able to vertically/horizontally center correctly.  Without the table element, the layout gets all messed up. -->
         <div class="table">
-            <div class="vertical contents">
-                <h2><%= gettext('Communicating with server') %></h2>
-                <p><%= gettext('Just a moment while we talk with the server.') %></p>
+            <div class="vertical">
+                <div class="contents">
+                    <h2 class="center"><%= gettext('Connecting to Persona...') %></h2>
+                </div>
             </div>
         </div>
     </section>
 
     <section id="error">
         <div class="table">
-            <div class="vertical contents">
+            <div class="vertical">
+                <div class="contents">
+                </div>
             </div>
         </div>
     </section>
 
     <section id="delay">
         <div class="table">
-            <div class="vertical contents">
+            <div class="vertical">
+                <div class="contents">
+                </div>
             </div>
         </div>
     </section>
diff --git a/resources/views/dialog_layout.ejs b/resources/views/dialog_layout.ejs
index 4b12722611e3681ead1acbc0eb7e202eff51bad5..d04fa6dbb88dcf1df35efb1170cda483a0fe1858 100644
--- a/resources/views/dialog_layout.ejs
+++ b/resources/views/dialog_layout.ejs
@@ -6,11 +6,11 @@
 <html LANG="<%= lang %>" dir="<%= lang_dir %>">
 <head>
   <meta charset="utf-8">
-  <meta name="viewport" content="initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,width=device-width" />
+  <meta name="viewport" content="initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
   <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]>
@@ -31,7 +31,7 @@
       </div>
 
       <footer>
-<%- format(gettext('Mozilla Persona is the fast and secure way to sign in.  <a %s>Learn more &rarr;</a>'), [" href='/about' target='_blank'"]) %>
+<%- format(gettext('<strong>Persona.</strong> Simplified sign-in, built by a non-profit.  <a %s>Learn more&rarr;</a>'), [" href='/about' target='_blank'"]) %>
 
       </footer>
 
diff --git a/resources/views/forgot.ejs b/resources/views/forgot.ejs
index 369ef504aefe55627d3a83216fb11b13dee532f4..87b0d8f7b4f853feeb6ffd4cdbd1f64ef3d7ad71 100644
--- a/resources/views/forgot.ejs
+++ b/resources/views/forgot.ejs
@@ -72,7 +72,7 @@
                     </div>
 
                     <div class="tooltip" id="passwords_no_match" for="vpassword">
-                      Passwords do not match.
+                      These passwords don't match!
                     </div>
 
                 </li>
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..cbc7cc078e9ea6f1f6e0dbb0f6b13347196e1094 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>
+    <%- cachify_js('/production/html5shim.js') %>
   <![endif]-->
   <%- cachify_css('/production/browserid.css') %>
   <!--[if lt IE 9]>
diff --git a/resources/views/signin.ejs b/resources/views/signin.ejs
index a0ed5a5fc8300f55d29493db7f345dd0bf23f23e..d09e2a56e3ad317ced53ac1e9547dcf606433900 100644
--- a/resources/views/signin.ejs
+++ b/resources/views/signin.ejs
@@ -42,7 +42,8 @@
                     </div>
 
                     <div id="cannot_authenticate" class="tooltip" for="password">
-                      The account cannot be logged in with this username and password.
+                      This email address and password do not match.
+                    </div>
 
                 </li>
             </ul>
diff --git a/resources/views/signup.ejs b/resources/views/signup.ejs
index dcf66e0702c932803c7ca991217b509a4e69323e..81ec178b4280f3f1394062c80d9574f04e00dad5 100644
--- a/resources/views/signup.ejs
+++ b/resources/views/signup.ejs
@@ -75,17 +75,27 @@
                     </div>
 
                     <div class="tooltip" id="passwords_no_match" for="vpassword">
-                      Passwords do not match.
+                      These passwords don't match!
                     </div>
 
                 </li>
             </ul>
 
             <div class="submit cf forminputs">
-                <button>Verify Email</button>
-                <div class="remember cf">
-                    <a class="action" href="/signin" tabindex="2">Existing account? Sign in.</a>
-                </div>
+                <p class="cf">
+                  <button>Verify Email</button>
+                  <a class="action remember" href="/signin" tabindex="2">Existing account? Sign in.</a>
+                </p>
+
+                <p class="tospp">
+                   <%- format(
+                        gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                             [ "Persona",
+                               ' href="https://login.persona.org/tos" target="_new"',
+                               ' href="https://login.persona.org/privacy" target="_new"',
+                             ]) %>
+                  </p>
+
             </div>
 
             <ul class="notifications">
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>
diff --git a/scripts/awsbox/post_deploy.js b/scripts/awsbox/post_deploy.js
index af133aa60c7652b91cdc1bf66b2a616af507a92c..a8e56f72a4795cfa944337506894cb9cdaa524a8 100755
--- a/scripts/awsbox/post_deploy.js
+++ b/scripts/awsbox/post_deploy.js
@@ -10,7 +10,20 @@ fi
 
 echo ">> updating strings"
 svn co -q http://svn.mozilla.org/projects/l10n-misc/trunk/browserid/locale
+cd locale
+svn up
+cd ..
+./scripts/extract_po.sh locale/
+# yuck!  our debug language breaks if this is not present
+for file in locale/templates/LC_MESSAGES/*.pot ; do
+    mv $file $file.old
+    sed 's/CHARSET/UTF-8/g' $file.old > $file
+    rm -f $file.old
+done
+
+./scripts/merge_po.sh locale/
 ./locale/compile-mo.sh locale/
+./locale/compile-json.sh locale/ resources/static/i18n/
 
 echo ">> generating production resources"
 scripts/compress
diff --git a/scripts/compress b/scripts/compress
index 7c8b797d6157699f19f7eb316bc7cb6ff9670e97..55e5a241f3cc1758d256c607b7c7fc233b19e871 100755
--- a/scripts/compress
+++ b/scripts/compress
@@ -41,7 +41,7 @@ Object.keys(all).forEach(function(resource) {
   // in prod '/build/templates.js' has all templates glommed into it,
   // and is bundled into the Big Minified Piles Of Resources we ship.
   // Here we sub the former with the latter.
-  var ix = all[resource].indexOf('/shared/templates.js');
+  var ix = all[resource].indexOf('/common/js/templates.js');
   if (ix != -1) all[resource].splice(ix, 1, '/build/templates.js');
 
   cc.enqueue({
diff --git a/tests/static-resource-test.js b/tests/static-resource-test.js
index ceda594e43ea9f697b56507f2b909bf9f66286f8..66a83a9ba88fb4ef3428474f635e8acfc5ac9142 100755
--- a/tests/static-resource-test.js
+++ b/tests/static-resource-test.js
@@ -23,7 +23,7 @@ suite.addBatch({
       var res = resources.resources;
       assert.ok(files['/production/dialog.css'].length >= 3);
       // Get ride of non-localized asset bundles
-      ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js'].forEach(
+      ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js', '/production/html5shim.js'].forEach(
         function (nonLocaleAsset) {
           delete res[nonLocaleAsset];
           delete files[nonLocaleAsset];