diff --git a/services/web/app/views/_mixins/ciam.pug b/services/web/app/views/_mixins/ciam.pug deleted file mode 100644 index 9559f114be..0000000000 --- a/services/web/app/views/_mixins/ciam.pug +++ /dev/null @@ -1,42 +0,0 @@ -include terms_of_service -include recaptcha - -mixin ciamLogo - header.ciam-logo - a.brand.overleaf-ds-logo(href='/') - span.visually-hidden Overleaf - -mixin ciamCardSeparator - hr.ciam-card-separator - -mixin ciamCardFooter - section.ciam-card-footer - +ciamCardSeparator - .ciam-footer-ds-logo - img( - src=buildImgPath('digital-science/digital-science.svg') - alt='Digital Science' - ) - p - | !{translate('advancing_research_with', { linkOverleaf: 'https://www.overleaf.com', linkPapers: 'https://www.papersapp.com/', linkDS: 'https://www.digital-science.com/products/' })} - -mixin ciamTermsOfServiceAgreement - p - +termsOfServiceAgreementContent - -mixin ciamRecaptchaConditions - p - +recaptchaConditionsContent - -mixin ciamCustomFormDangerMessage(key) - div( - class='notification ciam-notification notification-type-error' - hidden - data-ol-custom-form-message=key - role='alert' - aria-live='polite' - ) - .notification-icon - ph-warning-circle(aria-hidden='true') - .notification-content.text-left - block diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index ec76e64a22..5f7b913308 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -106,6 +106,7 @@ "address_second_line_optional": "", "adjust_column_width": "", "advanced_reference_search_mode": "", + "advancing_research_with": "", "after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "", "aggregate_changed": "", "aggregate_to": "", diff --git a/services/web/frontend/js/features/form-helpers/form-phosphor-icons.ts b/services/web/frontend/js/features/form-helpers/form-phosphor-icons.ts index 7b7e72876f..3ebe910702 100644 --- a/services/web/frontend/js/features/form-helpers/form-phosphor-icons.ts +++ b/services/web/frontend/js/features/form-helpers/form-phosphor-icons.ts @@ -1,4 +1,6 @@ -// These are used in the CIAM registration form +// These are used in the CIAM registration and login flows import '@phosphor-icons/webcomponents/PhBank' import '@phosphor-icons/webcomponents/PhEye' +import '@phosphor-icons/webcomponents/PhInfo' import '@phosphor-icons/webcomponents/PhEyeSlash' +import '@phosphor-icons/webcomponents/PhWarningCircle' diff --git a/services/web/frontend/js/features/form-helpers/hydrate-form.ts b/services/web/frontend/js/features/form-helpers/hydrate-form.ts index 2c8927b5e5..eb2abdb0ca 100644 --- a/services/web/frontend/js/features/form-helpers/hydrate-form.ts +++ b/services/web/frontend/js/features/form-helpers/hydrate-form.ts @@ -4,6 +4,7 @@ import { canSkipCaptcha, validateCaptchaV2 } from './captcha' import inputValidator from './input-validator' import { disableElement, enableElement } from '../utils/disableElement' import { materialIcon as createMaterialIcon } from '@/features/utils/material-icon' +import { ciamIcon } from '@/features/utils/ciam-icon' // Form helper(s) to handle: // - Attaching to the relevant form elements @@ -343,12 +344,20 @@ function showMessagesNewStyle( } // create the left icon - const icon = createMaterialIcon( - message.type === 'error' ? 'error' : 'check_circle' - ) + const isDsBranded = formEl.dataset.ciamForm !== undefined const messageIcon = document.createElement('div') messageIcon.className = 'notification-icon' - messageIcon.appendChild(icon) + if ( + isDsBranded && + (message.type === 'error' || message.type === 'info') + ) { + messageIcon.append(ciamIcon(message.type)) + } else { + const icon = createMaterialIcon( + message.type === 'error' ? 'error' : 'check_circle' + ) + messageIcon.appendChild(icon) + } // append icon first so it's on the left messageElContainer.appendChild(messageIcon) @@ -379,23 +388,43 @@ function showMessagesNewStyle( }) } +function querySelectorAllWithSelf(el: HTMLElement, selector: string) { + const nodeList = el.querySelectorAll(selector) + return el.matches(selector) ? [el, ...nodeList] : Array.from(nodeList) +} + export function inflightHelper(el: HTMLElement) { - const disabledInflight = el.querySelectorAll('[data-ol-disabled-inflight]') + const disabledInflight = querySelectorAllWithSelf( + el, + '[data-ol-disabled-inflight]' + ) const showWhenNotInflight = el.querySelectorAll( '[data-ol-inflight="idle"]' ) const showWhenInflight = el.querySelectorAll( '[data-ol-inflight="pending"]' ) + const spinnerInflight = querySelectorAllWithSelf( + el, + '[data-ol-spinner-inflight]' + ) el.addEventListener('pending', () => { disabledInflight.forEach(disableElement) toggleDisplay(showWhenNotInflight, showWhenInflight) + spinnerInflight.forEach(loadingEl => { + loadingEl.setAttribute('data-ol-loading', 'true') + loadingEl.classList.add('button-loading') + }) }) el.addEventListener('idle', () => { disabledInflight.forEach(enableElement) toggleDisplay(showWhenInflight, showWhenNotInflight) + spinnerInflight.forEach(loadingEl => { + loadingEl.removeAttribute('data-ol-loading') + loadingEl.classList.remove('button-loading') + }) }) } diff --git a/services/web/frontend/js/features/form-helpers/input-validator.ts b/services/web/frontend/js/features/form-helpers/input-validator.ts index 1447ee04cf..c7bc9b50cc 100644 --- a/services/web/frontend/js/features/form-helpers/input-validator.ts +++ b/services/web/frontend/js/features/form-helpers/input-validator.ts @@ -1,13 +1,7 @@ import { materialIcon } from '@/features/utils/material-icon' import classNames from 'classnames' import '@phosphor-icons/webcomponents/PhWarningCircle' - -function dsErrorIcon() { - const icon = document.createElement('ph-warning-circle') - icon.className = 'ciam-form-text-icon' - icon.ariaHidden = 'true' - return icon -} +import { ciamIcon } from '@/features/utils/ciam-icon' export default function inputValidator( inputEl: HTMLInputElement | HTMLTextAreaElement @@ -30,7 +24,9 @@ export default function inputValidator( const messageTextNode = document.createTextNode('') - const iconEl = isDsBranded ? dsErrorIcon() : materialIcon('error') + const iconEl = isDsBranded + ? ciamIcon('error', 'ciam-form-text-icon') + : materialIcon('error') messageInnerEl.append(iconEl) messageInnerEl.append(messageTextNode) diff --git a/services/web/frontend/js/features/settings/components/emails/ciam-six-digits-input.tsx b/services/web/frontend/js/features/settings/components/emails/ciam-six-digits-input.tsx index cda9e56b76..08ecec6aea 100644 --- a/services/web/frontend/js/features/settings/components/emails/ciam-six-digits-input.tsx +++ b/services/web/frontend/js/features/settings/components/emails/ciam-six-digits-input.tsx @@ -37,6 +37,8 @@ const CIAMSixDigitsInput = forwardRef< )} maxLength={7} inputMode="numeric" + autocomplete="off" + data-1p-ignore /> - diff --git a/services/web/frontend/js/features/utils/ciam-icon.ts b/services/web/frontend/js/features/utils/ciam-icon.ts new file mode 100644 index 0000000000..d37579a4d2 --- /dev/null +++ b/services/web/frontend/js/features/utils/ciam-icon.ts @@ -0,0 +1,9 @@ +export function ciamIcon(type: 'error' | 'info', className?: string) { + const elName = type === 'error' ? 'ph-warning-circle' : 'ph-info' + const icon = document.createElement(elName) + if (className) { + icon.className = className + } + icon.ariaHidden = 'true' + return icon +} diff --git a/services/web/frontend/js/shared/components/layouts/ciam-layout.tsx b/services/web/frontend/js/shared/components/layouts/ciam-layout.tsx index 77c893d787..72964b93bb 100644 --- a/services/web/frontend/js/shared/components/layouts/ciam-layout.tsx +++ b/services/web/frontend/js/shared/components/layouts/ciam-layout.tsx @@ -1,22 +1,43 @@ import React, { FC, ReactNode } from 'react' +import { Trans } from 'react-i18next' +import dsLogo from '@/shared/svgs/digital-science.svg' type Props = { children: ReactNode } const CiamLayout: FC = ({ children }: Props) => (
- + Overleaf
{children} +
+
+
+ Digital Science — home +
+

+ , + // eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key + , + ]} + /> +

+
) diff --git a/services/web/frontend/js/shared/svgs/digital-science.svg b/services/web/frontend/js/shared/svgs/digital-science.svg new file mode 100644 index 0000000000..f9c164da97 --- /dev/null +++ b/services/web/frontend/js/shared/svgs/digital-science.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/services/web/frontend/stylesheets/ciam/all.scss b/services/web/frontend/stylesheets/ciam/all.scss index 87694807fe..efe9c3e859 100644 --- a/services/web/frontend/stylesheets/ciam/all.scss +++ b/services/web/frontend/stylesheets/ciam/all.scss @@ -1,5 +1,6 @@ @import '../ds/all'; // DS design system styles @import 'ciam-layout'; -@import 'ciam-variables'; +@import 'ciam-links'; +@import 'ciam-login'; @import 'ciam-register'; @import 'ciam-six-digits'; diff --git a/services/web/frontend/stylesheets/ciam/ciam-layout.scss b/services/web/frontend/stylesheets/ciam/ciam-layout.scss index d4ef165801..4fd96f659e 100644 --- a/services/web/frontend/stylesheets/ciam/ciam-layout.scss +++ b/services/web/frontend/stylesheets/ciam/ciam-layout.scss @@ -1,13 +1,21 @@ @use 'sass:math'; +@mixin ciam-bottom-margin { + margin: 0 0 var(--ds-spacing-400); +} + +@mixin ciam-vertically-spaced { + display: flex; + flex-direction: column; + gap: var(--ds-spacing-400); +} + .overleaf-ds-logo { background-image: url('../../../frontend/js/shared/svgs/overleaf-a-ds-solution-mallard.svg'); } .ciam-layout { - display: flex; - flex-direction: column; - gap: var(--ds-spacing-400); + @include ciam-vertically-spaced; @include media-breakpoint-up(sm) { gap: var(--ds-spacing-800); @@ -25,141 +33,195 @@ color: var(--ds-color-text-primary); } - .ciam-container { - flex: 1 1 auto; - padding: 0 var(--ds-spacing-300); - } - - .ciam-logo { - padding: var(--ds-spacing-800) 0 0 0; - text-align: center; - - @include media-breakpoint-up(sm) { - padding-left: var(--ds-spacing-800); - padding-right: var(--ds-spacing-800); - } - } - - .ciam-logo .brand { - flex-shrink: 0; - background-repeat: no-repeat; - background-position: center center; - background-size: contain; - height: 49px; - width: 107px; - margin: 0 auto; - display: block; - - @include media-breakpoint-up(sm) { - height: 64px; - width: 130px; - margin: 9px 0; // Vertical margin isn't an exacting spacing value in the design - } - } - h1 { - @include ds-heading-sm-semibold; + @include ds-heading-md-semibold; padding-bottom: var(--ds-spacing-200); margin: 0; } - .ciam-card { - display: flex; - flex-direction: column; - gap: var(--ds-spacing-400); - background-color: var(--bg-light-primary); - box-shadow: - 0 4px 6px -4px rgb(0 0 0 / 10%), - 0 1px 29px -3px rgb(0 0 0 / 16%); - padding: var(--ds-spacing-800) var(--ds-spacing-400); - border-radius: var(--ds-border-radius-400); - max-width: 464px; - margin: 0 auto; - min-height: 500px; - - @include media-breakpoint-up(sm) { - padding: var(--ds-spacing-1300); - } - - .notification { - @include ds-body-sm-regular; - - color: var(--ds-color-text-primary); - padding: 0 var(--ds-spacing-400); - border-width: 0; - border-radius: var(--ds-border-radius-200); - - .notification-icon { - font-size: math.div(20em, 14); - } - - .notification-content { - padding: var(--ds-spacing-400) 0; - } - - &.notification-type-error { - background-color: var(--ds-color-red-50); - } - } - } - - .ciam-disclaimers p { - @include ds-body-sm-regular; - - padding-bottom: var(--ds-spacing-250); - margin: 0; - } - - .ciam-card-separator { - margin: var(--ds-spacing-500) 0; - height: 0; - border-top: solid 1px var(--ds-grey-20); - } - - .ciam-card-footer { - display: flex; - flex-direction: column; - gap: var(--ds-spacing-250); - - p { - @include ds-body-sm-regular; - - color: var(--ds-color-text-secondary); - margin-bottom: 0; - } - } - - .ciam-footer-ds-logo { - text-align: center; - padding: var(--ds-spacing-200) 0; - } - - .ciam-stepper { - margin: 0; - height: 4px; - border-radius: var(--ds-border-radius-full); - - .step { - background: var(--ds-color-neutral-200); - } - } - footer { - display: flex; - gap: var(--ds-spacing-600); - justify-content: center; - padding: var(--ds-spacing-300) 0; - margin: 0 auto; + // Allow room for the cookie banner + padding-bottom: 100px; - @include media-breakpoint-up(sm) { - margin-left: var(--ds-spacing-800); - margin-right: var(--ds-spacing-800); - justify-content: start; - } + .footer-links { + display: flex; + gap: var(--ds-spacing-600); + justify-content: center; + color: var(--ds-color-text-secondary); + padding: var(--ds-spacing-300) 0; + margin: 0 auto; - a { - @include ds-body-sm-regular; + @include media-breakpoint-up(sm) { + margin-left: var(--ds-spacing-800); + margin-right: var(--ds-spacing-800); + } + + a { + @include ds-body-sm-regular; + } } } } + +.ciam-container { + flex: 1 1 auto; + padding: 0 var(--ds-spacing-300); +} + +.ciam-logo { + padding: var(--ds-spacing-800) 0 0 0; + text-align: center; + + @include media-breakpoint-up(sm) { + padding-left: var(--ds-spacing-800); + padding-right: var(--ds-spacing-800); + } +} + +.ciam-logo .brand { + flex-shrink: 0; + background-repeat: no-repeat; + background-position: center center; + background-size: contain; + height: 60px; + width: 133px; + margin: 0 auto; + display: block; + + @include media-breakpoint-up(sm) { + height: 64px; + width: 142px; + margin: 9px 0; // Vertical margin isn't an exacting spacing value in the design + } +} + +.ciam-card { + @include ciam-vertically-spaced; + + background-color: var(--bg-light-primary); + box-shadow: + 0 4px 6px -4px rgb(0 0 0 / 10%), + 0 1px 29px -3px rgb(0 0 0 / 16%); + padding: var(--ds-spacing-800) var(--ds-spacing-400); + border-radius: var(--ds-border-radius-400); + max-width: 464px; + margin: 0 auto; + min-height: 500px; + + @include media-breakpoint-up(sm) { + padding: var(--ds-spacing-1300); + } + + .notification { + @include ds-body-sm-regular; + + color: var(--ds-color-text-primary); + padding: 0 var(--ds-spacing-400); + border-width: 0; + border-radius: var(--ds-border-radius-200); + gap: var(--ds-spacing-300); + + .notification-icon { + font-size: math.div(20em, 14); + display: flex; + align-items: center; + padding: var(--ds-spacing-50); + } + + .notification-content { + padding: var(--ds-spacing-400) 0; + } + + &.notification-type-error { + background-color: var(--ds-color-red-50); + } + + &.notification-type-info { + background-color: var(--ds-color-blue-50); + } + } +} + +.ciam-login-register-error-container .notification { + @include ciam-bottom-margin; +} + +.ciam-disclaimers p { + @include ds-body-sm-regular; + + padding-bottom: var(--ds-spacing-250); + margin: 0; +} + +.ciam-card-separator { + margin: var(--ds-spacing-500) 0; + height: 0; + border-top: solid 1px var(--ds-grey-20); +} + +.ciam-card-footer { + display: flex; + flex-direction: column; + gap: var(--ds-spacing-250); + + p { + @include ds-body-sm-regular; + + color: var(--ds-color-text-secondary); + margin-bottom: 0; + text-align: center; + + @include media-breakpoint-up(sm) { + text-align: start; + } + } +} + +.ciam-login-register-text { + @include ciam-bottom-margin; +} + +.ciam-login-register-text-final { + margin: 0; +} + +.ciam-footer-ds-logo { + text-align: center; + padding: var(--ds-spacing-200) 0; +} + +.ciam-stepper { + margin: 0; + height: 4px; + border-radius: var(--ds-border-radius-full); + + .step { + background: var(--ds-color-neutral-200); + } +} + +.ciam-work-uni-sso { + padding-top: var(--ds-spacing-200); + font-weight: var(--ds-font-weight-semibold); +} + +.ciam-login-register-or-text-container { + @include ds-body-xs-semibold; + @include ciam-bottom-margin; + + display: flex; + align-items: center; + gap: var(--ds-spacing-250); + color: var(--ds-color-text-secondary); + padding: var(--ds-spacing-200) 0 0 0; + + &::before, + &::after { + content: ''; + display: block; + flex-grow: 1; + height: 1px; + background-color: var(--ds-color-neutral-200); + } +} diff --git a/services/web/frontend/stylesheets/ciam/ciam-links.scss b/services/web/frontend/stylesheets/ciam/ciam-links.scss new file mode 100644 index 0000000000..39bad58577 --- /dev/null +++ b/services/web/frontend/stylesheets/ciam/ciam-links.scss @@ -0,0 +1,37 @@ +// TODO: Replace `fuchsia` by the correct colors. + +.ciam-enabled, +.website-redesign:not(.application-page) .ciam-enabled .notification { + // Links, used in services/web/frontend/stylesheets/base/links.scss + --link-color: currentcolor; + --link-hover-color: var(--ds-color-text-primary); + --link-visited-color: var(--ds-color-text-secondary); + --link-text-decoration: underline; + --link-hover-text-decoration: underline; + + a { + text-decoration-thickness: 5%; + text-underline-offset: 19.5%; + } + + a:hover, + a:focus { + text-decoration-thickness: 10%; + } + + a:focus-visible { + box-shadow: none; + outline: none; + background-color: var(--ds-color-yellow-500); + + &.ciam-image-link { + @include ds-focus-outline; + + background-color: transparent; + } + + &.btn { + background-color: var(--bs-btn-bg); + } + } +} diff --git a/services/web/frontend/stylesheets/ciam/ciam-login.scss b/services/web/frontend/stylesheets/ciam/ciam-login.scss new file mode 100644 index 0000000000..98c137528b --- /dev/null +++ b/services/web/frontend/stylesheets/ciam/ciam-login.scss @@ -0,0 +1,15 @@ +@import 'ds-design-system'; + +.ciam-login-text { + text-align: center; + padding: var(--ds-spacing-200) 0; + margin: 0; + + &.ciam-login-bottom-space { + @include ciam-bottom-margin; + } +} + +.ciam-login-sso-form-controls { + @include ciam-vertically-spaced; +} diff --git a/services/web/frontend/stylesheets/ciam/ciam-register.scss b/services/web/frontend/stylesheets/ciam/ciam-register.scss index c2fd506db5..f6743a5a95 100644 --- a/services/web/frontend/stylesheets/ciam/ciam-register.scss +++ b/services/web/frontend/stylesheets/ciam/ciam-register.scss @@ -11,41 +11,19 @@ padding: var(--ds-spacing-200) 0; } -.ciam-work-uni-sso { - color: var(--ds-color-text-secondary); - padding-top: var(--ds-spacing-200); - margin-bottom: var(--ds-spacing-400); - font-weight: var(--ds-font-weight-semibold); -} - .ciam-register-container { display: flex; flex-direction: column; - .login-register-or-text-container { - @include ds-body-xs-semibold; - - gap: var(--ds-spacing-250); - padding: var(--ds-spacing-200) 0 0 0; - margin-bottom: var(--ds-spacing-400); - - &::before, - &::after { - background-color: var(--ds-color-neutral-200); - } - } - - .login-register-error-container { - padding-bottom: 0; - - .notification { - margin-bottom: var(--ds-spacing-400); + footer .footer-links { + @include media-breakpoint-up(sm) { + justify-content: start; } } } .ciam-password-group { - margin-bottom: var(--ds-spacing-400); + @include ciam-bottom-margin; } .ciam-password-requirements-message { diff --git a/services/web/frontend/stylesheets/ciam/ciam-variables.scss b/services/web/frontend/stylesheets/ciam/ciam-variables.scss deleted file mode 100644 index fadf7fb5d5..0000000000 --- a/services/web/frontend/stylesheets/ciam/ciam-variables.scss +++ /dev/null @@ -1,11 +0,0 @@ -// TODO: Replace `fuchsia` by the correct colors. - -.ciam-enabled, -.website-redesign:not(.application-page) .ciam-enabled .notification { - // Links, used in services/web/frontend/stylesheets/base/links.scss - --link-color: var(--ds-color-text-primary); - --link-hover-color: var(--ds-color-text-secondary); - --link-visited-color: var(--ds-color-text-secondary); - --link-text-decoration: underline; - --link-hover-text-decoration: none; -} diff --git a/services/web/frontend/stylesheets/ds/components/button.scss b/services/web/frontend/stylesheets/ds/components/button.scss index d00afa3156..4a3beefabd 100644 --- a/services/web/frontend/stylesheets/ds/components/button.scss +++ b/services/web/frontend/stylesheets/ds/components/button.scss @@ -111,4 +111,18 @@ visibility: hidden; } } + + .button-content { + display: inline-flex; + align-items: center; + gap: var(--ds-spacing-200); // Add gap between text and icons + justify-content: center; + } + + .btn-social-icon { + height: 24px; + width: 24px; + padding: var(--ds-spacing-50); + margin: 0; + } } diff --git a/services/web/frontend/stylesheets/ds/components/form-control.scss b/services/web/frontend/stylesheets/ds/components/form-control.scss index c099d34b89..c367754706 100644 --- a/services/web/frontend/stylesheets/ds/components/form-control.scss +++ b/services/web/frontend/stylesheets/ds/components/form-control.scss @@ -8,8 +8,6 @@ .form-group-ds label, .website-redesign .form-group-ds label, .website-redesign .form-label-ds { - @include ds-body-sm-semibold; - --bs-body-font-family: var(--ds-font-family-sans), sans-serif; --bs-success-rgb: 25, 117, 76; // #19754c --bs-danger-rgb: 195, 9, 43; // #c3092b @@ -22,6 +20,12 @@ margin-bottom: var(--ds-spacing-100); } +.form-label-ds, +.form-group-ds label, +.website-redesign .form-group-ds label { + @include ds-body-md-semibold; +} + .form-group-ds { margin-bottom: var(--ds-spacing-400); } diff --git a/services/web/frontend/stylesheets/pages/onboarding-confirm-email-ciam.scss b/services/web/frontend/stylesheets/pages/onboarding-confirm-email-ciam.scss index 86b455367d..f680a4174d 100644 --- a/services/web/frontend/stylesheets/pages/onboarding-confirm-email-ciam.scss +++ b/services/web/frontend/stylesheets/pages/onboarding-confirm-email-ciam.scss @@ -22,6 +22,10 @@ margin: auto; max-width: 480px; + h1 { + margin-bottom: var(--ds-spacing-400); + } + .notification { margin-bottom: var(--spacing-05); } diff --git a/services/web/locales/en.json b/services/web/locales/en.json index c9b66f9e30..6ab4119ae6 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -128,7 +128,7 @@ "administration_and_security": "Administration and security", "advanced_reference_search": "Advanced <0>reference search", "advanced_reference_search_mode": "Advanced reference search", - "advancing_research_with": "Advancing research with Overleaf, Papers, and more", + "advancing_research_with": "Advancing research with <0>Overleaf, <1>Papers, and more", "after_that_well_bill_you_x_total_y_subtotal_z_tax_annually_on_date_unless_you_cancel": "After that, we’ll bill you __totalAmount__ (__subtotalAmount__ + __taxAmount__ tax) annually on __date__, unless you cancel.", "aggregate_changed": "Changed", "aggregate_to": "to", @@ -830,6 +830,7 @@ "for_teams_and_organizations_who_want_a_streamlined_sso_and_security": "For teams and organizations who want a streamlined sign-on process and our strongest cloud security.", "for_universities": "For universities", "forever": "forever", + "forgot_password": "Forgot password?", "forgot_your_password": "Forgot your password", "format": "Format", "found_matching_deleted_users": "Found __deletedUserCount__ matching deleted users", diff --git a/services/web/public/img/website-redesign/features-universities-hero-ciam.webp b/services/web/public/img/website-redesign/features-universities-hero-ciam.webp new file mode 100644 index 0000000000..a288adecdb Binary files /dev/null and b/services/web/public/img/website-redesign/features-universities-hero-ciam.webp differ