diff --git a/app/app/account_linking.py b/app/app/account_linking.py
index 61fcc9a..ea0451d 100644
--- a/app/app/account_linking.py
+++ b/app/app/account_linking.py
@@ -5,10 +5,11 @@ from typing import Optional
 
 from arrow import Arrow
 from newrelic import agent
+from sqlalchemy import or_
 
 from app.db import Session
 from app.email_utils import send_welcome_email
-from app.utils import sanitize_email
+from app.utils import sanitize_email, canonicalize_email
 from app.errors import (
     AccountAlreadyLinkedToAnotherPartnerException,
     AccountIsUsingAliasAsEmail,
@@ -131,8 +132,9 @@ class ClientMergeStrategy(ABC):
 class NewUserStrategy(ClientMergeStrategy):
     def process(self) -> LinkResult:
         # Will create a new SL User with a random password
+        canonical_email = canonicalize_email(self.link_request.email)
         new_user = User.create(
-            email=self.link_request.email,
+            email=canonical_email,
             name=self.link_request.name,
             password=random_string(20),
             activated=True,
@@ -213,11 +215,21 @@ def process_login_case(
         partner_id=partner.id, external_user_id=link_request.external_user_id
     )
     if partner_user is None:
+        canonical_email = canonicalize_email(link_request.email)
         # We didn't find any SimpleLogin user registered with that partner user id
         # Make sure they aren't using an alias as their link email
         check_alias(link_request.email)
+        check_alias(canonical_email)
         # Try to find it using the partner's e-mail address
-        user = User.get_by(email=link_request.email)
+        users = User.filter(
+            or_(User.email == link_request.email, User.email == canonical_email)
+        ).all()
+        if len(users) > 1:
+            user = [user for user in users if user.email == canonical_email][0]
+        elif len(users) == 1:
+            user = users[0]
+        else:
+            user = None
         return get_login_strategy(link_request, user, partner).process()
     else:
         # We found the SL user registered with that partner user id
diff --git a/app/app/admin_model.py b/app/app/admin_model.py
index 4f7e6d0..36c69c7 100644
--- a/app/app/admin_model.py
+++ b/app/app/admin_model.py
@@ -611,6 +611,26 @@ class NewsletterAdmin(SLModelView):
             else:
                 flash(error_msg, "error")
 
+    @action(
+        "clone_newsletter",
+        "Clone this newsletter",
+    )
+    def clone_newsletter(self, newsletter_ids):
+        if len(newsletter_ids) != 1:
+            flash("you can only select 1 newsletter", "error")
+            return
+
+        newsletter_id = newsletter_ids[0]
+        newsletter: Newsletter = Newsletter.get(newsletter_id)
+        new_newsletter = Newsletter.create(
+            subject=newsletter.subject,
+            html=newsletter.html,
+            plain_text=newsletter.plain_text,
+            commit=True,
+        )
+
+        flash(f"Newsletter {new_newsletter.subject} has been cloned", "success")
+
 
 class NewsletterUserAdmin(SLModelView):
     column_searchable_list = ["id"]
diff --git a/app/app/models.py b/app/app/models.py
index 9293617..69f825e 100644
--- a/app/app/models.py
+++ b/app/app/models.py
@@ -3517,7 +3517,7 @@ class PartnerSubscription(Base, ModelMixin):
 
 class Newsletter(Base, ModelMixin):
     __tablename__ = "newsletter"
-    subject = sa.Column(sa.String(), nullable=False, unique=True, index=True)
+    subject = sa.Column(sa.String(), nullable=False, index=True)
 
     html = sa.Column(sa.Text)
     plain_text = sa.Column(sa.Text)
diff --git a/app/migrations/versions/2023_110714_4bc54632d9aa_.py b/app/migrations/versions/2023_110714_4bc54632d9aa_.py
new file mode 100644
index 0000000..874b494
--- /dev/null
+++ b/app/migrations/versions/2023_110714_4bc54632d9aa_.py
@@ -0,0 +1,31 @@
+"""empty message
+
+Revision ID: 4bc54632d9aa
+Revises: 46ecb648a47e
+Create Date: 2023-11-07 14:02:17.610226
+
+"""
+import sqlalchemy_utils
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '4bc54632d9aa'
+down_revision = '46ecb648a47e'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_index('ix_newsletter_subject', table_name='newsletter')
+    op.create_index(op.f('ix_newsletter_subject'), 'newsletter', ['subject'], unique=False)
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_index(op.f('ix_newsletter_subject'), table_name='newsletter')
+    op.create_index('ix_newsletter_subject', 'newsletter', ['subject'], unique=True)
+    # ### end Alembic commands ###
diff --git a/app/static/images/coupon.png b/app/static/images/coupon.png
index c7cccbf..99f21aa 100644
Binary files a/app/static/images/coupon.png and b/app/static/images/coupon.png differ
diff --git a/app/templates/base.html b/app/templates/base.html
index 59c88b5..3a94b82 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -86,6 +86,12 @@
   </head>
   <body>
     <div class="page">
+      {% if current_user.is_authenticated and current_user.should_show_upgrade_button() %}
+
+        <div class="alert alert-success text-center mb-0" role="alert">
+          Black Friday: $20 for the first year instead of $30. Available until December 1st.
+        </div>
+      {% endif %}
       {% block announcement %}{% endblock %}
       <div class="container">
         <!-- For flash messages -->
diff --git a/app/templates/dashboard/pricing.html b/app/templates/dashboard/pricing.html
index 462e1e7..93e6f2e 100644
--- a/app/templates/dashboard/pricing.html
+++ b/app/templates/dashboard/pricing.html
@@ -57,6 +57,19 @@
 {% endblock %}
 {% block default_content %}
 
+  <div class="alert alert-info">
+    Black Friday Deal: 33% off on the yearly plan for the <b>first</b> year ($20 instead of $30).
+    <br>
+    Please use this coupon code
+    <em data-toggle="tooltip"
+        title="Click to copy"
+        class="clipboard"
+        data-clipboard-text="BF2023">BF2023</em> during the checkout.
+    <br>
+    <img src="/static/images/coupon.png" class="m-2" style="max-width: 300px">
+    <br>
+    Available until December 1, 2023.
+  </div>
   <div class="pb-8">
     <div class="text-center mx-md-auto mb-8 mt-6">
       <h1>Upgrade to unlock premium features</h1>
diff --git a/app/tests/test_account_linking.py b/app/tests/test_account_linking.py
index 0825daf..1ef7222 100644
--- a/app/tests/test_account_linking.py
+++ b/app/tests/test_account_linking.py
@@ -18,7 +18,7 @@ from app.db import Session
 from app.errors import AccountAlreadyLinkedToAnotherPartnerException
 from app.models import Partner, PartnerUser, User
 from app.proton.utils import get_proton_partner
-from app.utils import random_string
+from app.utils import random_string, canonicalize_email
 from tests.utils import random_email
 
 
@@ -377,3 +377,48 @@ def test_link_account_with_uppercase(flask_client):
     )
     assert partner_user.partner_id == get_proton_partner().id
     assert partner_user.external_user_id == partner_user_id
+
+
+def test_login_to_account_with_canonical_email(flask_client):
+    email = "a.{rand}@gmail.com".format(rand=random_string(10))
+    canonical_email = canonicalize_email(email)
+    assert email != canonical_email
+    partner_user_id = random_string()
+    link_request = random_link_request(
+        external_user_id=partner_user_id, email=email.upper()
+    )
+    user = create_user(canonical_email)
+    assert user.email == canonical_email
+    res = process_login_case(link_request, get_proton_partner())
+    assert res.user.id == user.id
+
+
+def test_login_to_account_with_canonical_email_if_there_is_also_non_canonical(
+    flask_client,
+):
+    email = "a.{rand}@gmail.com".format(rand=random_string(10))
+    canonical_email = canonicalize_email(email)
+    assert email != canonical_email
+    partner_user_id = random_string()
+    link_request = random_link_request(
+        external_user_id=partner_user_id, email=email.upper()
+    )
+    user = create_user(canonical_email)
+    create_user(email)
+    assert user.email == canonical_email
+    res = process_login_case(link_request, get_proton_partner())
+    assert res.user.id == user.id
+
+
+def test_login_creates_account_with_canonical_email(
+    flask_client,
+):
+    email = "a.{rand}@gmail.com".format(rand=random_string(10))
+    canonical_email = canonicalize_email(email)
+    assert email != canonical_email
+    partner_user_id = random_string()
+    link_request = random_link_request(
+        external_user_id=partner_user_id, email=email.upper()
+    )
+    res = process_login_case(link_request, get_proton_partner())
+    assert res.user.email == canonical_email