diff --git a/services/web/app/views/subscriptions/plans-light-design.pug b/services/web/app/views/subscriptions/plans-light-design.pug index df8dc4218f..3e5f87ee84 100644 --- a/services/web/app/views/subscriptions/plans-light-design.pug +++ b/services/web/app/views/subscriptions/plans-light-design.pug @@ -32,7 +32,7 @@ block content +eyebrow(translate('plans_and_pricing_lowercase')) | #{translate('choose_your_plan')} - include ./plans/_cards_controls_tables + include ./plans/light-redesign/_cards_controls_tables +currency_and_payment_methods() diff --git a/services/web/app/views/subscriptions/plans/light-redesign/_cards_controls_tables.pug b/services/web/app/views/subscriptions/plans/light-redesign/_cards_controls_tables.pug new file mode 100644 index 0000000000..042dd7aba4 --- /dev/null +++ b/services/web/app/views/subscriptions/plans/light-redesign/_cards_controls_tables.pug @@ -0,0 +1,74 @@ +include ./_mixins + +.row.plans-v2-top-switch + .col-xs-12 + ul.nav.plans-v2-nav(role="tablist") + li.active.plans-v2-top-switch-individual( + data-ol-plans-v2-view-tab='individual' + event-tracking="plans-page-toggle-plan" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"button": "individual"}' + role="presentation" + ) + button.btn.btn-default-outline(role="tab" aria-controls="panel-individual" aria-selected="true") #{translate("indvidual_plans")} + li.plans-v2-top-switch-group( + data-ol-plans-v2-view-tab='group' + event-tracking="plans-page-toggle-plan" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"button": "group"}' + role="presentation" + ) + button.btn.btn-default-outline( + aria-controls="panel-group" + href="#" + role="tab" + aria-selected="false" + ) + span #{translate("group_plans")} + span (#{translate("save_30_percent_or_more")}) + li.plans-v2-top-switch-student( + data-ol-plans-v2-view-tab='student' + event-tracking="plans-page-toggle-plan" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"button": "student"}' + role="presentation" + ) + button.btn.btn-default-outline( + aria-controls="panel-student" + href="#" + role="tab" + aria-selected="false" + ) #{translate("student_plans")} + ++monthly_annual_switch("annual", "plans-page-toggle-period") + +.row(hidden data-ol-plans-v2-license-picker-container) + .col-sm-12 + +group_plans_license_picker() + ++table_sticky_header_all(plansConfig) + +.row.plans-v2-table-container(hidden data-ol-plans-v2-period='monthly') + .col-sm-12(data-ol-plans-v2-view='individual' role="tabpanel") + .row + +table_individual('monthly') + .col-sm-12(hidden data-ol-plans-v2-view='student' role="tabpanel") + .row + +table_student('monthly') + +.row.plans-v2-table-container(data-ol-plans-v2-period='annual') + .col-sm-12(data-ol-plans-v2-view='individual' id="panel-individual" role="tabpanel") + .row + +table_individual('annual') + .col-sm-12(hidden data-ol-plans-v2-view='group' id="panel-group" role="tabpanel") + .row + +table_group('annual') + .col-sm-12(hidden data-ol-plans-v2-view='student' id="panel-student" role="tabpanel") + .row + +table_student('annual') + +//- sticky header on mobile will be "hidden" (by removing its sticky position) if it reaches this div +.invisible(aria-hidden="true" data-ol-plans-v2-table-sticky-header-stop) diff --git a/services/web/app/views/subscriptions/plans/light-redesign/_mixins.pug b/services/web/app/views/subscriptions/plans/light-redesign/_mixins.pug new file mode 100644 index 0000000000..d6abc13116 --- /dev/null +++ b/services/web/app/views/subscriptions/plans/light-redesign/_mixins.pug @@ -0,0 +1,642 @@ +mixin features_premium + li   + li + strong #{translate('all_premium_features')} + li #{translate('sync_dropbox_github')} + li #{translate('full_doc_history')} + li #{translate('track_changes')} + li + #{translate('more').toLowerCase()} + +mixin gen_localized_price_for_plan_view(plan, view) + span #{formatCurrency(settings.localizedPlanPricing[recommendedCurrency][plan][view], recommendedCurrency, language, true)} + +mixin currency_and_payment_methods() + .row.row-spaced-large.text-centered + .col-xs-12 + p.text-centered + strong #{translate("all_prices_displayed_are_in_currency", { recommendedCurrency })} + |   + span #{translate("subject_to_additional_vat")} + i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true")   + span.sr-only #{translate('payment_method_accepted', { paymentMethod: 'Mastercard' })} + i.fa.fa-cc-visa.fa-2x(aria-hidden="true")   + span.sr-only #{translate('payment_method_accepted', { paymentMethod: 'Visa' })} + i.fa.fa-cc-amex.fa-2x(aria-hidden="true")   + span.sr-only #{translate('payment_method_accepted', { paymentMethod: 'Amex' })} + i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")   + span.sr-only #{translate('payment_method_accepted', { paymentMethod: 'Paypal' })} + + +mixin plans_v2_table(period, config) + - var baseColspan = config.baseColspan || 1 + - var maxColumn = config.maxColumn || 4 + - var tableHeadKeys = Object.keys(config.tableHead) + tr(class=`plans-v2-table-cols-${tableHeadKeys.length}`) + th(colspan=baseColspan) + - for (var i = 0; i < maxColumn; i++) + - var tableHeadKey = tableHeadKeys[i] + - var tableHeadOptions = Object.values(config.tableHead)[i] || {} + - var colspan = tableHeadOptions.colspan || baseColspan + - var highlighted = i === config.highlightedColumn.index + - var eventTrackingKey = config.eventTrackingKey + - var additionalEventSegmentation = config.additionalEventSegmentation || {} + - + if (highlighted) { + var thClass = 'plans-v2-table-green-highlighted' + } else if (i === config.highlightedColumn.index - 1) { + var thClass = 'plans-v2-table-cell-before-green-highlighted-column' + } else { + var thClass = '' + } + thClass += ' plans-v2-table-column-header' + if (colspan > 1) { + var scopeValue = 'colgroup' + } + else { + var scopeValue = 'col' + } + case tableHeadKey + when 'individual_free' + - var ariaLabel = translate("free") + when 'individual_collaborator' + - var ariaLabel = translate("standard") + when 'individual_professional' + - var ariaLabel = translate("professional") + when 'group_collaborator' + - var ariaLabel = translate("group_standard") + when 'group_professional' + - var ariaLabel = translate("group_professional") + when 'group_organization' + - var ariaLabel = translate("organization") + when 'student_free' + - var ariaLabel = translate("free") + when 'student_student' + - var ariaLabel = translate("student") + when 'student_university' + - var ariaLabel = translate("university") + default + - var ariaLabel = undefined + th( + aria-label=ariaLabel + class=thClass + colspan=colspan + scope=scopeValue + ) + .plans-v2-table-th + if (highlighted) + p.plans-v2-table-green-highlighted-text #{translate(config.highlightedColumn.text[period]).toUpperCase()} + case tableHeadKey + when 'individual_free' + +table_head_individual_free(highlighted, period) + when 'individual_collaborator' + +table_head_individual_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation, period) + when 'individual_professional' + +table_head_individual_professional(highlighted, eventTrackingKey, additionalEventSegmentation, period) + when 'group_collaborator' + +table_head_group_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation) + when 'group_professional' + +table_head_group_professional(highlighted, eventTrackingKey, additionalEventSegmentation) + when 'group_organization' + +table_head_group_organization(highlighted, eventTrackingKey, additionalEventSegmentation) + when 'student_free' + +table_head_student_free(highlighted, period) + when 'student_student' + +table_head_student_student(highlighted, eventTrackingKey, additionalEventSegmentation, period, tableHeadOptions.showExtraContent) + when 'student_university' + +table_head_student_university(highlighted, eventTrackingKey, additionalEventSegmentation, period) + + for featuresPerSection in config.features + - var dividerColspan = Object.values(config.tableHead).reduce((prev, curr) => (prev) + (curr.colspan || 1), baseColspan) + if featuresPerSection.divider + tr.plans-v2-table-divider + td( + colspan=dividerColspan + class=((config.highlightedColumn.index === Object.keys(config.tableHead).length - 1) ? 'plans-v2-table-divider-highlighted' : '') + ) + div + b.plans-v2-table-divider-label #{translate(featuresPerSection.dividerLabel)} + //- will only appear on screen width >= 768px (using CSS) + i.fa.fa-question-circle.plans-v2-table-divider-question-icon( + data-toggle="tooltip" + title=translate(featuresPerSection.dividerInfo), + data-placement="top" + ) + //- will only appear on screen width < 768px (using CSS) + span.plans-v2-table-divider-learn-more-container + span ( + span.plans-v2-table-divider-learn-more-text( + data-toggle="tooltip" + title=translate(featuresPerSection.dividerInfo), + data-placement="top" + ) #{translate("learn_more_lowercase")} + span ) + span.sr-only #{translate(featuresPerSection.dividerInfo)} + for feature, featureIndex in featuresPerSection.items + tr( + class=(featureIndex === (featuresPerSection.items.length - 1) ? `plans-v2-table-row-last-row-per-section plans-v2-table-cols-${tableHeadKeys.length}` : `plans-v2-table-cols-${tableHeadKeys.length}`) + ) + th( + class="plans-v2-table-row-header" + event-tracking="plans-page-table" + event-tracking-trigger="hover" + event-tracking-ga="subscription-funnel" + event-tracking-label=`${feature.feature}` + colspan=baseColspan + scope="row" + ) + .plans-v2-table-feature-name + if feature.info + span #{translate(feature.feature)} + //- will only appear on screen width >= 768px (using CSS) + i.fa.fa-question-circle.plans-v2-table-feature-name-question-icon( + data-toggle="tooltip" + title=translate(feature.info), + data-placement="right" + ) + //- will only appear on screen width < 768px (using CSS) + span.plans-v2-table-feature-name-learn-more-container + span ( + span.plans-v2-table-feature-name-learn-more-text( + data-toggle="tooltip" + title=translate(feature.info), + data-placement="top" + ) #{translate("learn_more_lowercase")} + span ) + span.sr-only #{translate(feature.info)} + else + | #{translate(feature.feature)} + for plan, planIndex in Object.keys(feature.plans) + - var tableHeadOptions = Object.values(config.tableHead)[planIndex] || {} + - var colspan = tableHeadOptions.colspan || baseColspan + - + if (planIndex === config.highlightedColumn.index) { + var tdClass = 'plans-v2-table-green-highlighted' + } else if (planIndex === config.highlightedColumn.index - 1) { + var tdClass = 'plans-v2-table-cell-before-green-highlighted-column' + } else { + var tdClass = '' + } + td( + class=tdClass + colspan=colspan + ) + +table_cell(feature, plan) + +mixin table_individual(period) + table.card.plans-v2-table.plans-v2-table-individual + +plans_v2_table(period, plansConfig.individual) + +mixin table_group + table.card.plans-v2-table.plans-v2-table-group + +plans_v2_table('annual', plansConfig.group) + +mixin table_student(period) + table.card.plans-v2-table.plans-v2-table-student + +plans_v2_table(period, plansConfig.student) + +mixin table_head_individual_free(highlighted, period) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("free")} + +table_head_price('free', period) + .plans-v2-table-btn-buy-container-mobile + +btn_buy_individual_free(highlighted) + ul.plans-v2-table-th-content-benefit + li #{translate("one_collaborator")} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_individual_free(highlighted) + +mixin table_head_individual_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation, period) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("standard")} + +table_head_price('collaborator', period) + .plans-v2-table-btn-buy-container-mobile + +btn_buy_individual_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation, period) + ul.plans-v2-table-th-content-benefit + li !{translate("x_collaborators_per_project", {collaboratorsCount: '10'})} + li #{translate("all_premium_features")} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_individual_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +mixin table_head_individual_professional(highlighted, eventTrackingKey, additionalEventSegmentation, period) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("professional")} + +table_head_price('professional', period) + .plans-v2-table-btn-buy-container-mobile + +btn_buy_individual_professional(highlighted, eventTrackingKey, additionalEventSegmentation, period) + ul.plans-v2-table-th-content-benefit + li !{translate("unlimited_collabs_rt",{},["b"])} + li #{translate("all_premium_features")} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_individual_professional(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +mixin table_head_group_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("group_standard")} + .plans-v2-table-price-container + strike.plans-v2-table-price-before-discount + +gen_localized_price_for_plan_view('collaborator', 'annual') + p.plans-v2-table-price + span(data-ol-plans-v2-group-price-per-user='collaborator') #{initialLocalizedGroupPrice.pricePerUser.collaborator} + p.plans-v2-table-price-period-label + | #{translate('per_user_year')} + .plans-v2-table-btn-buy-container-mobile + +btn_buy_group_collaborator(highlighted, eventTrackingKey) + +additional_link_group(eventTrackingKey, additionalEventSegmentation, 'group_collaborator') + ul.plans-v2-table-th-content-benefit + li #{translate("up_to")} !{translate("x_collaborators_per_project", {collaboratorsCount: '10'})} + li + +table_head_group_total_per_year('collaborator') + .plans-v2-table-btn-buy-container-desktop + +btn_buy_group_collaborator(highlighted, eventTrackingKey) + +additional_link_group(eventTrackingKey, additionalEventSegmentation, 'group_collaborator') + +mixin table_head_group_professional(highlighted, eventTrackingKey, additionalEventSegmentation) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("group_professional")} + .plans-v2-table-price-container + strike.plans-v2-table-price-before-discount + +gen_localized_price_for_plan_view('professional', 'annual') + p.plans-v2-table-price + span(data-ol-plans-v2-group-price-per-user='professional') #{initialLocalizedGroupPrice.pricePerUser.professional} + p.plans-v2-table-price-period-label + | #{translate('per_user_year')} + .plans-v2-table-btn-buy-container-mobile + +btn_buy_group_professional(highlighted, eventTrackingKey) + +additional_link_group(eventTrackingKey, additionalEventSegmentation, 'group_professional') + ul.plans-v2-table-th-content-benefit + li #{translate("unlimited_collaborators_in_each_project")} + li + +table_head_group_total_per_year('professional') + .plans-v2-table-btn-buy-container-desktop + +btn_buy_group_professional(highlighted, eventTrackingKey) + +additional_link_group(eventTrackingKey, additionalEventSegmentation, 'group_professional') + +mixin table_head_group_total_per_year(groupPlan) + - var initialLicenseSize = '2' + span.plans-v2-group-total-price(data-ol-plans-v2-group-total-price=groupPlan) #{initialLocalizedGroupPrice.price[groupPlan]} + |   + for licenseSize in groupPlanModalOptions.sizes + span( + hidden=(licenseSize !== initialLicenseSize) + data-ol-plans-v2-table-th-group-license-size=licenseSize + ) !{translate("total_per_year_for_x_users", {licenseSize})} + +mixin table_head_group_organization(highlighted, eventTrackingKey, additionalEventSegmentation) + - var segmentation = JSON.stringify(Object.assign({}, {button: 'group_organization-link', location: 'table-header-list', period: 'annual'}, additionalEventSegmentation)) + .plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("organization")} + .plans-v2-table-comments-icon + i.fa.fa-comments-o + .plans-v2-table-btn-buy-container-mobile + +btn_buy_group_organization(highlighted, eventTrackingKey) + small.plans-v2-table-th-content-additional-link.invisible(aria-hidden="true") + ul.plans-v2-table-th-content-benefit + li #{translate("best_choices_companies_universities_non_profits")} + li #{translate("for_groups_or_site_wide")} + li + a( + target="_blank" + href="/for/contact-sales" + event-tracking="plans-page-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation=segmentation + ) #{translate("also_available_as_on_premises")} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_group_organization(highlighted, eventTrackingKey) + small.plans-v2-table-th-content-additional-link.invisible(aria-hidden="true") + +mixin table_head_student_free(highlighted, period) + div.plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("free")} + +table_head_price('free', period) + .plans-v2-table-btn-buy-container-mobile + +btn_buy_student_free(highlighted) + ul.plans-v2-table-th-content-benefit + li #{translate("one_collaborator")} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_student_free(highlighted) + +mixin table_head_student_student(highlighted, eventTrackingKey, additionalEventSegmentation, period, showExtraContent) + div.plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("student")} + +table_head_price('student', period) + .plans-v2-table-btn-buy-container-mobile + +btn_buy_student_student(highlighted, eventTrackingKey, additionalEventSegmentation, period) + ul.plans-v2-table-th-content-benefit + li !{translate("x_collaborators_per_project", {collaboratorsCount: '6'})} + li #{translate("all_premium_features")} + if showExtraContent + li + b !{translate("for_students_only")} + + .plans-v2-table-btn-buy-container-desktop + +btn_buy_student_student(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +mixin table_head_student_university(highlighted, eventTrackingKey, additionalEventSegmentation, period) + div.plans-v2-table-th-content + p.plans-v2-table-th-content-title #{translate("university")} + div.plans-v2-table-comments-icon + i.fa.fa-comments-o + .plans-v2-table-btn-buy-container-mobile + +btn_buy_student_university(highlighted, eventTrackingKey, additionalEventSegmentation, period) + p.plans-v2-table-th-content-benefit !{translate("all_our_group_plans_offer_educational_discount", {}, [{name: 'b'}, {name: 'b'}])} + .plans-v2-table-btn-buy-container-desktop + +btn_buy_student_university(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +mixin table_head_price(plan, period) + div.plans-v2-table-price-container + if plan !== 'free' && period === 'annual' + strike.plans-v2-table-price-before-discount + +gen_localized_price_for_plan_view(plan, 'monthlyTimesTwelve') + p.plans-v2-table-price + +gen_localized_price_for_plan_view(plan, period) + p.plans-v2-table-price-period-label + if period == 'annual' + | #{translate("per_year")} + else + | #{translate("per_month")} + +mixin table_cell(feature, plan) + - var planValue = feature.plans[plan] + - var featureName = feature.feature + + .plans-v2-table-cell + .plans-v2-table-cell-content( + data-ol-plans-v2-table-cell-plan=plan + data-ol-plans-v2-table-cell-feature=featureName + ) + if (feature.value === 'str') + | !{translate(planValue, {}, ['strong'])} + else if (feature.value === 'bool') + if (planValue) + i.fa.fa-check(aria-hidden="true") + span.sr-only #{translate("feature_included")} + else + span(aria-hidden="true") - + span.sr-only #{translate("feature_not_included")} + +mixin group_plans_license_picker() + form.plans-v2-license-picker-form(data-ol-plans-v2-license-picker-form) + .plans-v2-license-picker-select-container + span #{translate("number_of_users_with_colon")} + select.plans-v2-license-picker-select( + name="plans-v2-license-picker-select" + id="plans-v2-license-picker-select" + autocomplete="off" + data-ol-plans-v2-license-picker-select + event-tracking="plans-page-group-size" + event-tracking-mb="true" + event-tracking-trigger="click" + event-tracking-element="select" + ) + option(value="2") 2 + option(value="3") 3 + option(value="4") 4 + option(value="5") 5 + option(value="10") 10 + option(value="20") 20 + option(value="50") 50 + .plans-v2-license-picker-educational-discount + label.plans-v2-license-picker-educational-discount-label(data-ol-plans-v2-license-picker-educational-discount-label) + input.plans-v2-license-picker-educational-discount-checkbox( + type="checkbox" + id="license-picker-educational-discount" + autocomplete="off" + data-ol-plans-v2-license-picker-educational-discount-input + event-tracking="plans-page-edu-discount" + event-tracking-mb="true" + event-tracking-trigger="click" + event-tracking-element="checkbox" + ) + span #{translate("apply_educational_discount")} + //- will only appear on screen width >= 768px (using CSS) + i.fa.fa-question-circle.plans-v2-license-picker-educational-discount-question-icon( + data-toggle="tooltip" + title=translate("apply_educational_discount_info"), + data-placement="bottom" + ) + //- will only appear on screen width < 768px (using CSS) + span.plans-v2-license-picker-educational-discount-learn-more-container + span ( + span.plans-v2-license-picker-educational-discount-learn-more-text( + data-toggle="tooltip" + title=translate("apply_educational_discount_info"), + data-placement="bottom" + ) #{translate("learn_more_lowercase")} + span ) + span.sr-only #{translate("apply_educational_discount_info")} + +mixin btn_buy_individual(highlighted, eventTrackingKey, subscriptionPlan, period) + a.btn.plans-v2-table-btn-buy( + data-ol-start-new-subscription=subscriptionPlan + data-ol-event-tracking-key=eventTrackingKey + data-ol-item-view=period + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + if (period === 'monthly') + span #{translate("try_for_free")} + else + span #{translate("buy_now_no_exclamation_mark")} + +mixin btn_buy_individual_free() + if (!getSessionUser()) + a.btn.plans-v2-table-btn-buy( + href="/register" + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span #{translate("try_for_free")} + else + a.btn.plans-v2-table-btn-buy.invisible( + aria-hidden="true" + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + +mixin btn_buy_individual_collaborator(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +btn_buy_individual(highlighted, eventTrackingKey, 'collaborator', period) + if (period === 'monthly') + +additional_link_buy(eventTrackingKey, additionalEventSegmentation, 'collaborator', period) + +mixin btn_buy_individual_professional(highlighted, eventTrackingKey, additionalEventSegmentation, period) + +btn_buy_individual(highlighted, eventTrackingKey, 'professional', period) + if (period === 'monthly') + +additional_link_buy(eventTrackingKey, additionalEventSegmentation, 'professional', period) + +mixin btn_buy_group_collaborator(highlighted, eventTrackingKey) + a.btn.plans-v2-table-btn-buy( + data-ol-start-new-subscription='group_collaborator' + data-ol-event-tracking-key=eventTrackingKey + data-ol-item-view='annual' + data-ol-has-custom-href + data-ol-location='table-header' + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span.hidden-desktop #{translate("customize")} + span.hidden-mobile #{translate("customize_your_plan")} + +mixin btn_buy_group_professional(highlighted, eventTrackingKey) + a.btn.plans-v2-table-btn-buy( + data-ol-start-new-subscription='group_professional' + data-ol-event-tracking-key=eventTrackingKey + data-ol-item-view='annual' + data-ol-has-custom-href + data-ol-location='table-header' + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span.hidden-desktop #{translate("customize")} + span.hidden-mobile #{translate("customize_your_plan")} + +mixin btn_buy_group_organization(highlighted, eventTrackingKey) + a.btn.plans-v2-table-btn-buy( + data-ol-start-new-subscription='group_organization' + data-ol-event-tracking-key=eventTrackingKey + data-ol-item-view='annual' + data-ol-has-custom-href + data-ol-location='table-header' + href='/for/contact-sales' + target='_blank' + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span #{translate("contact_us_lowercase")} + +mixin btn_buy_student_free(highlighted) + if (!getSessionUser()) + a.btn.plans-v2-table-btn-buy( + href="/register" + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span #{translate("try_for_free")} + +mixin btn_buy_student_student(highlighted, eventTrackingKey, additionalEventSegmentation, period) + a.btn.plans-v2-table-btn-buy( + data-ol-start-new-subscription='student' + data-ol-event-tracking-key=eventTrackingKey + data-ol-item-view=period + data-ol-location='card' + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + if (period === 'monthly') + span #{translate("try_for_free")} + else + span #{translate("buy_now_no_exclamation_mark")} + if (period === 'monthly') + +additional_link_buy(eventTrackingKey, additionalEventSegmentation, 'student', period) + +mixin btn_buy_student_university(highlighted, eventTrackingKey, additionalEventSegmentation, period) + - var segmentation = JSON.stringify(Object.assign({}, {button: 'student-university', location: 'table-header-list', period}, additionalEventSegmentation)) + a.btn.plans-v2-table-btn-buy( + href="/for/contact-sales" + target="_blank" + event-tracking=eventTrackingKey + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation=segmentation + class=(highlighted ? 'btn-primary' : 'btn-default') + ) + span #{translate("contact_us_lowercase")} + +mixin additional_link_group(eventTrackingKey, additionalEventSegmentation, plan) + - var buttonSegmentation = plan + '-link' + - var segmentation = JSON.stringify(Object.assign({}, {button: buttonSegmentation, location: 'table-header'}, additionalEventSegmentation)) + small.plans-v2-table-th-content-additional-link + | #{translate("or")} + a( + href="/for/contact-sales" + target="_blank" + event-tracking=eventTrackingKey + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation=segmentation + ) #{translate("contact_us_lowercase")} + +mixin additional_link_buy(eventTrackingKey, additionalEventSegmentation, plan, period) + - var buttonSegmentation = plan + '-link' + - var segmentation = JSON.stringify(Object.assign({}, {button: buttonSegmentation, location: 'table-header', period}, additionalEventSegmentation)) + - var itmCampaign = itm_campaign ? { itm_campaign } : {itm_campaign: 'plans'} + - var itmReferrer = itm_referrer ? { itm_referrer } : {} + - var qs = new URLSearchParams({planCode: plan, currency: recommendedCurrency, itm_content: 'card', ...itmCampaign, ...itmReferrer}) + small.plans-v2-table-th-content-additional-link + | #{translate("or")} + a( + href=`/user/subscription/new?${qs.toString()}` + event-tracking=eventTrackingKey + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation=segmentation + ) #{translate("buy_now_no_exclamation_mark")} + +mixin plans_v2_table_sticky_header(withSwitch, config) + - var tableHeadKeys = Object.keys(config.tableHead) + .row.plans-v2-table-sticky-header.sticky( + data-ol-plans-v2-table-sticky-header + class=(withSwitch ? 'plans-v2-table-sticky-header-with-switch' : 'plans-v2-table-sticky-header-without-switch') + ) + - for (var i = 0; i < tableHeadKeys.length; i++) + - var tableHeadKey = tableHeadKeys[i] + - var translateKey = tableHeadKey.split('_')[1] + - + if (config.highlightedColumn.index === i) { + var elClass = 'plans-v2-table-sticky-header-item-green-highlighted' + } else { + var elClass = '' + } + .plans-v2-table-sticky-header-item( + class=elClass + ) + case tableHeadKey + when 'individual_collaborator' + span #{translate('standard')} + when 'group_professional' + span #{translate(tableHeadKey)} + when 'group_collaborator' + span #{translate('group_standard')} + default + span #{translate(translateKey)} + +mixin table_sticky_header_all(plansConfig) + .row.plans-v2-table-sticky-header-container( + data-ol-plans-v2-view='individual' + ) + +plans_v2_table_sticky_header(true, plansConfig.individual) + .row.plans-v2-table-sticky-header-container( + hidden + data-ol-plans-v2-view='group' + ) + +plans_v2_table_sticky_header(false, plansConfig.group) + .row.plans-v2-table-sticky-header-container( + hidden + data-ol-plans-v2-view='student' + ) + +plans_v2_table_sticky_header(true, plansConfig.student) + +mixin monthly_annual_switch(initialState, eventTracking, eventSegmentation) + - var monthlyAnnualToggleChecked = initialState === 'monthly' + .row + .col-md-4.col-md-offset-4.text-centered.plans-v2-m-a-switch-container(data-ol-plans-v2-m-a-switch-container) + .plans-v2-m-a-switch-annual-text-container + span.underline(data-ol-plans-v2-m-a-switch-text='annual') #{translate("annual")} + .tooltip.in.left.plans-v2-m-a-tooltip( + role="tooltip" + data-ol-plans-v2-m-a-tooltip + class=monthlyAnnualToggleChecked ? 'plans-v2-m-a-tooltip-monthly-selected' : '' + ) + .tooltip-arrow + .tooltip-inner + span(hidden=!monthlyAnnualToggleChecked data-ol-tooltip-period='monthly') #{translate("save_20_percent_by_paying_annually")} + span(hidden=monthlyAnnualToggleChecked data-ol-tooltip-period='annual') #{translate("saving_20_percent")} + + label.plans-v2-m-a-switch(data-ol-plans-v2-m-a-switch) + input( + type="checkbox" + checked=monthlyAnnualToggleChecked + role="switch" + aria-label=translate("select_monthly_plans") + autocomplete="off" + event-tracking=eventTracking + event-tracking-mb="true" + event-tracking-trigger="click" + event-tracking-element="checkbox" + event-segmentation=eventSegmentation + ) + span + span(data-ol-plans-v2-m-a-switch-text='monthly') #{translate("monthly")} diff --git a/services/web/frontend/stylesheets/app/plans/plans-light-touch-redesign.less b/services/web/frontend/stylesheets/app/plans/plans-light-touch-redesign.less new file mode 100644 index 0000000000..db94a559fc --- /dev/null +++ b/services/web/frontend/stylesheets/app/plans/plans-light-touch-redesign.less @@ -0,0 +1,1295 @@ +// m-a stands for: monthly annual + +@plans-v2-top-switch-item-height: 34px; +@plans-v2-top-switch-item-border-radius: @plans-v2-top-switch-item-height / 2; +@plans-v2-m-a-switch-height: 34px; +@plans-v2-highlighted-text-height-desktop: 30px; +@plans-v2-highlighted-text-height-mobile: 41px; +@plans-v2-learn-more-link-color: hsl(206, 100%, 52%); +@plans-v2-top-switch-group-width-mobile: 46%; +@plans-v2-table-border-radius: 20px; +@plans-v2-table-td-mobile-min-height: 34px; + +.plans-page { + p { + color: @neutral-70; + margin-bottom: @line-height-computed; + } + + .plans-header { + h1, + h2 { + color: @neutral-70; + } + } + + .circle { + font-size: 1.5rem; + font-weight: 700; + padding: 46px 18px; + margin: 0 auto @line-height-computed; + text-shadow: 0 -1px 1px darken(@link-color, 10%); + width: 120px; + height: 120px; + border-radius: 50%; + background-color: @brand-secondary; + color: white; + white-space: nowrap; + line-height: 1; + + .small { + color: rgba(255, 255, 255, 0.85); + font-size: @font-size-base * 0.8; + } + } + + .circle-lg { + height: 180px; + padding: 70px 8px; + width: 180px; + } + + .circle-subtext { + font-size: 1rem; + } + + .circle-img { + float: right; + + @media (max-width: @screen-sm-max) { + float: left; + margin: 0 15px; + } + } + + @media (max-width: @screen-xs-max) { + [data-ol-current-view='group'] [data-ol-plans-v2-m-a-switch-container] { + display: none; + } + } + + .plans-v2-top-switch ul.plans-v2-nav { + display: flex; + justify-content: center; + + li { + border-top: 1px solid @ol-blue-gray-3; + border-bottom: 1px solid @ol-blue-gray-3; + height: @plans-v2-top-switch-item-height; + + &.plans-v2-top-switch-individual { + border-right: 1px solid @ol-blue-gray-3; + border-left: 1px solid @ol-blue-gray-3; + border-top-left-radius: @plans-v2-top-switch-item-border-radius; + border-bottom-left-radius: @plans-v2-top-switch-item-border-radius; + } + + &.plans-v2-top-switch-student { + border-right: 1px solid @ol-blue-gray-3; + border-left: 1px solid @ol-blue-gray-3; + border-top-right-radius: @plans-v2-top-switch-item-border-radius; + border-bottom-right-radius: @plans-v2-top-switch-item-border-radius; + } + + button { + padding: 4px 16px; + height: 100%; + + // remove bootstrap default + &.btn:active { + box-shadow: none; + } + + // on Firefox, there will be a visible dotted border on the li element + // when it's `active` / `focus` + // `outline: none` rule intends to eliminate that border + &:active, + &:focus { + outline: none; + } + } + + &.active { + background-color: @ol-blue-gray-4; + + button.btn-default-outline { + color: @white; + } + } + + &:not(.active) { + background-color: @white; + } + } + + @media (max-width: @screen-xs-max) { + margin: 0 13px; + + li { + height: unset; + + &.plans-v2-top-switch-individual, + &.plans-v2-top-switch-student { + width: (100% - @plans-v2-top-switch-group-width-mobile) / 2; + } + + &.plans-v2-top-switch-group { + width: @plans-v2-top-switch-group-width-mobile; + + // force newline for the second span + span:first-child::after { + content: ''; + display: block; + } + + span:last-child { + font-weight: 400; + } + } + + &.plans-v2-top-switch-individual, + &.plans-v2-top-switch-student { + button { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + } + } + + button { + white-space: unset; + padding: 4px 8px; + font-size: 14px; + width: 100%; + } + } + } + } + + a.btn.plans-v2-btn-header, + button.plans-v2-btn-header { + font-family: @font-family-sans-serif; + margin-top: @margin-sm; + text-shadow: 0 0 0; + background-color: @ol-blue; + color: @white; + + &:hover { + color: @white; + } + } + + .plans-v2-m-a-switch-container { + display: flex; + align-items: center; + justify-content: center; + position: relative; + margin-top: @margin-xl; + + .underline { + text-decoration: underline; + } + + &.disabled { + span { + color: @ol-blue-gray-2; + } + + .plans-v2-m-a-switch { + input + span { + background-color: @ol-blue-gray-2; + cursor: not-allowed; + } + } + } + + @media (max-width: @screen-xs-max) { + margin-top: 25px; + + &[data-ol-current-view='group'] { + display: none; + } + } + } + + .plans-v2-m-a-switch { + position: relative; + display: inline-block; + width: 60px; + height: @plans-v2-m-a-switch-height; + margin: 0 @margin-lg; + + input { + opacity: 0; + width: 0; + height: 0; + } + + input + span { + background-color: @ol-green; + } + + input:checked + span { + background-color: @ol-blue-gray-4; + } + + input:checked + span::before { + transform: translateX(26px); + } + + span { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: @ol-blue-gray-1; + transition: 0.4s; + border-radius: @plans-v2-m-a-switch-height; + + &::before { + position: absolute; + content: ''; + height: 32px; + width: 32px; + left: 1px; + bottom: 1px; + background-color: @white; + transition: 0.4s; + border-radius: 50%; + } + } + } + + .plans-v2-m-a-switch-annual-text-container { + position: relative; + } + + .plans-v2-m-a-tooltip { + position: absolute; + color: @white; + width: auto; + border-radius: 4px; + right: 110%; + top: 0; + z-index: 0; + + &.tooltip.in.left { + .tooltip-inner { + background-color: @ol-green; + } + + .tooltip-arrow { + border-left-color: @ol-green; + } + } + + span { + white-space: nowrap; + } + + @media (max-width: @screen-xs-max) { + right: -31%; + top: unset; + + &.tooltip.in.bottom { + .tooltip-inner { + background-color: @ol-green; + } + + .tooltip-arrow { + border-left-color: unset; + border-bottom-color: @ol-green; + } + } + + &.plans-v2-m-a-tooltip-monthly-selected { + right: -119%; + } + + span { + font-size: @font-size-extra-small; + position: relative; + } + } + } + + .page-header.top-page-header { + // remove page-header border & margin on mobile + @media (max-width: @screen-xs-max) { + border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; + + h1 { + margin-top: 0; + } + } + } + + .plans-v2-table-comments-icon { + height: 65px; + + i { + font-size: 50px; + } + + @media (max-width: @screen-xs-max) { + height: 58px; + } + } + + .plans-v2-table-container { + position: relative; + } + + .plans-v2-license-picker-form { + margin-top: 25px; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + + @media (max-width: @screen-xs-max) { + font-size: @font-size-small; + margin-top: 15px; + flex-direction: column; + } + } + + .plans-v2-license-picker-select { + background-color: @white; + border: 1px solid #cccccc; + width: 64px; + height: 30px; + margin: 0 @margin-sm; + padding-left: @padding-sm; + + @media (max-width: @screen-xs-max) { + margin: 0 15px; + } + } + + .plans-v2-license-picker-educational-discount { + display: flex; + align-items: center; + + @media (max-width: @screen-xs-max) { + margin-top: 15px; + } + } + + .plans-v2-license-picker-educational-discount-label { + margin-bottom: 0; + display: flex; + align-items: center; + font-weight: normal; + + &.disabled span { + color: @ol-blue-gray-2; + } + } + + input[type='checkbox'].plans-v2-license-picker-educational-discount-checkbox { + margin-top: 0; + margin-right: @margin-sm; + + @media (max-width: @screen-xs-max) { + margin-right: 25px; + // scale the checkbox to around 30px width and height + transform: scale(2.307); + } + } + + i.plans-v2-license-picker-educational-discount-question-icon { + margin-left: @margin-xs; + + @media (max-width: @screen-xs-max) { + display: none; + } + } + + span.plans-v2-license-picker-educational-discount-learn-more-container { + display: none; + + @media (max-width: @screen-xs-max) { + margin-left: 5px; + display: inline; + // this white-space is important to ensure the "learn more" text won't get separated by a newline + white-space: nowrap; + + span.plans-v2-license-picker-educational-discount-learn-more-text { + color: @plans-v2-learn-more-link-color; + } + + .tooltip { + // force white-space to have initial value since the `white-space: nowrap` rule + // on .plans-v2-license-picker-educational-discount-learn-more-container selector (the current code block) above + // will also affect every child inside of it, including the generated tooltip + white-space: initial; + } + } + } + + .plans-v2-table-individual, + .plans-v2-table-student { + margin-top: 40px + @plans-v2-highlighted-text-height-desktop; + + @media (max-width: @screen-xs-max) { + margin-top: 60px + @plans-v2-highlighted-text-height-desktop; + + th .plans-v2-table-th-content { + min-height: 280px; + } + } + } + + .plans-v2-table-student { + tr { + // "hide" the last column on desktop by making the background-color as the default background color + th:last-of-type > .plans-v2-table-th { + background-color: @ol-blue-gray-0; + } + + &:last-child { + td:first-child { + background-color: @ol-blue-gray-0; + + .plans-v2-table-feature-name { + border-bottom-left-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-bottom-left-radius: 0; + } + } + } + + td:last-child { + background-color: @ol-blue-gray-0; + + .plans-v2-table-cell { + border-bottom-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-bottom-right-radius: 0; + } + } + } + } + } + + // only make corners have round borders if the column is not highlighted + th:not(.plans-v2-table-green-highlighted) { + &:nth-last-child(2) .plans-v2-table-th { + border-top-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-right-radius: 0; + } + } + + &:nth-child(2) .plans-v2-table-th { + border-top-left-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-right-radius: 0; + } + } + } + + @media (max-width: @screen-md-max) { + tr:first-of-type { + th:last-of-type { + // Last column is only a UI placeholder for desktop view. + // We need to hide it for mobile to have the full table fully visible on mobile + // without any empty horizontal space. + display: none; + } + } + } + } + + .plans-v2-table-individual { + tr { + &:last-child { + td:first-child { + background-color: @ol-blue-gray-0; + + .plans-v2-table-feature-name { + border-bottom-left-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-bottom-left-radius: 0; + } + } + } + + td:last-child { + background-color: @ol-blue-gray-0; + + .plans-v2-table-cell { + border-bottom-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-bottom-right-radius: 0; + } + } + } + } + + &:not(.plans-v2-table-divider) td { + background-color: @white; + } + } + + th { + &:last-child .plans-v2-table-th { + border-top-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-right-radius: 0; + } + } + + &:nth-child(2) .plans-v2-table-th { + border-top-left-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-left-radius: 0; + } + } + } + } + + .plans-v2-table-group { + margin-top: 26px + @plans-v2-highlighted-text-height-desktop; + + p.plans-v2-table-th-content-title { + // Reduce font size of table heading for screen size >= @screen-lg (1200px) from 20px to 19px + // In the English version of overleaf, 20px is enough for the text to not be wrapped to the next line, the longest text is the 2nd column of group tab: "Group Professional". + // In the English version, "Group Professional" won't be wrapped to two separate lines with 20px font size for big screen. + // In other languages, for example, German (subdomain `de`), 20px is too big so the text is wrapped and breaks the UI. 19px is enough to fit the text on one line on the German site. + // This font-size reduction is a case-to-case basis since we don't have any way to know the max length of every language unless we've translated them all. + // There's probably a smarter way to do it, but probably also more complex. This change in font should be acceptable and we can revisit it every time there's a longer text. + font-size: 19px; + + @media (max-width: @screen-md-max) { + font-size: @font-size-base; + } + + @media (max-width: @screen-xs-max) { + font-size: @font-size-small; + } + } + + tr { + // top right border radius on desktop only + &:first-of-type { + th:last-child > .plans-v2-table-th { + border-top-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-right-radius: 0; + } + } + } + + // bottom right border radius on desktop only + &:last-of-type { + td:last-child > .plans-v2-table-cell { + border-bottom-right-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-bottom-right-radius: 0; + } + } + } + } + } + + .plans-v2-table { + background-color: @ol-blue-gray-0; + margin-bottom: 15px; + table-layout: fixed; + width: 100%; + // We want these `div` inside `th` and `td` to have a 100% height since + // white backgrounds are defined here: + // 1. `td > div.plans-v2-table-cell` + // 2. `th > div.plans-v2-table-th` + // + // To make both of them have 100% height of their respective `td` and `th`, + // we need to have the `table` has an explicit `height` rule. + // The value can be arbitrary, since it will be ignored by the browser + // + // See https://stackoverflow.com/a/56913789 for more details + height: 100%; + + .plans-v2-table-row-header { + font-weight: 500; + } + + tr { + height: 100%; + } + + .plans-v2-table-column-header, + td { + background-clip: padding-box; /* needed for firefox when there is bg color */ + text-align: center; + + &:not(.plans-v2-table-cell-before-green-highlighted-column):not( + .plans-v2-table-green-highlighted + ):not(.plans-v2-table-divider-highlighted) { + border-right: 1px solid @ol-blue-gray-0; + + @media (max-width: @screen-xs-max) { + border-right: none; + } + } + } + + .plans-v2-table-column-header { + border-top: 0; + font-family: @headings-font-family; + font-size: @font-size-h2; + font-weight: @headings-font-weight; + line-height: @headings-line-height; + vertical-align: top; + padding: 0; + position: relative; + background-color: @ol-blue-gray-0; + + &:first-child { + border-left: 0; + } + + &:last-child { + border-right: 0; + } + } + + td { + vertical-align: middle; + height: 100%; + + &:last-child:not(.plans-v2-table-green-highlighted):not( + .plans-v2-table-divider-highlighted + ) { + border-right: 0; + } + } + + tr { + &:first-child th { + // keep the position here, otherwise messes up border on safari + position: relative; + } + + // css hack for table border-radius on the first feature name + &:nth-child(2) { + .plans-v2-table-row-header:first-child { + background-color: @ol-blue-gray-0; + + .plans-v2-table-feature-name { + border-top-left-radius: @plans-v2-table-border-radius; + + @media (max-width: @screen-xs-max) { + border-top-left-radius: 0; + } + } + } + } + + &:not(.plans-v2-table-row-last-row-per-section):not( + .plans-v2-table-divider + ):not(:last-of-type) { + th > .plans-v2-table-th > .plans-v2-table-th-content, + td > .plans-v2-table-feature-name, + td > .plans-v2-table-cell > .plans-v2-table-cell-content { + border-bottom: 1px solid @ol-blue-gray-0; + + @media (max-width: @screen-xs-max) { + border-bottom: unset; + } + } + } + + // this will show highlghted border-bottom on the last row + &:last-child td.plans-v2-table-green-highlighted { + > .plans-v2-table-cell { + border-bottom: 2px solid @ol-green; + + @media (max-width: @screen-xs-max) { + border-bottom: unset; + } + } + } + } + + .fa-check { + color: @green; + } + + @media (min-width: @screen-sm-min) { + // highlight rows on hover + tr:not(.plans-v2-table-divider):not(:first-child):hover { + background-color: @table-hover-bg; + + .plans-v2-table-feature-name, + .plans-v2-table-cell { + background-color: @table-hover-bg; + } + } + } + + @media (max-width: @screen-sm-max) { + font-size: @font-size-small; + } + + @media (max-width: @screen-xs-max) { + tr { + display: flex; + // for mobile, we need to show the feature name (the first column) as its own row + // the flex-flow will make the whole tr overflow to a next row if + // total td width combined is bigger than viewport width + // so, one tr can have multiple "visible" row depending on the total width. + flex-flow: row wrap; + justify-content: space-around; + } + + // The forced width below (plans-v2-table-cols-n) is to ensure the table columns have an equal width + // because on mobile, the first column (empty cell on first `tr` and feature name on the rest of `tr`) will be shown as its own row + // and the rest of the `tr` will incorporate a full width of the viewport + tr.plans-v2-table-cols-4:not(.plans-v2-table-divider) { + td, + th:not(:first-of-type) { + width: 25%; + } + } + + tr.plans-v2-table-cols-3:not(.plans-v2-table-divider) { + td, + th:not(:first-of-type) { + width: calc(100% / 3); + } + } + + tr.plans-v2-table-cols-2:not(.plans-v2-table-divider) { + td, + th:not(:first-of-type) { + width: 50%; + } + } + + .plans-v2-table-column-header { + font-size: 12px; + } + + // hide the first column header + tr:first-child th:first-child { + display: none; + } + + // for mobile, we need to show the feature name (the first td) as its own row + // the width: 100vw rule will enhance the flex-flow on the tr by ensuring the feature name (first column of each feature row) + // will have the full width of viewport, + // and making the first td appear like the it is in its own row (although it's technically still on the same tr as the other tds) + .plans-v2-table-row-header { + background: @gray-lightest; + width: 100vw; + + span.plans-v2-table-feature-name { + width: 100%; + + span { + text-align: left; + } + } + } + } + } + + // plans-v2-table-cell class only appears for the inner `div` of a `td` + // that doesn't contain divider or feature name + // (i.e it contains either check mark icon or a dash) + .plans-v2-table-cell { + background-color: @white; + height: 100%; + + .plans-v2-table-cell-content { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + + @media (max-width: @screen-xs-max) { + .plans-v2-table-cell-content { + padding: 6px; + min-height: @plans-v2-table-td-mobile-min-height; + } + } + } + + .plans-v2-table-th { + height: 100%; + background-color: @white; + + .plans-v2-table-th-content { + display: flex; + flex-direction: column; + min-height: 321px; + height: 100%; + padding: 10px 13px; + + @media (max-width: @screen-md-max) { + min-height: 330px; + } + + @media (max-width: @screen-sm-max) { + padding-left: @padding-xs; + padding-right: @padding-xs; + } + } + } + + .plans-v2-table-btn-buy-container-desktop { + margin-top: auto; + display: flex; + flex-direction: column; + min-height: 60px; + + @media (max-width: @screen-xs-max) { + display: none; + } + } + + .plans-v2-table-btn-buy-container-mobile { + display: none; + + @media (max-width: @screen-xs-max) { + display: flex; + flex-direction: column; + min-height: 50px; + margin-top: @padding-sm; + } + } + + .plans-v2-table-btn-buy { + padding: 4px 15px 5px; + + @media (min-width: (@screen-xs-max+1)) { + .hidden-desktop { + display: none; + } + } + + @media (max-width: @screen-sm-max) { + font-size: @font-size-small; + } + + @media (max-width: @screen-xs-max) { + padding-left: 9px; + padding-right: 9px; + // truncating group plans long button text for mobile + .hidden-mobile { + display: none; + } + } + } + + p.plans-v2-table-th-content-title { + font-size: @font-size-large; + padding-bottom: @padding-sm; + border-bottom: 1px solid @gray-lighter; + margin-bottom: @margin-lg; + + @media (max-width: @screen-md-max) { + font-size: @font-size-base; + } + + // `plans-v2-table-th-content-title` is visually invisible on screen size less than @screen-xs-max + // because there is a sticky header (`plans-v2-table-sticky-header` class) that "replace" the content visually + // (see the `@plans-v2-table-sticky-header-z-index` variable that make the sticky header element is "above" the whole table) + // however, we still need to properly styling the `plans-v2-table-th-content-title` for small screen size to + // fit this element to the size of the sticky header + // this trick is used because th element can not have a `position: sticky` rule, at least for our use case + @media (max-width: @screen-xs-max) { + min-height: 40px; + margin-bottom: @margin-md; + font-size: @font-size-small; + padding-bottom: 0; + } + } + + p.plans-v2-table-th-content-benefit, + ul.plans-v2-table-th-content-benefit { + padding-top: 15px; + font-size: @font-size-small; + font-family: @font-family-sans-serif; + line-height: 23px; + hyphens: auto; + } + + ul.plans-v2-table-th-content-benefit { + padding-left: 15px; + text-align: left; + margin-bottom: 0; + + li a { + color: @ol-blue-gray-3; + text-decoration: underline; + } + } + + // this class should only appear on the group table + .plans-v2-group-total-price { + font-weight: 700; + } + + .plans-v2-table-price-container { + position: relative; + } + + p.plans-v2-table-price-period-label { + margin: 0; + font-size: @font-size-small; + line-height: 23px; + color: @ol-blue-gray-3; + } + + p.plans-v2-table-price { + margin: 0; + + span { + font-weight: 700; + font-size: 32px; + line-height: 42px; + + @media (max-width: @screen-xs-max) { + line-height: 35px; + font-size: @font-size-large; + } + } + } + + strike.plans-v2-table-price-before-discount { + position: absolute; + top: -27px; + font-weight: 700; + font-size: @font-size-small; + line-height: 42px; + display: flex; + justify-content: center; + width: 100%; + color: @ol-blue-gray-2; + + @media (max-width: @screen-xs-max) { + font-size: @font-size-extra-small; + } + } + + small.plans-v2-table-th-content-additional-link { + text-transform: lowercase; + margin-top: @margin-xs; + font-size: @font-size-extra-small; + height: 16px; + font-family: @font-family-sans-serif; + + a { + color: @ol-blue-gray-5; + text-decoration: underline; + } + } + + .plans-v2-table-feature-name { + background-color: @white; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 6px 6px 18px; + + i.plans-v2-table-feature-name-question-icon { + margin-left: 10px; + } + + span.plans-v2-table-feature-name-learn-more-container { + display: none; + } + + @media (max-width: @screen-xs-max) { + display: inline-block; + text-align: left; + padding: 6px; + width: 100%; + background-color: @ol-blue-gray-0; + min-height: @plans-v2-table-td-mobile-min-height; + + i.plans-v2-table-feature-name-question-icon { + display: none; + } + + span.plans-v2-table-feature-name-learn-more-container { + margin-left: 5px; + display: inline; + // this white-space is important to ensure the "learn more" text won't get separated by a newline + white-space: nowrap; + + span.plans-v2-table-feature-name-learn-more-text { + color: @plans-v2-learn-more-link-color; + } + + .tooltip { + // force white-space to have initial value since the `white-space: nowrap` rule + // on .plans-v2-table-feature-name-learn-more-container selector (the current code block) above + // will also affect every child inside of it, including the generated tooltip + white-space: initial; + } + } + } + } + + tr.plans-v2-table-divider { + background-color: @ol-blue-gray-1; + + // direct child selector to NOT affect the generated tooltip + td > div { + text-align: center; + padding: 6px; + + .plans-v2-table-divider-label { + margin-bottom: 0; + margin-right: 5px; + } + + .plans-v2-table-divider-learn-more-container { + display: none; + } + } + + // highlighted divider will only show if the highlighted column is on the last visible column + .plans-v2-table-divider-highlighted > div { + border-right: 2px solid @ol-green; + } + + @media (max-width: @screen-xs-max) { + // need the colspan attribute selector to increase specificity to override more specific css rule on another selector + td[colspan] { + padding-top: 0; + padding-right: 0; + padding-left: 0; + + // direct child selector to NOT affect the generated tooltip + > div { + background-color: @ol-blue-gray-1; + padding: 15px 5px; + + i.plans-v2-table-divider-question-icon { + display: none; + } + + .plans-v2-table-divider-learn-more-container { + display: inline; + // this white-space is important to ensure the "learn more" text won't get separated by a newline + white-space: nowrap; + + span.plans-v2-table-divider-learn-more-text { + color: @plans-v2-learn-more-link-color; + } + + // force white-space to have initial value since the `white-space: nowrap` rule + // on .plans-v2-table-divider-learn-more-container selector (the current code block) above + // will also affect every child inside of it, including the generated tooltip + .tooltip { + white-space: initial; + } + } + } + } + + .plans-v2-table-divider-highlighted > div { + border-right: none; + } + } + } + + // the `.plans-v2-table-green-highlighted` class below can be applied to both `th` and `td` + .plans-v2-table-green-highlighted { + > .plans-v2-table-cell, + > .plans-v2-table-th { + border-left: 2px solid @ol-green; + border-right: 2px solid @ol-green; + + @media (max-width: @screen-xs-max) { + border-left: unset; + border-right: unset; + } + } + } + + p.plans-v2-table-green-highlighted-text { + border: 2px solid @ol-green; + position: absolute; + top: -1 * @plans-v2-highlighted-text-height-desktop; + left: 0; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + font-size: @font-size-small; + line-height: 19px; + font-weight: 700; + background-color: @ol-green; + border-radius: @plans-v2-table-border-radius @plans-v2-table-border-radius 0 + 0; + color: @white; + height: @plans-v2-highlighted-text-height-desktop; + margin: 0; + + @media (max-width: @screen-sm-max) { + top: -1 * @plans-v2-highlighted-text-height-mobile; + height: @plans-v2-highlighted-text-height-mobile; + } + + @media (max-width: @screen-xs-max) { + padding-left: 5px; + padding-right: 5px; + font-size: @font-size-extra-small; + line-height: 15px; + } + } + + .plans-v2-table-sticky-header-container { + @media (max-width: @screen-xs-max) { + // `height: 60%` is just an arbitrary percentage + // since we need to cover the whole plans_v2_table + // but not too much so it vertically overflows the page + // and it's not trivial to calculate the precise table height + height: 60%; + width: 100%; + position: absolute; + } + } + + .plans-v2-table-sticky-header-without-switch { + margin-bottom: -107px; + margin-top: 67px; + } + + .plans-v2-table-sticky-header-with-switch { + margin-bottom: -140px; + margin-top: 100px; + } + + .plans-v2-table-sticky-header { + display: none; + + @media (max-width: @screen-xs-max) { + display: flex; + width: 100vw; + top: 0; + left: 0; + z-index: @z-index-plans-page-table-header-mobile; + height: 40px; + + // separate sticky class because we want to remove the class (to visually remove the sticky header) + // with javascript whenever the table is not visible anymore + &.sticky { + position: sticky; + } + } + } + + .plans-v2-table-sticky-header-item { + background-color: @white; + flex: 1 1 0px; + + span { + margin: 0px @margin-xs; + border-bottom: 1px solid #cccccc; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-family: @font-family-serif; + + @media (max-width: @screen-xs-max) { + font-size: @font-size-small; + text-align: center; + line-height: 19px; + } + } + } + + .plans-v2-table-sticky-header-item-green-highlighted span { + color: @ol-green; + } + + .plans-v2-faq { + p { + margin: 0; + } + + // need for specificity to override default a color + p > a { + color: @ol-blue; + + &:hover { + color: @ol-dark-blue; + } + } + } + + .plans-v2-university-info { + padding: @padding-lg @padding-xxl; + background-color: @ol-blue-gray-1; + border-radius: 20px; + + h3.plans-v2-university-info-header { + margin: 0 @margin-xl @margin-md @margin-xl; + } + + p.plans-v2-university-info-text { + margin: 0 @margin-xl; + } + + a.plans-v2-btn-university-info { + margin-top: @margin-md; + } + + @media (max-width: @screen-sm-max) { + h3.plans-v2-university-info-header { + margin: 0 0 @margin-md 0; + } + + p.plans-v2-university-info-text { + margin: 0; + } + } + + @media (max-width: @screen-xs-max) { + padding: @padding-lg; + } + } + + padding-top: calc(var(--spacing-16) + var(--header-height)); + + h1 { + margin-top: 0; + margin-bottom: var(--spacing-08); + } + .plans-v2-top-switch { + margin-top: var(--spacing-09); + } + + .plans-v2-table, + .plans-v2-table-column-header { + background-color: transparent; // remove colors set by default variant + } + + .plans-page-quote-row { + margin: var(--spacing-13) 0; + } +} diff --git a/services/web/frontend/stylesheets/main-style.less b/services/web/frontend/stylesheets/main-style.less index 26ae140a06..fa641d9456 100644 --- a/services/web/frontend/stylesheets/main-style.less +++ b/services/web/frontend/stylesheets/main-style.less @@ -106,6 +106,7 @@ @import 'app/editor.less'; @import 'app/homepage.less'; @import 'app/plans/plans-v2.less'; +@import 'app/plans/plans-light-touch-redesign.less'; @import 'app/plans/plans-new-design.less'; @import 'app/recurly.less'; @import 'app/bonus.less';