Просмотр исходного кода

Merge pull request #852 from sharelatex/jel-plans-pricing-v2

Add v2 Plans and Pricing
Jessica Lawshe 8 лет назад
Родитель
Сommit
e7b3d31acb

+ 26 - 23
services/web/app/coffee/Features/Subscription/SubscriptionController.coffee

@@ -15,30 +15,33 @@ planFeatures = require './planFeatures'
 module.exports = SubscriptionController =
 module.exports = SubscriptionController =
 
 
 	plansPage: (req, res, next) ->
 	plansPage: (req, res, next) ->
-		plans = SubscriptionViewModelBuilder.buildViewModel()
-		viewName = "subscriptions/plans"
-		if req.query.v?
-			viewName = "#{viewName}_#{req.query.v}"
-		logger.log viewName:viewName, "showing plans page"
-		currentUser = null
-
-		GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
-			return next(err) if err?
-			render = () ->
-				res.render viewName,
-					title: "plans_and_pricing"
-					plans: plans
-					gaExperiments: Settings.gaExperiments.plansPage
-					recomendedCurrency:recomendedCurrency
-					planFeatures: planFeatures
-			user_id = AuthenticationController.getLoggedInUserId(req)
-			if user_id?
-				UserGetter.getUser user_id, {signUpDate: 1}, (err, user) ->
-					return next(err) if err?
-					currentUser = user
+		if Settings.overleaf? && !req.query.plns
+			res.redirect "#{Settings.overleaf.host}/plans"
+		else
+			plans = SubscriptionViewModelBuilder.buildViewModel()
+			viewName = "subscriptions/plans"
+			if req.query.v?
+				viewName = "#{viewName}_#{req.query.v}"
+			logger.log viewName:viewName, "showing plans page"
+			currentUser = null
+
+			GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)->
+				return next(err) if err?
+				render = () ->
+					res.render viewName,
+						title: "plans_and_pricing"
+						plans: plans
+						gaExperiments: Settings.gaExperiments.plansPage
+						recomendedCurrency:recomendedCurrency
+						planFeatures: planFeatures
+				user_id = AuthenticationController.getLoggedInUserId(req)
+				if user_id?
+					UserGetter.getUser user_id, {signUpDate: 1}, (err, user) ->
+						return next(err) if err?
+						currentUser = user
+						render()
+				else
 					render()
 					render()
-			else
-				render()
 
 
 	#get to show the recurly.js page
 	#get to show the recurly.js page
 	paymentPage: (req, res, next) ->
 	paymentPage: (req, res, next) ->

+ 1 - 5
services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee

@@ -9,11 +9,7 @@ module.exports =
 	apply: (webRouter, privateApiRouter, publicApiRouter) ->
 	apply: (webRouter, privateApiRouter, publicApiRouter) ->
 		return unless Settings.enableSubscriptions
 		return unless Settings.enableSubscriptions
 
 
-		if Settings.overleaf?
-			webRouter.get  '/user/subscription/plans', (req, res) ->
-				res.redirect "#{Settings.overleaf.host}/plans"
-		else
-			webRouter.get  '/user/subscription/plans', SubscriptionController.plansPage
+		webRouter.get  '/user/subscription/plans', SubscriptionController.plansPage
 
 
 		webRouter.get  '/user/subscription',            AuthenticationController.requireLogin(), SubscriptionController.userSubscriptionPage
 		webRouter.get  '/user/subscription',            AuthenticationController.requireLogin(), SubscriptionController.userSubscriptionPage
 
 

+ 14 - 4
services/web/app/views/subscriptions/_plans_faq.pug

@@ -6,7 +6,7 @@
 	.row
 	.row
 		.col-md-6
 		.col-md-6
 			h3 #{translate("faq_how_free_trial_works_question")}
 			h3 #{translate("faq_how_free_trial_works_question")}
-			p #{translate('faq_how_free_trial_works_answer', { len:'{{trial_len}}' })}
+			p #{translate('faq_how_does_free_trial_works_answer', { appName:'{{settings.appName}}', len:'{{trial_len}}' })}
 		.col-md-6
 		.col-md-6
 			h3 #{translate('faq_change_plans_question')}
 			h3 #{translate('faq_change_plans_question')}
 			p #{translate('faq_change_plans_answer')}
 			p #{translate('faq_change_plans_answer')}
@@ -16,11 +16,18 @@
 			p #{translate('faq_do_collab_need_premium_answer')}
 			p #{translate('faq_do_collab_need_premium_answer')}
 		.col-md-6
 		.col-md-6
 			h3 #{translate('faq_need_more_collab_question')}
 			h3 #{translate('faq_need_more_collab_question')}
-			p !{translate('faq_need_more_collab_answer', { referFriendsLink: '<a href="/user/bonus">' + translate('referring_your_friends') + '</a>'})}
+			if settings.overleaf
+				p !{translate('faq_need_more_collab_answer', { referFriendsLink: translate('referring_your_friends') })}
+			else
+				p !{translate('faq_need_more_collab_answer', { referFriendsLink: '<a href="/user/bonus">' + translate('referring_your_friends') + '</a>'})}
 	.row
 	.row
 		.col-md-6
 		.col-md-6
 			h3 #{translate('faq_purchase_more_licenses_question')}
 			h3 #{translate('faq_purchase_more_licenses_question')}
-			p !{translate('faq_purchase_more_licenses_answer', { groupLink: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}
+			if settings.overleaf
+				p !{translate('faq_purchase_more_licenses_answer', { groupLink: translate('discounted_group_accounts') })}&nbsp;
+					a(href, ng-click="openGroupPlanModal()") #{translate("get_in_touch_for_details")}
+			else
+				p !{translate('faq_purchase_more_licenses_answer', { groupLink: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}
 		.col-md-6
 		.col-md-6
 			h3 #{translate('faq_monthly_or_annual_question')}
 			h3 #{translate('faq_monthly_or_annual_question')}
 			p #{translate('faq_monthly_or_annual_answer')}
 			p #{translate('faq_monthly_or_annual_answer')}
@@ -30,4 +37,7 @@
 			p #{translate('faq_how_to_pay_answer')}
 			p #{translate('faq_how_to_pay_answer')}
 		.col-md-6
 		.col-md-6
 			h3 #{translate('faq_pay_by_invoice_question')}
 			h3 #{translate('faq_pay_by_invoice_question')}
-			p !{translate('faq_pay_by_invoice_answer', { groupLink: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}
+			if settings.overleaf
+				p !{translate('faq_pay_by_invoice_answer', { groupLink: translate('discounted_group_accounts') })}
+			else
+				p !{translate('faq_pay_by_invoice_answer', { groupLink: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}

+ 10 - 5
services/web/app/views/subscriptions/_plans_page_mixins.pug

@@ -1,20 +1,23 @@
 //- Buy Buttons
 //- Buy Buttons
 mixin btn_buy_collaborator(location)
 mixin btn_buy_collaborator(location)
-	a.btn.btn-info(
+	a.btn(
+		class=settings.overleaf ? 'btn-primary' : 'btn-info'
 		ng-href="/user/subscription/new?planCode={{ getCollaboratorPlanCode() }}&currency={{currencyCode}}",
 		ng-href="/user/subscription/new?planCode={{ getCollaboratorPlanCode() }}&currency={{currencyCode}}",
 		ng-click="signUpNowClicked('collaborator','" + location + "')"
 		ng-click="signUpNowClicked('collaborator','" + location + "')"
 	)
 	)
 		span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
 		span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")}
 		span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
 		span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
 mixin btn_buy_free(location)
 mixin btn_buy_free(location)
-	a.btn.btn-info(
+	a.btn(
+		class=settings.overleaf ? 'btn-primary' : 'btn-info'
 		href="/register"
 		href="/register"
 		style=(getLoggedInUserId() === null ? "" : "visibility: hidden")
 		style=(getLoggedInUserId() === null ? "" : "visibility: hidden")
 		ng-click="signUpNowClicked('free','" + location + "')"
 		ng-click="signUpNowClicked('free','" + location + "')"
 	)
 	)
 		span.text-capitalize #{translate('get_started_now')}
 		span.text-capitalize #{translate('get_started_now')}
 mixin btn_buy_professional(location)
 mixin btn_buy_professional(location)
-	a.btn.btn-info(
+	a.btn(
+		class=settings.overleaf ? 'btn-primary' : 'btn-info'
 		ng-href="/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}&currency={{currencyCode}}"
 		ng-href="/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}&currency={{currencyCode}}"
 		ng-click="signUpNowClicked('professional','" + location + "')"
 		ng-click="signUpNowClicked('professional','" + location + "')"
 	)
 	)
@@ -22,13 +25,15 @@ mixin btn_buy_professional(location)
 		span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
 		span(ng-show="ui.view == 'annual'") #{translate("buy_now")}
 mixin btn_buy_student(location, plan)
 mixin btn_buy_student(location, plan)
 	if plan == 'annual'
 	if plan == 'annual'
-		a.btn.btn-info(
+		a.btn(
+			class=settings.overleaf ? 'btn-primary' : 'btn-info'
 			ng-href="/user/subscription/new?planCode=student-annual&currency={{currencyCode}}",
 			ng-href="/user/subscription/new?planCode=student-annual&currency={{currencyCode}}",
 			ng-click="signUpNowClicked('student-annual','" + location + "')"
 			ng-click="signUpNowClicked('student-annual','" + location + "')"
 		) #{translate("buy_now")}
 		) #{translate("buy_now")}
 	else
 	else
 		//- planQueryString will contain _free_trial_7_days
 		//- planQueryString will contain _free_trial_7_days
-		a.btn.btn-info(
+		a.btn(
+			class=settings.overleaf ? 'btn-primary' : 'btn-info'
 			ng-href="/user/subscription/new?planCode=student{{planQueryString}}&currency={{currencyCode}}",
 			ng-href="/user/subscription/new?planCode=student{{planQueryString}}&currency={{currencyCode}}",
 			ng-click="signUpNowClicked('student-monthly','" + location + "')"
 			ng-click="signUpNowClicked('student-monthly','" + location + "')"
 		) #{translate("start_free_trial")}
 		) #{translate("start_free_trial")}

+ 25 - 0
services/web/app/views/subscriptions/_plans_quotes.pug

@@ -0,0 +1,25 @@
+.row.row-spaced-large
+	.col-md-12
+		.page-header.plans-header.plans-subheader.text-centered
+			h2 #{translate('in_good_company')}
+.row
+	.col-md-6
+		div
+			.row
+				.col-md-3
+					.circle-img
+						img(src=buildImgPath('advocates/erdogmus.jpg') alt="Professor Erdogmus")
+				.col-md-9
+					blockquote
+						p The ability to track changes and the real-time collaborative nature is what sets ShareLaTeX apart.
+						footer Professor Erdogmus, Northeastern University
+	.col-md-6
+		div
+			.row
+				.col-md-3
+					.circle-img
+						img(src=buildImgPath('advocates/henderson.jpg') alt="Rob Henderson")
+				.col-md-9
+					blockquote
+						p ShareLaTeX has proven to be a powerful and robust collaboration tool that is widely used in our School.
+						footer Rob Henderson, School Of Informatics And Computing - Indiana University

+ 8 - 7
services/web/app/views/subscriptions/new.pug

@@ -14,7 +14,7 @@ block content
 		.container(ng-controller="NewSubscriptionController" ng-cloak)
 		.container(ng-controller="NewSubscriptionController" ng-cloak)
 			.row.card-group
 			.row.card-group
 				.col-md-5.col-md-push-4
 				.col-md-5.col-md-push-4
-					.card.card-highlighted
+					.card.card-highlighted(class=settings.overleaf ? 'card-border' : '')
 						.page-header
 						.page-header
 							.row
 							.row
 								.col-xs-9
 								.col-xs-9
@@ -250,12 +250,13 @@ block content
 							hr
 							hr
 								
 								
 							p.small.text-center We're confident that you'll love #{settings.appName}, but if not you can cancel anytime. We'll give you your money back, no questions asked, if you let us know within 30 days.
 							p.small.text-center We're confident that you'll love #{settings.appName}, but if not you can cancel anytime. We'll give you your money back, no questions asked, if you let us know within 30 days.
-							hr
-							span &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;
-							a(href="https://www.positivessl.com" style="font-family: arial; font-size: 10px; color: #212121; text-decoration: none;")
-								img(src="https://www.positivessl.com/images-new/PositiveSSL_tl_trans.png" alt="SSL Certificate" title="SSL Certificate" border="0")
-								div(style="font-family: arial;font-weight:bold;font-size:15px;color:#86BEE0;")
-									a(href="https://www.positivessl.com" style="color:#86BEE0; text-decoration: none;")
+							if !settings.overleaf
+								hr
+								span &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;  &nbsp;
+								a(href="https://www.positivessl.com" style="font-family: arial; font-size: 10px; color: #212121; text-decoration: none;")
+									img(src="https://www.positivessl.com/images-new/PositiveSSL_tl_trans.png" alt="SSL Certificate" title="SSL Certificate" border="0")
+									div(style="font-family: arial;font-weight:bold;font-size:15px;color:#86BEE0;")
+										a(href="https://www.positivessl.com" style="color:#86BEE0; text-decoration: none;")
 
 
 
 
 	script(type="text/javascript").
 	script(type="text/javascript").

+ 16 - 28
services/web/app/views/subscriptions/plans.pug

@@ -15,7 +15,7 @@ block content
 				.row
 				.row
 					.col-md-12
 					.col-md-12
 						.page-header.centered.plans-header.text-centered
 						.page-header.centered.plans-header.text-centered
-							h1.text-capitalize #{translate('instant_access')}
+							h1.text-capitalize #{translate('get_instant_access_to')} #{settings.appName}
 				.row
 				.row
 					.col-md-8.col-md-offset-2
 					.col-md-8.col-md-offset-2
 						p.text-centered #{translate("sl_benefits_plans")}
 						p.text-centered #{translate("sl_benefits_plans")}
@@ -89,7 +89,13 @@ block content
 							span #{translate('reduce_costs_group_licenses')}
 							span #{translate('reduce_costs_group_licenses')}
 							br
 							br
 							br
 							br
-							a.btn.btn-info(href="/i/university/groups") #{translate('find_out_more')}
+							if settings.overleaf
+								a.btn.btn-default(
+									href
+									ng-click="openGroupPlanModal()"
+								) #{translate('find_out_more')}
+							else
+								a.btn.btn-info(href="/i/university/groups") #{translate('find_out_more')}
 
 
 				div
 				div
 					.row.row-spaced-large
 					.row.row-spaced-large
@@ -107,31 +113,8 @@ block content
 						.col-sm-12(ng-if="ui.view == 'student'")
 						.col-sm-12(ng-if="ui.view == 'student'")
 							+table_student
 							+table_student
 
 
-					.row.row-spaced-large
-						.col-md-12
-							.page-header.plans-header.plans-subheader.text-centered
-								h2 #{translate('in_good_company')}
-					.row
-						.col-md-6
-							div
-								.row
-									.col-md-3
-										.circle-img
-											img(src=buildImgPath('advocates/erdogmus.jpg') alt="Professor Erdogmus")
-									.col-md-9
-										blockquote
-											p The ability to track changes and the real-time collaborative nature is what sets ShareLaTeX apart.
-											footer Professor Erdogmus, Northeastern University
-						.col-md-6
-							div
-								.row
-									.col-md-3
-										.circle-img
-											img(src=buildImgPath('advocates/henderson.jpg') alt="Rob Henderson")
-									.col-md-9
-										blockquote
-											p ShareLaTeX has proven to be a powerful and robust collaboration tool that is widely used in our School.
-											footer Rob Henderson, School Of Informatics And Computing - Indiana University
+					if !settings.overleaf
+						include _plans_quotes
 
 
 					include _plans_faq
 					include _plans_faq
 
 
@@ -139,8 +122,13 @@ block content
 						.col-md-12
 						.col-md-12
 							.plans-header.plans-subheader.text-centered
 							.plans-header.plans-subheader.text-centered
 								h2 #{translate('still_have_questions')}
 								h2 #{translate('still_have_questions')}
-									button.btn.btn-info.btn-header.text-capitalize(ng-controller="ContactGeneralModal" ng-click="openModal()") #{translate('get_in_touch')}
+									button.btn.btn-header.text-capitalize(
+										class=settings.overleaf ? 'btn-default' : 'btn-info'
+										ng-controller="ContactGeneralModal"
+										ng-click="openModal()"
+									) #{translate('get_in_touch')}
 									!= moduleIncludes("contactModalGeneral", locals)
 									!= moduleIncludes("contactModalGeneral", locals)
 
 
 					.row.row-spaced
 					.row.row-spaced
 
 
+	include _modal_group_inquiry

+ 14 - 0
services/web/public/stylesheets/app/plans-ol.less

@@ -0,0 +1,14 @@
+
+.plans {
+	blockquote {
+		footer{
+			/* accessibility fix */
+			color: @ol-blue-gray-3;
+		}
+	}
+	.plans-header{
+		h1, h2 {
+			color: @gray-dark;
+		}
+	}
+}

+ 34 - 19
services/web/public/stylesheets/app/plans.less

@@ -4,8 +4,9 @@
 		margin-bottom: @line-height-computed;
 		margin-bottom: @line-height-computed;
 	}
 	}
 	.best-value {
 	.best-value {
-		color: @red;
+		color: @brand-secondary;
 		line-height: @line-height-computed;
 		line-height: @line-height-computed;
+		text-transform: capitalize;
 	}
 	}
 	blockquote {
 	blockquote {
 		footer{
 		footer{
@@ -20,6 +21,22 @@
 		text-shadow: 0 0 0;
 		text-shadow: 0 0 0;
 	}
 	}
 	.card .btn { white-space:normal; }
 	.card .btn { white-space:normal; }
+	.card.features {
+		margin-top: @line-height-computed;
+		i {
+			color: @red;
+		}
+		p {
+			margin: 0;
+		}
+	}
+	.btn-header {
+		font-family: @font-family-sans-serif;
+		margin-left: 10px;
+		margin-top: -10px;
+		text-shadow: 0 0 0;
+	}
+	.card .btn { white-space:normal; }
 	.card-group {
 	.card-group {
 		.card-highlighted {
 		.card-highlighted {
 			padding-top: @line-height-computed * 2;
 			padding-top: @line-height-computed * 2;
@@ -27,10 +44,10 @@
 		}
 		}
 	}
 	}
 	.card-first, .card-last {
 	.card-first, .card-last {
-		background: @white-med;
+		background: @plans-non-highlighted;
 	}
 	}
 	.card-highlighted {
 	.card-highlighted {
-		border: @highlight-border solid @gray-lighter;
+		border: @border-width-base solid @border-color-base;
 		padding-top: 10px!important;
 		padding-top: 10px!important;
 		.best-value {
 		.best-value {
 			margin-bottom: 15px;
 			margin-bottom: 15px;
@@ -51,7 +68,7 @@
 		width: 120px;
 		width: 120px;
 		height: 120px;
 		height: 120px;
 		border-radius: 50%;
 		border-radius: 50%;
-		background-color: @red;
+		background-color: @brand-secondary;
 		color: white;
 		color: white;
 		white-space: nowrap;
 		white-space: nowrap;
 		span.small {
 		span.small {
@@ -148,10 +165,8 @@
 	Plans Test
 	Plans Test
 */
 */
 @best-val-height: 35px;
 @best-val-height: 35px;
-@highlight-border: 3px;
-@highlight-color: #d3584b;
+@border-width-base: 3px;
 @gray-med: #6d6d6d;
 @gray-med: #6d6d6d;
-@white-med: #fdfdfd;
 
 
 /* Media Queries */
 /* Media Queries */
 @media (max-width: @screen-sm-max) {
 @media (max-width: @screen-sm-max) {
@@ -192,7 +207,7 @@
 */
 */
 .plans-table {
 .plans-table {
 	border: 1px solid @gray-lighter;
 	border: 1px solid @gray-lighter;
-	background-color: @white-med;
+	background-color: @plans-non-highlighted;
 	margin: @best-val-height 0 15px 0;
 	margin: @best-val-height 0 15px 0;
 	table-layout: fixed;
 	table-layout: fixed;
 	width: 100%;
 	width: 100%;
@@ -215,8 +230,8 @@
 		border-top: 0;
 		border-top: 0;
 		font-family: @headings-font-family;
 		font-family: @headings-font-family;
 		font-size: @font-size-h2;
 		font-size: @font-size-h2;
-	  font-weight: @headings-font-weight;
-	  line-height: @headings-line-height;
+		font-weight: @headings-font-weight;
+		line-height: @headings-line-height;
 		padding: 18px;
 		padding: 18px;
 	}
 	}
 
 
@@ -246,13 +261,13 @@
 			border-bottom: 0;
 			border-bottom: 0;
 			padding: 18px;
 			padding: 18px;
 		}
 		}
-			/* highlighted column */
+		/* highlighted column */
 		td:nth-child(3) {
 		td:nth-child(3) {
 			position: relative;
 			position: relative;
 			/* keep here position here, otherwise messes up border on safari when there is a bg color */
 			/* keep here position here, otherwise messes up border on safari when there is a bg color */
 			&:before {
 			&:before {
 				/* needed for safafi */
 				/* needed for safafi */
-				border-top: 1px solid @gray-lighter;
+				border-top: 1px solid @border-color-base;
 				content: '';
 				content: '';
 				left: 0;
 				left: 0;
 				position: absolute;
 				position: absolute;
@@ -277,17 +292,17 @@
 	/* highlighted column */
 	/* highlighted column */
 	td:nth-child(3), th:nth-child(3) {
 	td:nth-child(3), th:nth-child(3) {
 		background-color: white;
 		background-color: white;
-		border-left: @highlight-border solid @gray-lighter;
-		border-right: @highlight-border solid @gray-lighter;
+		border-left: @border-width-base solid @border-color-base;
+		border-right: @border-width-base solid @border-color-base;
 	}
 	}
 	.outer {
 	.outer {
-		left: -@highlight-border;
-		right: -@highlight-border;
+		left: -@border-width-base;
+		right: -@border-width-base;
 		position: absolute;
 		position: absolute;
 
 
 		.outer-content {
 		.outer-content {
 			background: white;
 			background: white;
-			border: @highlight-border solid @gray-lighter;
+			border: @border-width-base solid @border-color-base;
 			border-radius: @border-radius-base;
 			border-radius: @border-radius-base;
 			font-size: @font-size-base;
 			font-size: @font-size-base;
 			font-family: @font-family-sans-serif;
 			font-family: @font-family-sans-serif;
@@ -317,7 +332,7 @@
 	/* highlight rows on hover */
 	/* highlight rows on hover */
 	tr:hover {
 	tr:hover {
 		td {
 		td {
-			background-color: @gray-lightest;
+			background-color: @table-hover-bg;
 		}
 		}
 	}
 	}
 	tr:first-child:hover {
 	tr:first-child:hover {
@@ -332,7 +347,7 @@
 
 
 	/* tooltip */
 	/* tooltip */
 	sup {
 	sup {
-		color: @red;
+		color: @brand-secondary;
 		cursor: pointer;
 		cursor: pointer;
 		margin-left: 5px;
 		margin-left: 5px;
 	}
 	}

+ 5 - 5
services/web/public/stylesheets/app/subscription.less

@@ -44,16 +44,16 @@
 		border: solid 1px @gray-lighter;
 		border: solid 1px @gray-lighter;
 		border-radius: @border-radius-large 0 0 @border-radius-large;
 		border-radius: @border-radius-large 0 0 @border-radius-large;
 		padding: (@line-height-computed / 2);
 		padding: (@line-height-computed / 2);
-		color: @gray;
+		color: @btn-switch-color;
 
 
 		&:hover,
 		&:hover,
 		&:focus {
 		&:focus {
-			color: @gray;
+			color: @btn-switch-color;
 			text-decoration: none;
 			text-decoration: none;
 		}
 		}
 
 
 		&:hover {
 		&:hover {
-			color: @gray-dark;
+			color: @btn-switch-hover-color;
 		}
 		}
 
 
 		& + & {
 		& + & {
@@ -62,12 +62,12 @@
 		}
 		}
 
 
 		&-selected {
 		&-selected {
-			color: @link-color;
+			color: @link-active-color;
 			box-shadow: inset 0 -2px 0 0;
 			box-shadow: inset 0 -2px 0 0;
 
 
 			&:hover,
 			&:hover,
 			&:focus {
 			&:focus {
-				color: @link-color;
+				color: @link-active-color;
 			}
 			}
 		}
 		}
 	}
 	}

+ 4 - 0
services/web/public/stylesheets/components/card.less

@@ -50,4 +50,8 @@
             border-bottom-right-radius: @border-radius-base;
             border-bottom-right-radius: @border-radius-base;
         }
         }
     }
     }
+}
+
+.card-border {
+  border: @border-width-base solid @border-color-base;
 }
 }

+ 7 - 0
services/web/public/stylesheets/components/navs-ol.less

@@ -0,0 +1,7 @@
+.nav-pills {
+  > li {
+    > a {
+      border-radius: @btn-border-radius-base;
+    }
+  }
+}

+ 9 - 2
services/web/public/stylesheets/components/navs.less

@@ -123,8 +123,14 @@
     // Links rendered as pills
     // Links rendered as pills
     > a {
     > a {
       border-radius: @nav-pills-border-radius;
       border-radius: @nav-pills-border-radius;
-      border: 2px solid @link-color;
+      border: 2px solid @nav-pills-link-color;
+      color: @nav-pills-link-color;
       padding: 8px 13px;
       padding: 8px 13px;
+      &:hover,
+      &:focus {
+        background-color: @nav-pills-link-hover-bg;
+        border: 2px solid @nav-pills-link-hover-bg;
+      }
     }
     }
     + li {
     + li {
       margin-left: 2px;
       margin-left: 2px;
@@ -136,6 +142,7 @@
       &:hover,
       &:hover,
       &:focus {
       &:focus {
         color: @nav-pills-active-link-hover-color;
         color: @nav-pills-active-link-hover-color;
+        border: 2px solid @nav-pills-active-link-hover-bg;
         background-color: @nav-pills-active-link-hover-bg;
         background-color: @nav-pills-active-link-hover-bg;
       }
       }
     }
     }
@@ -247,4 +254,4 @@
   margin-top: -1px;
   margin-top: -1px;
   // Remove the top rounded corners here since there is a hard edge above the menu
   // Remove the top rounded corners here since there is a hard edge above the menu
   .border-top-radius(0);
   .border-top-radius(0);
-}
+}

+ 14 - 0
services/web/public/stylesheets/core/_common-variables.less

@@ -13,6 +13,7 @@
 
 
 //** Global textual link color.
 //** Global textual link color.
 @link-color:            @brand-primary;
 @link-color:            @brand-primary;
+@link-active-color:     @link-color;
 //** Link hover color set via `darken()` function.
 //** Link hover color set via `darken()` function.
 @link-hover-color:      darken(@link-color, 15%);
 @link-hover-color:      darken(@link-color, 15%);
 
 
@@ -87,6 +88,12 @@
 @border-radius-base:        3px;
 @border-radius-base:        3px;
 @border-radius-large:       5px;
 @border-radius-large:       5px;
 @border-radius-small:       2px;
 @border-radius-small:       2px;
+@border-width-base:         3px;
+@border-color-base:         @gray-lighter;
+
+@btn-switch-color:               @gray;
+@btn-switch-hover-color:         @gray-dark;
+
 //** Global color for active items (e.g., navs or dropdowns).
 //** Global color for active items (e.g., navs or dropdowns).
 @component-active-color:    #fff;
 @component-active-color:    #fff;
 //** Global background color for active items (e.g., navs or dropdowns).
 //** Global background color for active items (e.g., navs or dropdowns).
@@ -370,6 +377,9 @@
 @nav-tabs-justified-active-link-border-color:     @body-bg;
 @nav-tabs-justified-active-link-border-color:     @body-bg;
 
 
 //== Pills
 //== Pills
+@nav-pills-link-color:                      @link-color;
+@nav-pills-link-hover-color:                @nav-link-hover-bg;
+@nav-pills-link-hover-bg:                   @nav-link-hover-bg;
 @nav-pills-border-radius:                   @border-radius-base;
 @nav-pills-border-radius:                   @border-radius-base;
 @nav-pills-active-link-hover-bg:            @component-active-bg;
 @nav-pills-active-link-hover-bg:            @component-active-bg;
 @nav-pills-active-link-hover-color:         @component-active-color;
 @nav-pills-active-link-hover-color:         @component-active-color;
@@ -412,6 +422,10 @@
 @pager-disabled-color:                 @pagination-disabled-color;
 @pager-disabled-color:                 @pagination-disabled-color;
 
 
 
 
+// Plans
+@table-hover-bg:                       @gray-lightest;
+@plans-non-highlighted:                #fdfdfd;
+
 //== Jumbotron
 //== Jumbotron
 //
 //
 //##
 //##

+ 16 - 0
services/web/public/stylesheets/core/ol-variables.less

@@ -34,14 +34,22 @@
 @navbar-default-border    : transparent;
 @navbar-default-border    : transparent;
 @navbar-brand-image-url   : url(/img/ol-brand/overleaf-white.svg);
 @navbar-brand-image-url   : url(/img/ol-brand/overleaf-white.svg);
 @navbar-default-link-bg   : transparent;
 @navbar-default-link-bg   : transparent;
+@nav-pills-active-link-hover-bg: @ol-dark-green;
+@nav-pills-link-color     : @btn-default-bg;
+@nav-pills-link-hover-bg  : darken(@ol-blue-gray-4, 8%); // match button-variant mixin
+
 // Backgrounds
 // Backgrounds
 @body-bg                : #FFF;
 @body-bg                : #FFF;
 @content-alt-bg-color   : @ol-blue-gray-1;
 @content-alt-bg-color   : @ol-blue-gray-1;
 
 
+// Border
+@border-color-base:         @ol-blue-gray-2;
+
 // Typography
 // Typography
 @text-small-color       : @ol-type-color;
 @text-small-color       : @ol-type-color;
 @text-color             : @ol-type-color;
 @text-color             : @ol-type-color;
 @link-color             : @ol-blue;
 @link-color             : @ol-blue;
+@link-active-color      : @ol-dark-green;
 @link-hover-color       : @ol-dark-blue;
 @link-hover-color       : @ol-dark-blue;
 
 
 // Button colors and sizing
 // Button colors and sizing
@@ -68,6 +76,9 @@
 @btn-info-bg             : @ol-blue;
 @btn-info-bg             : @ol-blue;
 @btn-info-border         : transparent;
 @btn-info-border         : transparent;
 
 
+@btn-switch-color        : @ol-blue-gray-4;
+@btn-switch-hover-color  : darken(@ol-blue-gray-4, 8%);
+
 // Padding
 // Padding
 @padding-xs-horizontal   : 8px;
 @padding-xs-horizontal   : 8px;
 
 
@@ -308,6 +319,10 @@
 @log-line-no-color              : #FFF;
 @log-line-no-color              : #FFF;
 @log-hints-color                : @ol-blue-gray-4;
 @log-hints-color                : @ol-blue-gray-4;
 
 
+// Plans
+@table-hover-bg                 : @ol-blue-gray-0;
+@plans-non-highlighted          : white;
+
 // Portals
 // Portals
 @black-alpha-strong             : rgba(0,0,0,0.8);
 @black-alpha-strong             : rgba(0,0,0,0.8);
 
 
@@ -360,6 +375,7 @@
 @purple:                #7a43b6;
 @purple:                #7a43b6;
 
 
 @brand-primary:         @ol-green;
 @brand-primary:         @ol-green;
+@brand-secondary:       @ol-dark-green;
 @brand-success:         @green;
 @brand-success:         @green;
 @brand-info:            @ol-blue;
 @brand-info:            @ol-blue;
 @brand-warning:         @orange;
 @brand-warning:         @orange;

+ 1 - 0
services/web/public/stylesheets/core/variables.less

@@ -20,6 +20,7 @@
 @purple:                #7a43b6;
 @purple:                #7a43b6;
 
 
 @brand-primary:         @red;
 @brand-primary:         @red;
+@brand-secondary:       @red;
 @brand-success:         @green;
 @brand-success:         @green;
 @brand-info:            @blue;
 @brand-info:            @blue;
 @brand-warning:         @orange;
 @brand-warning:         @orange;

+ 2 - 0
services/web/public/stylesheets/ol-style.less

@@ -8,10 +8,12 @@
 @import "_ol_style_includes.less";
 @import "_ol_style_includes.less";
 @import "components/embed-responsive.less";
 @import "components/embed-responsive.less";
 @import "components/icons.less";
 @import "components/icons.less";
+@import "components/navs-ol.less";
 @import "components/pagination.less";
 @import "components/pagination.less";
 
 
 // Pages
 // Pages
 @import "app/about.less";
 @import "app/about.less";
 @import "app/blog-posts.less";
 @import "app/blog-posts.less";
 @import "app/cms-page.less";
 @import "app/cms-page.less";
+@import "app/plans-ol.less";
 @import "app/portals.less";
 @import "app/portals.less";