diff --git a/README.md b/README.md
index 548b2a3bf..ba81262ad 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ We've made it easy to fork the project, run a simple script that'll install all
 
 ### [Getting In Touch](https://github.com/codecombat/codecombat/wiki/Developer-organization)
 
-Whether you're novice or pro, the CodeCombat team is ready to help you implement your ideas. Reach out on our [forum](http://discourse.codecombat.com), our [issue tracker](https://github.com/codecombat/codecombat/issues), or our [developer chat room](https://www.hipchat.com/g3plnOKqa), or see the docs for [more on how to contribute](https://github.com/codecombat/codecombat/wiki/Developer-organization).
+Whether you're novice or pro, the CodeCombat team is ready to help you implement your ideas. Reach out on our [forum](http://discourse.codecombat.com), our [issue tracker](https://github.com/codecombat/codecombat/issues), or our [developer chat room](https://www.hipchat.com/gkaufqwnj), or see the docs for [more on how to contribute](https://github.com/codecombat/codecombat/wiki/Developer-organization).
 
 ### [License](https://github.com/codecombat/codecombat/blob/master/LICENSE)
 
diff --git a/app/core/Router.coffee b/app/core/Router.coffee
index a958417bc..29ce8a800 100644
--- a/app/core/Router.coffee
+++ b/app/core/Router.coffee
@@ -65,6 +65,8 @@ module.exports = class CocoRouter extends Backbone.Router
     'courses/mock1/enroll/:courseID': go('courses/mock1/CourseEnrollView')
     'courses/mock1/:courseID': go('courses/mock1/CourseDetailsView')
     'courses': go('courses/CoursesView')
+    'courses/students': go('courses/CoursesView')
+    'courses/teachers': go('courses/CoursesView')
     'courses/enroll(/:courseID)': go('courses/CourseEnrollView')
     'courses/:courseID(/:courseInstanceID)': go('courses/CourseDetailsView')
 
diff --git a/app/lib/world/names.coffee b/app/lib/world/names.coffee
index d17ab48f7..48cdc51d2 100644
--- a/app/lib/world/names.coffee
+++ b/app/lib/world/names.coffee
@@ -15,6 +15,7 @@ module.exports.thangNames = thangNames =
     'Shmeal'
     'Upfish'
     'Yugark'
+    'Shema'
   ]
   'Ogre Munchkin M': [
     # Male
@@ -50,6 +51,7 @@ module.exports.thangNames = thangNames =
     'Weeb'
     'Yart'
     'Zozo'
+    'Zock'
   ]
   'Ogre Thrower': [
     # Female
@@ -90,6 +92,7 @@ module.exports.thangNames = thangNames =
     'Taric'
     'Vaelia'
     'Antary'
+    'Femae'
   ]
   'Ogre Witch': [
     # Female
@@ -121,12 +124,14 @@ module.exports.thangNames = thangNames =
     'Ganju'
     'Hopper'
     'Ralthora'
+    'Yugorota'
   ]
   'Burl': [
     # Animal
     'Borlit'
     'Burlosh'
     'Dorf'
+    'Teemer'
   ]
   'Sand Yak': [
     # Animal
@@ -468,6 +473,7 @@ module.exports.thangNames = thangNames =
     'Warshall'
     'Yue Fei'
     'Zhou Tong'
+    'Archy'
   ]
   'Peasant M': [
     # Male
@@ -497,6 +503,7 @@ module.exports.thangNames = thangNames =
     'Winkler'
     'Yorik'
     'Yusef'
+    'Yoltovic'
   ]
   'Peasant F': [
     # Female
@@ -522,6 +529,7 @@ module.exports.thangNames = thangNames =
     'Ruth'
     'Tabitha'
     'Thea'
+    'Lea'
   ]
   'Soldier M': [
     # Male
@@ -648,6 +656,7 @@ module.exports.thangNames = thangNames =
     'Randy'
     'Raymond'
     'Remy'
+    'Rex'
     'Ricardo'
     'Richard'
     'Robert'
diff --git a/app/locale/es-419.coffee b/app/locale/es-419.coffee
index 967f56134..aa71c96eb 100644
--- a/app/locale/es-419.coffee
+++ b/app/locale/es-419.coffee
@@ -294,7 +294,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     infinite_loop_reset_level: "Reiniciar Nivel"
     infinite_loop_comment_out: "Comente Mi Código"
     tip_toggle_play: "Activa jugar/pausa con Ctrl+P."
-    tip_scrub_shortcut: "Ctrl+[ y Ctrl+] para rebobinar y avanzar rápido." 
+    tip_scrub_shortcut: "Ctrl+[ y Ctrl+] para rebobinar y avanzar rápido."
     tip_guide_exists: "Haga click en la guía en la parte superior de la página para obtener información útil"
     tip_open_source: "¡CodeCombat es 100% código abierto!"
     tip_tell_friends: "¿Disfrutando de CodeCombat? ¡Cuéntale a tus amigos acerca de nosotros!"
@@ -658,17 +658,17 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     sys_requirements_2: "CodeCombat no está soportado en iPad aún."
 
   teachers_survey:
-    title: "Encuesta para profesores"
-    must_be_logged: "Debe iniciar sesión primero. Por favor cree una cuenta o inicie sesión desde el menú en la parte superior."
+    title: "Encuesta de Maestros"
+    must_be_logged: "Debes ingresar primero. Por favor, crea una cuenta o ingresa desde el menú de arriba."
     retrieving: "Obteniendo información..."
-    being_reviewed_1: "Su solicitud para una prueba gratuita de subscripción está siendo"
+    being_reviewed_1: "Su aplicación a una suscripción gratuita está siendo"
     being_reviewed_2: "revisada."
-    approved_1: "Su solicitud para una prueba gratuita de subscripción fue"
-    approved_2: "Aprobada."
-    approved_3: "Instruccciones posteriores han sido enviadas a"
-    denied_1: "Su solicitud para una prueba gratuita de subscripción fue"
+    approved_1: "Su aplicación a una suscripción gratuita fue"
+    approved_2: "aprobada."
+    approved_3: "Más instrucciones han sido enviadas a"
+    denied_1: "Su aplicación a una suscripción gratuita ha sido"
     denied_2: "denegada."
-    contact_1: "Por favor contáctenos"
+    contact_1: "Por favor contactarse"
     contact_2: "si tiene más preguntas."
     description_1: "Ofrecemos suscripciones gratuitas a maestros con propósitos de evaluación. Puede hallar más información en nuestra"
     description_2: "página"
@@ -677,13 +677,13 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     email: "Dirección de email"
     school: "Nombre del colegio"
     location: "Nombre de la ciudad"
-    age_students: "¿Qué edad tienen sus estudiantes?"
-    under: "Menor"
-    other: "Otro:"
-    amount_students: "¿A cuantos alumnos les enseña?"
-    hear_about: "¿Donde escuchó sobre CodeCombat?"
-    fill_fields: "Porfavor llene todos los campos."
-    thanks: "Gracias! Vamos a mandarle instrucciónes para iniciar proximamente."
+    age_students: "¿Qué edad tienen tus estudiantes?"
+    under: "Bajo"
+    other: "Otros:"
+    amount_students: "¿A cuantos alumnos le enseña?"
+    hear_about: "¿Donde escuchaste acerca de CodeCombat?"
+    fill_fields: "Por favor llene todos los campos."
+    thanks: "¡Gracias! Pronto te enviaremos instrucciones para iniciar."
 
   versions:
     save_version_title: "Guardar nueva versión"
@@ -693,7 +693,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     cla_url: "CLA"
     cla_suffix: "."
     cla_agree: "ACEPTO"
-    owner_approve: "Un dueño necesitará aprobarlo antes de que tus cambios puedan ser visibles."
+    owner_approve: "Necesita la aprobación de un propietario para que los cambios sean visibles."
 
   contact:
     contact_us: "Contacta a CodeCombat"
@@ -730,8 +730,8 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     admin: "Admin"
     new_password: "Nueva Contraseña"
     new_password_verify: "Verificar"
-    type_in_email: "Ingrese su correo electrónico para confirmar la eliminación" # {change}
-    type_in_password: "También, escribe tu password."
+    type_in_email: "Ingrese su correo electrónico para confirmar la eliminación de su cuenta."
+    type_in_password: "Asimismo, ingrese su contraseña."
     email_subscriptions: "Suscripciones de Email"
     email_subscriptions_none: "No tienes suscripciones."
     email_announcements: "Noticias"
@@ -789,9 +789,9 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     article_editor_prefix: "¿Ves algún error en nuestros documentos? ¿Quieres hacer algunas instrucciones para tus propias creaciones? Revisa el"
     article_editor_suffix: "y ayuda a los jugadores de CodeCombat conseguir lo más posible de su tiempo jugando."
     find_us: "Encuentranos en etsos sitios"
-    social_github: "Checa todo nuestro código en el GitHub"
+    social_github: "Revisa todo nuestro código en GitHub"
     social_blog: "Lee el blog de CodeCombat en Sett"
-    social_discource: "Unite a la discusión en nuestro foro"
+    social_discource: "Únete a la discusión en nuestro foro"
     social_facebook: "Me Gusta CodeCombat en Facebook"
     social_twitter: "Sigue a CodeCombat en Twitter"
     social_gplus: "Únete a CodeCombat con Google+"
@@ -806,12 +806,12 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     make_private: "Hacer clan privado"
     subs_only: "solo suscriptores"
     create_clan: "Crear nuevo clan"
-    private_preview: "Previsualizar"
+    private_preview: "Vista previa"
     public_clans: "Clanes publicos"
     my_clans: "Mis Clanes"
     clan_name: "Nombre del clan"
     name: "Nombre"
-    chieftain: "Cacique/Líder"
+    chieftain: "Líder del Clan"
     type: "Tipo"
     edit_clan_name: "Editar el nombre del Clan"
     edit_clan_description: "Editar descripción del clan"
@@ -962,7 +962,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     indoor: "Interior"
     desert: "Desierto"
     grassy: "Herboso"
-    mountain: "Mountaña"
+    mountain: "Montaña"
     glacier: "Glaciar"
     small: "Pequeño"
     large: "Grande"
@@ -985,8 +985,8 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     level_tab_thangs_title: "Tiliches Actuales"
     level_tab_thangs_all: "Todo"
     level_tab_thangs_conditions: "Condiciones Iniciales"
-    level_tab_thangs_add: "Agregar Tiliches"
-    level_tab_thangs_search: "Buscar thangs"
+    level_tab_thangs_add: "Agregar Thangs"
+    level_tab_thangs_search: "Buscar Thangs"
     add_components: "Agregar Componentes"
     component_configs: "Configuraciones del Componente"
     config_thang: "Doble clic para configurar un Tiliche"
@@ -1032,8 +1032,8 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     pop_i18n: "Poblar I18N"
     tasks: "Tareas"
     clear_storage: "Borrar tus cambios locales"
-    add_system_title: "Agregar Sistemas al Level"
-    done_adding: "Se terminó de agregar"
+    add_system_title: "Agregar Sistemas al Nivel"
+    done_adding: "Finalizar"
 
   article:
     edit_btn_preview: "Vista previa"
@@ -1317,7 +1317,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     user_polls_record: "Historia de Visitas de Encuestas"
 
   concepts:
-    advanced_strings: "Manipulación Avanzada de Cadenas"
+    advanced_strings: "Cadenas - Avanzado"
     algorithms: "Algoritmos"
     arguments: "Argumentos"
     arithmetic: "Aritmética"
@@ -1327,18 +1327,17 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
     break_statements: "Sentencias Break"
     classes: "Clases"
     continue_statements: "Sentencias Continue"
-    for_loops: "Ciclos For"
+    for_loops: "Bucle For"
     functions: "Funciones"
     graphics: "Gráficos"
     if_statements: "Sentencias If"
-    input_handling: "Manejo de Entrada"
+    input_handling: "Manejo de Entradas"
     math_operations: "Operaciones Matemáticas"
-    object_literals: "Literales de Objeto"
-    parameters: "Parámetros"
+    object_literals: "Objetos Literales"
     strings: "Cadenas"
     variables: "Variables"
     vectors: "Vectores"
-    while_loops: "Ciclos While"
+    while_loops: "Bucles"
     recursion: "Recursividad"
 
   delta:
diff --git a/app/styles/courses/course-details.sass b/app/styles/courses/course-details.sass
index 3cace8e09..aa895e2d1 100644
--- a/app/styles/courses/course-details.sass
+++ b/app/styles/courses/course-details.sass
@@ -26,10 +26,10 @@
     padding: 2px
 
   .progress-concept-cell-complete
-    background-color: lightgray
+    background-color: lightgreen
 
   .progress-concept-cell-started
-    background-color: lightgreen
+    background-color: lightyellow
 
   .progress-concept-completion-container
     font-size: 10pt
@@ -76,10 +76,10 @@
     padding: 2px
 
   .progress-key-complete
-    background-color: lightgray
+    background-color: lightgreen
 
   .progress-key-started
-    background-color: lightgreen
+    background-color: lightyellow
 
   .progress-expand-checkbox
     margin-left: 14px
@@ -99,11 +99,11 @@
 
   .progress-level-cell-complete
     cursor: pointer
-    background-color: lightgray
+    background-color: lightgreen
 
   .progress-level-cell-started
     cursor: pointer
-    background-color: lightgreen
+    background-color: lightyellow
 
   .progess-levels-label
     color: #317EAC
diff --git a/app/templates/community-view.jade b/app/templates/community-view.jade
index b48e1514f..e4cd73006 100644
--- a/app/templates/community-view.jade
+++ b/app/templates/community-view.jade
@@ -65,7 +65,7 @@ block content
         a(href="https://plus.google.com/115285980638641924488/posts")
           img(src="/images/pages/community/logo_g+.png", data-i18n="[data-content]community.social_gplus" data-content="Join CodeCombat on Google+")
 
-        a(href="http://www.hipchat.com/g3plnOKqa")
+        a(href="http://www.hipchat.com/gkaufqwnj")
           img(src="/images/pages/community/logo_hipchat.png", data-i18n="[data-content]community.social_hipchat" data-content="Chat with us in the public CodeCombat HipChat room")
 
     .half-width
diff --git a/app/templates/contribute/archmage.jade b/app/templates/contribute/archmage.jade
index 200a2e454..0a7fac0b9 100644
--- a/app/templates/contribute/archmage.jade
+++ b/app/templates/contribute/archmage.jade
@@ -51,7 +51,7 @@ block content
           | Email us
         span(data-i18n="contribute.join_desc_3")
           | , or find us in our 
-        a(href="http://www.hipchat.com/g3plnOKqa", data-i18n="contribute.join_url_hipchat") public HipChat room
+        a(href="http://www.hipchat.com/gkaufqwnj", data-i18n="contribute.join_url_hipchat") public HipChat room
         span  
         span(data-i18n="contribute.join_desc_4")
           | and we'll go from there!
diff --git a/app/templates/contribute/artisan.jade b/app/templates/contribute/artisan.jade
index 47a9438be..08a033ad7 100644
--- a/app/templates/contribute/artisan.jade
+++ b/app/templates/contribute/artisan.jade
@@ -49,7 +49,7 @@ block content
         li
           a(href="/editor/level", data-i18n="contribute.artisan_join_step2") Create a new level and explore existing levels.
         li
-          a(href="http://www.hipchat.com/g3plnOKqa", data-i18n="contribute.artisan_join_step3") Find us in our public HipChat room for help.
+          a(href="http://www.hipchat.com/gkaufqwnj", data-i18n="contribute.artisan_join_step3") Find us in our public HipChat room for help.
         li
           a(href="http://discourse.codecombat.com", data-i18n="contribute.artisan_join_step4") Post your levels on the forum for feedback.
 
diff --git a/app/templates/courses/course-details.jade b/app/templates/courses/course-details.jade
index 1e9879484..ed424f25c 100644
--- a/app/templates/courses/course-details.jade
+++ b/app/templates/courses/course-details.jade
@@ -50,21 +50,31 @@ block content
 
     div.well.well-sm(role='tabpanel')
       ul.nav.nav-pills(role='tablist')
-        li.active(role='presentation')
-          a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab', data-i18n="courses.progress")
         if adminMode
+          li.active(role='presentation')
+            a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab', data-i18n="courses.progress")
           li(role='presentation')
             a(href='#invite', aria-controls='invite', role='tab', data-toggle='tab', data-i18n="courses.add_students")
-        li(role='presentation')
-          a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab', data-i18n="nav.play")
+          li(role='presentation')
+            a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab', data-i18n="nav.play")
+        else
+          li.active(role='presentation')
+            a(href='#levels', aria-controls='levels', role='tab', data-toggle='tab', data-i18n="nav.play")
+          li(role='presentation')
+            a(href='#progress', aria-controls='progress', role='tab', data-toggle='tab', data-i18n="courses.progress")
     .tab-content
-      .tab-pane.active#progress(role='tabpanel')
-        +progress-tab
       if adminMode
+        .tab-pane.active#progress(role='tabpanel')
+          +progress-tab
         .tab-pane#invite(role='tabpanel')
           +invite-tab
-      .tab-pane#levels(role='tabpanel')
-        +levels-tab
+        .tab-pane#levels(role='tabpanel')
+          +levels-tab
+      else
+        .tab-pane.active#levels(role='tabpanel')
+          +levels-tab
+        .tab-pane#progress(role='tabpanel')
+          +progress-tab
 
 mixin progress-tab
   .container-fluid.progress-summary-container
@@ -141,7 +151,8 @@ mixin progress-members
             span(style='padding-left:16px;')
           span.progress-key.progress-key-complete(data-i18n="clans.complete_1")
           span.progress-key.progress-key-started(data-i18n="clans.started_1")
-          span.progress-key(data-i18n="clans.not_started_1")
+          if showExpandedProgress
+            span.progress-key(data-i18n="clans.not_started_1")
           input.progress-expand-checkbox(type='checkbox')
           span.spl.progress-expand-label(data-i18n="courses.expand_details")
     tbody
@@ -250,19 +261,20 @@ mixin invite-tab
   h3(data-i18n="courses.invite_link_header")
   p(data-i18n="courses.invite_link_p_1")
   .alert.alert-info
-    strong= document.location.origin + "/courses?_ppc=" + view.prepaid.get('code')
+    strong= document.location.origin + "/courses/students?_ppc=" + view.prepaid.get('code')
   p(data-i18n="courses.invite_link_p_2")
   .form
     .form-group
       textarea#invite-emails-textarea.form-control(
-        rows=3, data-i18n="[placeholder]courses.enter_emails", placeholder="Enter student emails to invite, one per line")
+        rows=3, data-i18n="[placeholder]courses.enter_emails")
+      .help-block(data-i18n="courses.enter_emails")
     .form-group
       button#invite-btn.btn.btn-success(data-i18n="courses.send_invites")
       #invite-emails-sending-alert.alert.alert-info.hide(data-i18n="common.sending")
       #invite-emails-success-alert.alert.alert-success.hide(data-i18n="play_level.done")
 
-  h3 Class Capacity
-  if view.prepaid.loaded
+  if view.prepaid.loaded && pricePerSeat > 0
+    h3 Class Capacity
     p
       span.spr(data-i18n="courses.capacity_used")
       span #{view.prepaid.get('redeemers').length} / #{view.prepaid.get('maxRedeemers')}.
@@ -277,13 +289,18 @@ mixin levels-tab
         th(data-i18n="courses.concepts")
     tbody
       if campaign
+        - var lastLevelCompleted = true;
         each level, levelID in campaign.get('levels')
           tr
             td
-              button.btn.btn-success.btn-play-level(data-level-slug=level.slug, data-i18n="home.play")
+              if lastLevelCompleted || adminMode
+                button.btn.btn-success.btn-play-level(data-level-slug=level.slug, data-i18n="home.play")
             td
               if userLevelStateMap[me.id]
                 div= userLevelStateMap[me.id][levelID]
+                - lastLevelCompleted = userLevelStateMap[me.id][levelID] === 'complete'
+              else
+                - lastLevelCompleted = false
             td= level.name.replace('Course: ', '')
             td
               if levelConceptMap[levelID]
@@ -307,14 +324,5 @@ mixin settings-dialog
           strong(data-i18n="courses.description")
         p 
           textarea.settings-description-input(rows=2)= courseInstance.get('description')
-        p(data-i18n="courses.languages_available")
-        p 
-          select.form-control.settings-language-select
-            option(value="Python") Python
-            option(value="JavaScript") JavaScript
-            option(value="All Languages", data-i18n="courses.all_lang")
-        p
-          input.settings-public-progress(type='checkbox', checked)
-          span.spl(data-i18n="courses.show_progress")
       .modal-footer
         button.btn.btn-save-settings(data-i18n="common.save_changes")
diff --git a/app/templates/courses/course-enroll.jade b/app/templates/courses/course-enroll.jade
index c18d47006..056309c2e 100644
--- a/app/templates/courses/course-enroll.jade
+++ b/app/templates/courses/course-enroll.jade
@@ -37,14 +37,18 @@ block content
           if courses.length > 1
             option(value="All Courses", data-i18n="courses.all_courses")
 
-      h3
-        span 2.
-        span.spl(data-i18n="courses.number_students")
-      p(data-i18n="courses.enter_number_students")
-      input.input-seats(type='text', value="#{seats}")
+      if price > 0
+        h3
+          span 2.
+          span.spl(data-i18n="courses.number_students")
+        p(data-i18n="courses.enter_number_students")
+        input.input-seats(type='text', value="#{seats}")
 
       h3
-        span 3.
+        if price > 0
+          span 3.
+        else
+          span 2.
         span.spl(data-i18n="courses.name_class")
       p(data-i18n="courses.displayed_course_page")
       input.class-name(type='text', placeholder="Mrs. Smith's 4th Period", value="#{className ? className : ''}")
@@ -55,7 +59,7 @@ block content
           span.spl(data-i18n="courses.buy") Buy
       else
         h3
-          span 4.
+          span 3.
           span.spl(data-i18n="courses.create_class")
       p
         if price > 0
@@ -77,11 +81,11 @@ block content
       +trial-and-questions
 
 mixin trial-and-questions
-  h3(data-i18n="courses.free_trial")
-  p
-    span.spr(data-i18n="teachers.teacher_subs_1")
-    a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
-    span.spl(data-i18n="courses.get_access")
+  //- h3(data-i18n="courses.free_trial")
+  //- p
+  //-   span.spr(data-i18n="teachers.teacher_subs_1")
+  //-   a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
+  //-   span.spl(data-i18n="courses.get_access")
 
   h3(data-i18n="courses.questions")
   p
diff --git a/app/templates/courses/courses.jade b/app/templates/courses/courses.jade
index 997c9f5ba..4d740ada5 100644
--- a/app/templates/courses/courses.jade
+++ b/app/templates/courses/courses.jade
@@ -9,6 +9,8 @@ block content
   br
   if state === 'enrolling'
     .alert.alert-info Enrolling in course..
+  else if state === 'ppc_logged_out'
+    .alert.alert-success Log in or create an account to join this course.
   else
     if state === 'unknown_error'
       .alert.alert-danger.alert-dismissible= stateMessage
@@ -55,6 +57,14 @@ mixin teacher-main
       .well.well-sm
         div.praise-quote "#{praise.quote}"
         div.praise-caption - #{praise.source}
+
+  //- h1.center(data-i18n="courses.free_trial")
+  //- .info-container
+  //-   p
+  //-     span.spr(data-i18n="teachers.teacher_subs_1")
+  //-     a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
+  //-     span.spl(data-i18n="courses.get_access")
+
   h2.center(data-i18n="courses.choose_course")
 
 mixin student-dialog(course)
diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade
index f12e3df08..d551f48f6 100644
--- a/app/templates/editor/level/edit.jade
+++ b/app/templates/editor/level/edit.jade
@@ -106,7 +106,7 @@ block header
             li
               a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki", target="_blank") Wiki
             li
-              a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
+              a(href='http://www.hipchat.com/gkaufqwnj', data-i18n="editor.live_chat", target="_blank") Live Chat
             li
               a(href='http://discourse.codecombat.com/category/artisan', data-i18n="nav.forum", target="_blank") Forum
             li
diff --git a/app/templates/editor/thang/thang-type-edit-view.jade b/app/templates/editor/thang/thang-type-edit-view.jade
index 8da04e152..2d9160f2f 100644
--- a/app/templates/editor/thang/thang-type-edit-view.jade
+++ b/app/templates/editor/thang/thang-type-edit-view.jade
@@ -67,7 +67,7 @@ block header
             li
               a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki", target="_blank") Wiki
             li
-              a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
+              a(href='http://www.hipchat.com/gkaufqwnj', data-i18n="editor.live_chat", target="_blank") Live Chat
             li
               a(href='http://discourse.codecombat.com/category/artisan', data-i18n="nav.forum", target="_blank") Forum
             li
diff --git a/app/templates/i18n/i18n-edit-model-view.jade b/app/templates/i18n/i18n-edit-model-view.jade
index efa2f4a9b..8898079cb 100644
--- a/app/templates/i18n/i18n-edit-model-view.jade
+++ b/app/templates/i18n/i18n-edit-model-view.jade
@@ -43,7 +43,7 @@ block header
             li
               a(href='https://github.com/codecombat/codecombat/wiki', data-i18n="editor.wiki", target="_blank") Wiki
             li
-              a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
+              a(href='http://www.hipchat.com/gkaufqwnj', data-i18n="editor.live_chat", target="_blank") Live Chat
             li
               a(href='http://discourse.codecombat.com/category/diplomat', data-i18n="nav.forum", target="_blank") Forum
             li
diff --git a/app/views/courses/CourseDetailsView.coffee b/app/views/courses/CourseDetailsView.coffee
index f8bde2ab9..cc6d60696 100644
--- a/app/views/courses/CourseDetailsView.coffee
+++ b/app/views/courses/CourseDetailsView.coffee
@@ -54,6 +54,7 @@ module.exports = class CourseDetailsView extends RootView
     context.memberUserMap = @memberUserMap ? {}
     context.noCourseInstance = @noCourseInstance
     context.noCourseInstanceSelected = @noCourseInstanceSelected
+    context.pricePerSeat = @course.get('pricePerSeat')
     context.showExpandedProgress = @showExpandedProgress
     context.sortedMembers = @sortedMembers ? []
     context.userConceptStateMap = @userConceptStateMap ? {}
@@ -150,11 +151,11 @@ module.exports = class CourseDetailsView extends RootView
       levelStateMap[levelID] = state
 
       @instanceStats.totalLevelsCompleted++ if state is 'complete'
-      @instanceStats.totalPlayTime += levelSession.get('playtime')
+      @instanceStats.totalPlayTime += parseInt(levelSession.get('playtime') ? 0)
 
       @memberStats[userID] ?= totalLevelsCompleted: 0, totalPlayTime: 0
       @memberStats[userID].totalLevelsCompleted++ if state is 'complete'
-      @memberStats[userID].totalPlayTime += levelSession.get('playtime')
+      @memberStats[userID].totalPlayTime += parseInt(levelSession.get('playtime') ? 0)
 
       @userConceptStateMap[userID] ?= {}
       for concept of @levelConceptMap[levelID]
@@ -168,6 +169,7 @@ module.exports = class CourseDetailsView extends RootView
 
     if @courseInstance.get('members').length > 0
       @instanceStats.averageLevelsCompleted = @instanceStats.totalLevelsCompleted / @courseInstance.get('members').length
+      @instanceStats.averageLevelPlaytime = @instanceStats.totalPlayTime / @courseInstance.get('members').length
     for levelID, level of @campaign.get('levels')
       @instanceStats.furthestLevelCompleted = level.name if levelStateMap[levelID] is 'complete'
 
diff --git a/app/views/courses/CourseEnrollView.coffee b/app/views/courses/CourseEnrollView.coffee
index b34850e25..43e6b5386 100644
--- a/app/views/courses/CourseEnrollView.coffee
+++ b/app/views/courses/CourseEnrollView.coffee
@@ -58,15 +58,16 @@ module.exports = class CourseEnrollView extends RootView
   onClickBuy: (e) ->
     return @openModalView new AuthModal() if me.isAnonymous()
 
-    if @seats < 1 or not _.isFinite(@seats)
-      alert("Please enter the maximum number of students needed for your class.")
-      return
-
     if @price is 0
+      @seats = 9999
       @state = 'creating'
       @createClass()
       return
 
+    if @seats < 1 or not _.isFinite(@seats)
+      alert("Please enter the maximum number of students needed for your class.")
+      return
+
     @state = undefined
     @stateMessage = undefined
     @render()
diff --git a/app/views/courses/CoursesView.coffee b/app/views/courses/CoursesView.coffee
index ff42d9541..dfb496cf6 100644
--- a/app/views/courses/CoursesView.coffee
+++ b/app/views/courses/CoursesView.coffee
@@ -20,13 +20,18 @@ module.exports = class CoursesView extends RootView
   constructor: (options) ->
     super(options)
     @praise = utils.getCoursePraise()
-    @studentMode = utils.getQueryVariable('student', false) or options.studentMode
+    @studentMode = Backbone.history.getFragment()?.indexOf('courses/students') >= 0
     @courses = new CocoCollection([], { url: "/db/course", model: Course})
     @supermodel.loadCollection(@courses, 'courses')
     @courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
     @listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
     @supermodel.loadCollection(@courseInstances, 'course_instances')
-    @courseEnroll(prepaidCode) if prepaidCode = utils.getQueryVariable('_ppc', false)
+    if prepaidCode = utils.getQueryVariable('_ppc', false)
+      if me.isAnonymous()
+        @state = 'ppc_logged_out'
+      else
+        @studentMode = true
+        @courseEnroll(prepaidCode)
 
   getRenderData: ->
     context = super()
@@ -75,7 +80,7 @@ module.exports = class CoursesView extends RootView
   onClickEnroll: (e) ->
     $('.continue-dialog').modal('hide')
     courseID = $(e.target).data('course-id')
-    prepaidCode = $(".code-input[data-course-id=#{courseID}]").val()
+    prepaidCode = ($(".code-input[data-course-id=#{courseID}]").val() ? '').trim()
     @courseEnroll(prepaidCode)
 
   onClickEnter: (e) ->
@@ -89,17 +94,15 @@ module.exports = class CoursesView extends RootView
     Backbone.Mediator.publish 'router:navigate', navigationEvent
 
   onClickStudent: (e) ->
-    route = "/courses?student=true"
+    route = "/courses/students"
     viewClass = require 'views/courses/CoursesView'
-    viewArgs = [studentMode: true]
-    navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
+    navigationEvent = route: route, viewClass: viewClass, viewArgs: []
     Backbone.Mediator.publish 'router:navigate', navigationEvent
 
   onClickTeacher: (e) ->
-    route = "/courses?student=false"
+    route = "/courses/teachers"
     viewClass = require 'views/courses/CoursesView'
-    viewArgs = [studentMode: false]
-    navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
+    navigationEvent = route: route, viewClass: viewClass, viewArgs: []
     Backbone.Mediator.publish 'router:navigate', navigationEvent
 
   courseEnroll: (prepaidCode) ->
diff --git a/scripts/mongodb/createBulkPrepaids.js b/scripts/mongodb/createBulkPrepaids.js
index 04d20efc9..0b5978ca1 100644
--- a/scripts/mongodb/createBulkPrepaids.js
+++ b/scripts/mongodb/createBulkPrepaids.js
@@ -51,7 +51,7 @@ function generateNewCode(done)
 function createCode(length)
 {
     var text = "";
-    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+    var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
 
     for( var i=0; i < length; i++ )
         text += possible.charAt(Math.floor(Math.random() * possible.length));
diff --git a/server/courses/course_instance_handler.coffee b/server/courses/course_instance_handler.coffee
index 25abed4c8..7ae5dcf52 100644
--- a/server/courses/course_instance_handler.coffee
+++ b/server/courses/course_instance_handler.coffee
@@ -1,5 +1,6 @@
 async = require 'async'
 Handler = require '../commons/Handler'
+Campaign = require '../campaigns/Campaign'
 Course = require './Course'
 CourseInstance = require './CourseInstance'
 LevelSession = require '../levels/sessions/LevelSession'
@@ -88,11 +89,19 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
     CourseInstance.findById courseInstanceID, (err, courseInstance) =>
       return @sendDatabaseError(res, err) if err
       return @sendNotFoundError(res) unless courseInstance
-      memberIDs = _.map courseInstance.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
-      LevelSession.find {creator: {$in: memberIDs}}, (err, documents) =>
-        return @sendDatabaseError(res, err) if err?
-        cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
-        @sendSuccess(res, cleandocs)
+      Course.findById courseInstance.get('courseID'), (err, course) =>
+        return @sendDatabaseError(res, err) if err
+        return @sendNotFoundError(res) unless course
+        Campaign.findById course.get('campaignID'), (err, campaign) =>
+          return @sendDatabaseError(res, err) if err
+          return @sendNotFoundError(res) unless campaign
+          levelIDs = (levelID for levelID of campaign.get('levels'))
+          memberIDs = _.map courseInstance.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
+          query = {$and: [{creator: {$in: memberIDs}}, {'level.original': {$in: levelIDs}}]}
+          LevelSession.find query, (err, documents) =>
+            return @sendDatabaseError(res, err) if err?
+            cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
+            @sendSuccess(res, cleandocs)
 
   getMembersAPI: (req, res, courseInstanceID) ->
     CourseInstance.findById courseInstanceID, (err, courseInstance) =>
@@ -112,20 +121,25 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
       return @sendNotFoundError(res) unless courseInstance
       return @sendForbiddenError(res) unless @hasAccessToDocument(req, courseInstance)
 
-      Prepaid.findById courseInstance.get('prepaidID'), (err, prepaid) =>
+      Course.findById courseInstance.get('courseID'), (err, course) =>
         return @sendDatabaseError(res, err) if err
-        return @sendNotFoundError(res) unless prepaid
-        return @sendForbiddenError(res) unless prepaid.get('maxRedeemers') > prepaid.get('redeemers').length
-        for email in req.body.emails
-          context =
-            email_id: sendwithus.templates.course_invite_email
-            recipient:
-              address: email
-            email_data:
-              class_name: courseInstance.get('name')
-              join_link: "https://codecombat.com/courses?_ppc=" + prepaid.get('code')
-          sendwithus.api.send context, _.noop
-        return @sendSuccess(res, {})
+        return @sendNotFoundError(res) unless course
+
+        Prepaid.findById courseInstance.get('prepaidID'), (err, prepaid) =>
+          return @sendDatabaseError(res, err) if err
+          return @sendNotFoundError(res) unless prepaid
+          return @sendForbiddenError(res) unless prepaid.get('maxRedeemers') > prepaid.get('redeemers').length
+          for email in req.body.emails
+            context =
+              email_id: sendwithus.templates.course_invite_email
+              recipient:
+                address: email
+              subject: course.get('name')
+              email_data:
+                class_name: course.get('name')
+                join_link: "https://codecombat.com/courses/students?_ppc=" + prepaid.get('code')
+            sendwithus.api.send context, _.noop
+          return @sendSuccess(res, {})
 
   redeemPrepaidCodeAPI: (req, res) ->
     return @sendUnauthorizedError(res) if not req.user? or req.user?.isAnonymous()
@@ -142,6 +156,9 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
         return @sendDatabaseError(res, err) if err
         return @sendForbiddenError(res) if prepaid.get('redeemers')?.length >= prepaid.get('maxRedeemers')
 
+        if _.find((prepaid.get('redeemers') ? []), (a) -> a.userID.equals(req.user.id))
+          return @sendSuccess(res, courseInstances)
+
         # Add to prepaid redeemers
         query =
           _id: prepaid.get('_id')
diff --git a/server/prepaids/Prepaid.coffee b/server/prepaids/Prepaid.coffee
index 3461dc32c..c56a60839 100644
--- a/server/prepaids/Prepaid.coffee
+++ b/server/prepaids/Prepaid.coffee
@@ -6,7 +6,7 @@ PrepaidSchema.index({code: 1}, { unique: true })
 
 PrepaidSchema.statics.generateNewCode = (done) ->
   tryCode = ->
-    code = _.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
+    code = _.sample("abcdefghijklmnopqrstuvwxyz0123456789", 8).join('')
     Prepaid.findOne code: code, (err, prepaid) ->
       return done() if err
       return done(code) unless prepaid
diff --git a/test/server/functional/course_instance.spec.coffee b/test/server/functional/course_instance.spec.coffee
index 5084f48c5..ff6506a01 100644
--- a/test/server/functional/course_instance.spec.coffee
+++ b/test/server/functional/course_instance.spec.coffee
@@ -222,7 +222,7 @@ describe 'CourseInstance', ->
 
   describe 'Redeem prepaid code', ->
 
-    it 'Redeem prepaid code an instance of max 2', (done) ->
+    it 'Redeem prepaid code for an instance of max 2', (done) ->
       stripe.tokens.create {
         card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
       }, (err, token) ->
@@ -264,7 +264,7 @@ describe 'CourseInstance', ->
                         expect(usersFound).toEqual(2)
                         done()
 
-    it 'Redeem full prepaid code on instance of max 1', (done) ->
+    it 'Redeem full prepaid code for on instance of max 1', (done) ->
       stripe.tokens.create {
         card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
       }, (err, token) ->
@@ -326,3 +326,29 @@ describe 'CourseInstance', ->
                     return done(err) if err
                     expect(prepaid.get('redeemers')?.length).toEqual(prepaid.get('maxRedeemers'))
                     done()
+
+    it 'Redeem prepaid code twice', (done) ->
+      stripe.tokens.create {
+        card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
+      }, (err, token) ->
+        loginNewUser (user1) ->
+          createCourse 0, (err, course) ->
+            expect(err).toBeNull()
+            return done(err) if err
+            createCourseInstances user1, course.get('_id'), 2, token.id, (err, courseInstances) ->
+              expect(err).toBeNull()
+              return done(err) if err
+              expect(courseInstances.length).toEqual(1)
+              Prepaid.findById courseInstances[0].get('prepaidID'), (err, prepaid) ->
+                expect(err).toBeNull()
+                return done(err) if err
+                loginNewUser (user2) ->
+                  # Redeem once
+                  request.post {uri: courseInstanceRedeemURL, json: {prepaidCode: prepaid.get('code')} }, (err, res) ->
+                    expect(err).toBeNull()
+                    expect(res.statusCode).toBe(200)
+                    # Redeem twice
+                    request.post {uri: courseInstanceRedeemURL, json: {prepaidCode: prepaid.get('code')} }, (err, res) ->
+                      expect(err).toBeNull()
+                      expect(res.statusCode).toBe(200)
+                      done()